一、EJB查询语言
EJB 1.1规范没有为CMP实体Bean(容器治理持久化的实体Bean)的查找器方法提供一种定义查询的标准,因此,各个EJB容器厂商分别为实体Bean的查找器方法定义了各自的查询语法。例如,BEA的WebLogic application Server v5.1定义了一种称为WLQL的查询语言。
由于这个原因,当应用从一个厂商的应用服务器移植到另一个厂商的应用服务器时,开发者必须重新为查找器方法定义查询。显然,这限制了带有CMP实体Bean的应用的可移植性。
另外,EJB 1.1规范也没有为CMP实体Bean在各种不同的代码上下文、关系或关联条件下与其它实体Bean的交互提供标准的方法,没有一种合适的机制可以用来定义那些从一个实体Bean导航到它所依靠的类以及这些类的成员变量的查询。
EJB 2.0规范通过定义EJB查询语言(EJB Query Language)来解决这些问题。EJB QL以SQL-92规范为基础,用来为CMP实体Bean定义各种查找器方法和select方法的查询。EJB QL查询语句由三个子句构成:SELECT子句,FROM子句,WHERE子句。EJB QL的用途之一是,通过在部署描述器中引入抽象模式类型和关系,它为定义实体Bean以及依靠类之间的关系提供了一种标准化的方法。另外,EJB QL还能够用抽象模式名字和关系为导航操作定义查询。
下面是EJB QL查询的基本语法格式:
EJB QL查询 ::= SELECT子句 FROM子句 [WHERE子句]
EJB QL查询必须包含SELECT和FROM子句,但WHERE子句是可选的。FROM子句声明了基于抽象模式名字的符号变量,SELECT子句利用这些符号变量定义查询的返回值类型,而WHERE子句则定义了查询的条件。
在部署描述器中,EJB QL查询用标记定义,如下所示:
查找器方法或select方法的名字在元素中指定,参数在元素中指定。元素指定了返回值类型,可以包含本地值或远程值。查询字符串在元素中声明。查找器方法和select方法用EJB QL定义查询,所以下面我将简略地介绍一下这两个概念。EJB 2.0规范定义了实体Bean的查找器方法和select方法,其中select方法是该规范新增的内容。
查找器方法(Finder Method):查找器方法通过关系数据库系统从持久性存储系统中提取出一个实体Bean的实例,或者一个实体Bean实例的集合。这些方法将在实体Bean的Home接口列出,因此,客户程序可以看到它们。Home接口既可以是远程的EJBHome,也可以是本地的EJBLocalHome。在远程Home接口中,查找器方法的返回类型或者是实体Bean的远程接口,或者是一个实现实体Bean远程接口的对象的集合。在本地Home接口中,查找器方法的返回值类型或者是实体Bean的本地接口,或者是一个实现实体Bean本地接口的对象的集合。例如:
// 远程Home接口
public interface OrderHome extends javax.ejb.EJBHome {
...
public Order findByPRimaryKey(int orderId) throws FinderException,RemoteException;
public Order findByBiggestOrder() throws FinderException,RemoteException;
public java.util.Collection findAllOrders(String supplierName) throws FinderException,RemoteException;
}
// 本地Home接口
public interface OrderHome extends javax.ejb.EJBLocalHome {
...
public Order findByPrimaryKey(int orderId) throws FinderException;
public Order findBiggestOrder() throws FinderException;
public java.util.Collection findAllOrders(String supplierName) throws FinderException;
}
select方法(Select Method):在实体Bean中,select方法作为一种非凡类型的查寻方法存在。这些方法不在Home接口中声明,因此客户程序不能直接访问它们。对于访问cmp-field或者在cmr-field中定义的任何远程接口实例来说,查找器方法不是很有用。利用select方法,实体Bean可返回cmp-field类型的实例,或由cmr-field描述的远程接口。
select方法通常有下面两种类型:
ejbSelect
ejbSelectInEntity
例如:
public abstract class OrderBean implements javax.ejb.EntityBean {
...
public abstract java.util.Collection ejbSelectAllOrderedProdUCts(Date date)
throws FinderException;
...
public abstract java.util.Collection ejbSelectAllOrderedProductsInEntity(Date date)
throws FinderException;
}
ejbSelect不关联到实体Bean的特定实例。因此,在上面的例子中,ejbSelectAllOrderedProducts方法返回一个集合,其中包含与所有订单关联的所有产品。ejbSelectInEntity面向特定的实体Bean实例执行,前面例子中的ejbSelectAllOrderProductsInEntity方法返回与该OrderBean实例关联的所有产品。
我们将以订单处理应用为例,具体介绍EJB QL查询的各个子句。图一显示了OrderEJB、LineItemEJB、ProductEJB和AddressEJB之间的关系:
在部署描述器中,上述关系的定义如下:
Order-LineItem
order-has-lineitems
One
OrderEJB
lineItems
java.util.Collection
lineitem-belongsto-order
Many
LineItemEJB
order
Product-LineItem
product-has-lineitems
One
ProductEJB
lineitem-for-product
Many
LineItemEJB
product
二、FROM子句
FROM子句通过声明符号变量,定义了查询的范围。符号变量不能在SELECT子句和WHERE子句中声明,SELECT子句和WHERE子句只能使用FROM子句中定义的符号变量。一个FROM子句中可以定义多个符号变量。
任何合法的标识符都可以用作符号变量,但也存在一些限制。例如,符号变量的名字不能与抽象模式名字或EJB名字相同。另外,符号变量名字不应该是保留的符号,保留的符号包括:SELECT,FROM,WHERE,DISTINCT,OBJECT,NULL,TRUE,FALSE,NOT,AND,OR,BETWEEN,LIKE,IN,AS,UNKNOWN,EMPTY,MEMBER,OF以及IS。符号变量的名字是大小写敏感的。
符号变量声明包括范围变量声明和集合成员变量声明。下面,我们将分析一个简单的查询命令,它的FROM子句既包括范围变量声明,也包括集合变量声明。要选择出所有包含“Floppy Drive”产品的订单,查询是:
SELECT OBJECT(o) FROM Order o, IN (o.lineItems) li
WHERE li.product.product_type='Floppy Drive'
在这个例子中,FROM子句把“o”标识符声明为一个范围变量,把标识符“li”声明为一个集合成员变量。范围变量声明了抽象模式类型。声明集合成员变量要用到:
保留符号“IN”,
范围变量的抽象模式类型,以及
关联的实体Bean的抽象模式类型。
范围变量“o”指示了抽象模式类型Order。类似地,符号变量“li”是LineItem抽象模式类型,li.product是Product抽象模式类型。WHERE子句中的li.product.product_type是java.lang.String类型。在EJB QL查询语言中,所有子句的分析和计算都是从左到右进行,因此,符号变量“li”利用了前面声明的“o”。在SELECT子句中的OBJECT符号是必须的,因为要用OBJECT操作符来限定和修饰SELECT子句中的所有独立的符号变量。
范围变量还可以使用可选的符号AS声明。也就是说,上面例子中的FROM子句也可以改写成:
FROM Order AS o, IN (o.lineItems) li
另外,一个FROM子句可以定义一个以上的范围变量。例如:
FROM Order AS o, IN (o.lineItems) li, Product p
三、WHERE子句
WHERE子句定义了选择结果中的对象或值必须满足的条件表达式。下面是WHERE子句的语法:
WHERE子句 ::= WHERE 条件表达式
在EJB QL查询语言中,WHERE子句中的所有符号变量必需在FROM子句中声明。查找器方法和select方法的输入参数也在这里传入。输入参数由一个问号(“?”)前缀指示,问号的后面加上方法声明中参数的序号(例如:?1,?2,等等)。
public abstract class OrderBean implements javax.ejb.EntityBean {
...
//method-a
public abstract java.util.Collection ejbSelectLineItems(int quantity)
throws FinderException;
...
//method-b
public abstract java.util.Collection ejbSelectAllProducts(String product_type, double