使用动态查询服务
在开发流程中,有时您可能想使用动态查询服务,而不想使用常规 Enterprise JavaBean (EJB) 查询服务(可称为部署查询)。例如,在测试期间,可以在应用程序运行时使用动态查询服务,因此不必重新部署应用程序。
关于此任务
- 您需要通过程序在应用程序运行时(而不是在部署时)对查询进行定义。
- 您必须从一个查询返回多个 CMP 或 CMR 字段。(部署查询只允许在 SELECT 子句中指定一个元素。)有关更多信息,请参阅主题“示例:EJB 查询”。
- 您要在查询中返回一个计算表达式。
- 您要在查询语句中使用值对象方法或 Bean 方法。有关更多信息,请参阅主题“路径表达式”。
- 您要在开发过程中对 EJB 查询进行交互测试,但不希望每次更新 finder 或 select 查询时重复部署应用程序。
动态查询 API 是一个无状态会话 Bean;它的使用方法类似于任何其他 J2EE EJB 应用程序 Bean。它包含在 API 包的 com.ibm.websphere.ejbquery 中。
remote interface = com.ibm.websphere.ejbquery.Query
remote home interface = com.ibm.websphere.ejbquery.QueryHome
local interface = com.ibm.websphere.ejbquery.QueryLocal
local home interface = com.ibm.websphere.ejbquery.QueryLocalHome
因为本地接口使用的应用程序服务器内存较少,所以它的整体 EJB 性能比远程接口更出色。
过程
示例
- 将远程接口用于动态查询。
使用远程接口运行动态 Enterprise JavaBeans (EJB) 查询时,您是对 Query 接口调用 executeQuery 方法。executeQuery 方法对该接口具有一个值为 REQUIRED 的事务属性;因此,您无需显式建立一个事务上下文即可运行查询。
使用远程接口运行动态 Enterprise JavaBeans (EJB) 查询时,您是对 Query 接口调用 executeQuery 方法。executeQuery 方法对该接口具有一个值为 REQUIRED 的事务属性;因此,您无需显式建立一个事务上下文即可运行查询。
用以下导入语句作为开头:
import com.ibm.websphere.ejbquery.QueryHome; import com.ibm.websphere.ejbquery.Query; import com.ibm.websphere.ejbquery.QueryIterator; import com.ibm.websphere.ejbquery.IQueryTuple; import com.ibm.websphere.ejbquery.QueryException;
接着,以字符串形式编写您的查询语句,如以下示例所示(该示例检索未付足工资员工的姓名和 EJB 引用):
String query = "select e.name as name , object(e) as emp from EmpBean e where e.salary < 50000";
通过从 QueryHome 类获取一个引用来创建 Query 对象。(这个类定义了 executeQuery 方法。)请注意,为简洁起见,以下示例对 Query 对象使用动态查询 JNDI 名称:
InitialContext ic = new InitialContext(); Object obj = ic.lookup("com/ibm/websphere/ejbquery/Query"); QueryHome qh = ( QueryHome) javax.rmi.PortableRemoteObject.narrow( obj, QueryHome.class ); Query qb = qh.create();
随后,您必须为查询结果集指定一个最大大小,它在 QueryIterator 对象(随 Class QueryIterator 附带)中定义。随后,您必须为查询结果集指定一个最大大小,它在 QueryIterator 对象(随 QueryIterator API 包附带)中定义。该示例将结果集的最大大小设置为 99:
QueryIterator it = qb.executeQuery(query, null, null ,0, 99 );
迭代器包含一组 IQueryTuple 对象,它们是返回集合值的记录。根据示例查询语句中的条件,该方案中的每个元组包含一个 name 值和一个 object(e) 值。要显示该查询结果的内容,请使用以下代码:while (it.hasNext() ) { IQueryTuple tuple = (IQueryTuple) it.next(); System.out.print( it.getFieldName(1) ); String s = (String) tuple.getObject(1); System.out.println( s); System.out.println( it.getFieldName(2) ); Emp e = ( Emp) javax.rmi.PortableRemoteObject.narrow( tuple.getObject(2), Emp.class ); System.out.println( e.getPrimaryKey().toString()); }
程序的输出可能类似于以下内容:name Bob emp 1001 name Dave emp 298003 ...
最后,捕获并处理任何异常。运行时处理错误或查询语句中的语法错误可能导致出现异常。以下示例捕获并处理了这些异常:} catch (QueryException qe) { System.out.println("Query Exception "+ qe.getMessage() ); }
处理远程接口查询的大型结果集合
如果您计划使查询返回一个大型集合,那么可以选择对它进行编程,以返回多个更容易管理、更小的结果集合。在远程 executeQuery 方法中使用 skipRow 和 maxRow 参数以分块检索答案。例如:
int skipRow=0; int maxRow=100; QueryIterator it = null; do { it = qb.executeQuery(query, null, null ,skipRow, maxRow ); while (it.hasNext() ) { // display result skipRow = skipRow + maxRow; } } while ( ! it.isComplete() ) ;
- 将本地接口用于动态查询。
使用本地接口运行动态 Enterprise JavaBeans (EJB) 查询时,您是在 QueryLocal 接口上调用 executeQuery 方法。此接口不会为该方法发起一个事务;因此,您必须显式建立一个事务上下文供查询运行。
注: 要建立一个事务上下文,以下示例调用了 begin() 和 commit() 方法。使用这些方法的另一个替代方法是,您只要将查询代码嵌入事务上下文中运行的 EJB 方法即可。查询代码用以下导入语句作为开头:
import com.ibm.websphere.ejbquery.QueryLocalHome; import com.ibm.websphere.ejbquery.QueryLocal; import com.ibm.websphere.ejbquery.QueryLocalIterator; import com.ibm.websphere.ejbquery.IQueryTuple; import com.ibm.websphere.ejbquery.QueryException;
接着,以字符串形式编写您的查询语句,如以下示例所示(该示例检索未付足工资员工的姓名和 EJB 引用):
String query = "select e.name, object(e) from EmpBean e where e.salary < 50000 ";
通过从 QueryLocalHome 类获取一个引用创建一个 QueryLocal 对象。(这个类定义了 executeQuery 方法。)请注意,在以下示例中,ejb/query 用作一个指向动态查询 JNDI 名称(com/ibm/websphere/ejbquery/Query)的本地 EJB 引用:
InitialContext ic = new InitialContext(); QueryLocalHome qh = ( LocalQueryHome) ic.lookup( "java:comp/env/ejb/query" ); QueryLocal qb = qh.create();
代码的最后一部分发起一个事务,调用 executeQuery 方法并显示查询结果。将对 QueryLocalIterator 类进行实例化,因为它定义了查询结果集。这个类包含在 Class QueryIterator API 包中。请记住,迭代器将在事务结束时失效;您必须在 executeQuery 调用的同一事务作用域中使用迭代器。
userTransaction.begin(); QueryLocalIterator it = qb.executeQuery(query, null, null); while (it.hasNext() ) { IQueryTuple tuple = (IQueryTuple) it.next(); System.out.print( it.getFieldName(1) ); String s = (String) tuple.getObject(1); System.out.println( s); System.out.println( it.getFieldName(2) ); EmpLocal e = ( EmpLocal ) tuple.getObject(2); System.out.println( e.getPrimaryKey().toString()); } userTransaction.commit();
在多数情况下,QueryLocalIterator 对象是需求驱动的。即,它导致数据呈增量返回:对于每个数据库记录检索,必须在迭代器上调用 next() 方法。(某些情况下,迭代器也可以不是需求驱动的。有关更多信息,请查阅动态查询性能注意事项主题中的“本地查询接口”子部分。)
因为应用程序服务器内存中的完整查询结果集将呈增量具体化,所以您可以轻松控制它的大小。例如,在测试运行中,您可以决定只需返回几组查询结果。在这种情况下,您应该在 QueryLocalIterator 对象上使用 close() 方法的一个调用来结束查询循环。这样做可以释放迭代器使用的 SQL 资源。否则只有内存中累积了完整的结果集或事务结束时,才会释放这些资源。