44.7. EJB QL

摘要

Enterprise JavaBeans Query Language,简称EJB QL,是用于对容器管理持久性的Entity Bean的查询进行定义的语言。EJB QL使Entity Bean的查询方法的语义定义更具可移植性。

44.7.1. 概述

EJB QL是用于容器管理持久性Entity Bean中finder方法和select方法的标准查询语言。EJB QL可被编译成为目标语言,如数据库或其他持久存储所使用的SQL语言,从而将查询转移到由持久存储提供的本地(native)语言运行环境。因此,EJB QL在提供可移植能力的同时可提高执行效率。

EJB QL使用Entity Bean的抽象持久模式(abstract persistence schema)和关系作为数据模型。在这些数据模型的基础上定义了运算符和表达式。

基于在部署描述中定义的抽象持久模式和关系,开发者使用EJB QL编写查询。相互关联的Entity Bean中定义的cmp-field和cmr-field决定Entity Bean之间进行定位(navigation)与选取(selection)的能力,EJB QL则取决于这种能力。通过在EJB QL中使用cmr-field域的名字,开发者可以从一个Entity Bean定位到其他的Entity Bean。

如在同一个部署描述文件中定义了Entity Bean的抽象持久模式与EJB QL查询,则开发者可以在查询中使用Entity Bean的抽象模式类型。

EJB QL有以下两种使用方式:

  • 通过定义在Entity Bean的Home接口中的finder方法,EJB QL可用于编写选取(select)Entity对象的查询。查询的结果可被Entity Bean的客户端使用。

  • 通过定义在Entity Bean的组件类中的select方法,EJB QL可用于编写选取(select)Entity对象或从Entity Bean的抽象模式类型派生的其他值的查询。开发者可以使用select方法查找对象或与Entity Bean状态相关的值,且结果不会暴露给客户端。

一个EJB QL查询语句必须包含一个SELECT子句和一个FROM子句,可以包含WHERE子句和ORDER BY子句。

44.7.2. 定义

EJB QL使用类似于SQL的语法,基于Entity Bean的抽象模式类型和关系,对值或对象进行选取。对于使用Entity Bean的cmr-field定义的关系,可使用EJB QL中的路径表达式(path expressions)在关系中进行定位。

本节提供EJB QL语言的完整定义。

EJB QL是包含以下四个部分的字符串:

  • SELECT子句,用于定义被选取对象或值的类型;

  • FROM子句,对于在SELECT子句中表达式需要指定的范围和应用于WHERE子句的查询范围,可使用FROM子句进行声明;

  • 可选的WHERE子句,用于限制查询返回的结果。

  • 可选的ORDER BY子句,用于对查询结果进行排序。

EJB QL可使用BNF语法定义如下:

EJB QL ::= select_clause from_clause [where_clause] [order by_clause]

EJB QL查询必须有一个SELECT子句和一个FROM子句,方括号表示WHERE和ORDER BY是可选的。

对应于使用EJB QL查询定义语义的finder方法和select方法,查询可以使用输入参数。

EJB QL查询使用ejb-ql部署描述元素进行定义。

44.7.2.1. 抽象持久类型与查询范围

EJB QL是基于EJB2.1容器管理持久性类型模型而设计的类型表达式语言。每个EJB QL表达式都拥有类型。表达式的类型取决于表达式的结构、标记变量声明(indetification variable declaration)的抽象模式类型、对cmp-field和cmr-field的进行求值的类型和字面值的类型。EJB QL中容许类型是Entity Bean的抽象模式类型和cmp-field的定义类型。

由Entity Bean类和部署描述中提供的信息得到Entity Bean的抽象模式类型。抽象模式类型和Entity Bean之间存在一对一的关系。定义在abstract-schema-name部署描述元素中的抽象模式的名字,用于在EJB QL中标识抽象模式类型。

抽象模式类型拥有以下特征:

  • 对于每个对应部署描述中的一个cmp-field元素的Entity Bean类的getter访问方法, 存在一个域,其类型与cmp-field元素指定的类型相同。

  • Entity Bean类中,对于每个对应部署描述中的cmr-field元素的getter访问方法, 存在一个域,其类型是Entity Bean的抽象模式类型。此Entity Bean由包含在对应的ejb-relationship-role元素中的ejb-name子元素指定,如角色的多重性(multiplicity)为many,则类型是包含此Entity Bean抽象模式类型实例的集合。

EJB QL查询的范围,由同一个部署描述中的所有容器管理持久性的Entity Bean组成。

EJB QL查询的可定位范围,由Entity Bean抽象模式类型中的cmr-field决定。使用cmr-field,查询可以选取关联的Entity Bean和在查询中使用关联的Entity Bean的抽象模式类型。

44.7.2.2. 命名

在EJB QL查询字符串中使用Entity Bean的抽象模式名来指定Entity Bean。

在部署过程中,开发者需要为每个容器管理持久性的Entity Bean分配一个唯一的抽象模式名字,以在查询中使用此名字来指定Entity Bean。名字的作用域范围在整个部署描述文件之内。

44.7.2.3. 范例

本节中所使用的Entity Bean范例名字遵循如下约定:Entity Bean使用<name>EJB方式进行命名,Entity Bean类和抽象模式类型使用<name>方式进行命名。如表示订单的Entity Bean命名为OrderEJB,其Entity Bean类和抽象模式类型名称为Order。

例如某个部署描述文件中包含如下几个Entity Bean,OrderEJB、ProductEJB、LineItemEJB、ShippingAddressEJB和BillingAddressEJB,对应的抽象模式类型名字为Order、Product、LineItem、ShippingAddress和BillingAddress。只有OrderEJB和ProductEJB拥有远程和远程Home接口。

以上Entity Bean之间的关系如下图:

范例关系图

开发者可以通过Order和LineItem中定义的cmr-field和cmp-field,为OrderEJB定义finder查询,查找所有未完成的Order的finder查询可用如下方式编写:

SELECT DISTINCT OBJECT(o)
FROM Order AS o, IN(o.lineItems) AS l
WHERE l.shipped = FALSE

查询定位到Order抽象模式类型中的cmr-field域lineItems,并在其中中遍历对lineItems进行查找,并使用LineItem中的cmp-field域shipped选择至少有一个lineItem未发送的Order的集合。

尽管上面的例子使用了如SELECT、DISTINCT、OBJECT、FROM、AS、IN、WHERE和FALSE等大写的保留字,但是,EJB QL中的保留字是大小写无关的。

上例中的SELECT子句指定了查询的返回值类型是Order,如这个查询语句用于定义一个在Entity Bean的远程Home接口中的finder方法,则对应于由查询所选取的抽象模式类型实例,方法返回值类型为Entity Bean的远程接口类型。如这个查询语句用于定义一个在Entity Bean的本地Home接口中的finder方法,则方法返回值类型为Entity Bean的本地接口类型。

因为同一个部署描述中定义了相关的Entity Bean的抽象持久模式,所以同样可以在OrderEJB的查询中使用ProductEJB的抽象模式类型,也可使用Order和Product抽象模式类型中的cmp-field和cmr-field。例如,Product抽象模式类型中包含一个product_type的cmp-field,OrderEJB中的finder查询可以使用这个cmp-field。如“查找包含产品种类为‘办公用品’的订单”,则可以用如下查询语句定义查询方法:

SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE l.product.product_type = ‘office_supplies’

通过Order与LineItem、LineItem和Product的关联,Order关联到Product,为表达此查询,必须通过lineItem和product这两个cmr-field进行定位(navigation)。通过在整个语句范围内指定使用OrderEJB的抽象模式名称Order指定了查询的类型。

本部分文档中的余下部分将对本节中的例子进行扩展,以详细说明EJB QL的特性和用法。

44.7.2.4. 返回值类型

EJB QL查询使用Entity Bean的抽象模式类型进行编写。使用SELECT子句指定的返回值类型,应该是Entity Bean的抽象模式类型或者是cmp-field类型。finder和select方法和在部署描述文件中定义的查询语句,决定了finder和select方法返回的结果被映射到何种Java类型。

查询结果如何映射取决于查询语句定义的查询方法是finder方法还是select方法,也取决于finder方法是定义在远程Home接口中还是定义在本地Home接口中。

如前面的例子:“查找包含产品种类为‘办公用品’的订单”

  • 如查询被用于远程Home接口中的finder方法,finder方法的结果是Entity Bean的远程接口(或是远程接口的集合)。如查询被用于本地Home接口中的finder方法,finder方法的结果是Entity Bean的本地接口(或是本地接口的集合)。

  • 同样的查询如被用于select方法,返回值类型是Entity Bean的远程接口或本地接口实例,最终返回的何种接口则取决于包含在query元素中的部署描述元素result-type-mapping的值是Local还是Remote,缺省为Local。

44.7.2.5. FROM子句与定位声明

EJB QL的FROM子句通过声明标记变量(indetification variable)定义查询的范围。范围可以使用路径表达式进行限制。

标记变量(indetification variable)指定特定的Entity Bean的抽象模式类型实例。通过在标记变量(indetification variable)之间使用逗号分隔,FROM子句可以包含多个标记变量(indetification variable)。

from_clause ::= 
          FROM identification_variable_declaration 
          [, identification_variable_declaration]*

identification_variable_declaration ::= 
          collection_memeber_declaration |range_member_declaration

collection_member_declaration ::= 
          IN ( colleciton_value_path_expression) [AS] identifier

range_variable_declaration ::= abstract_schema_name [AS] identifier

下面章节讨论FROM子句的构成。

44.7.2.5.1. 标识符(Identifier)

标识符是不限长的字符序列,首字符必须使用Java标识符的首字符(start character)开头,其他字母必须是Java标识符的组成字符(part character)。首字符可以是任何使用Character.isJavaIdentifierStart方法返回真值的字符,包括下划线(_)字符和美元符($)。其他字符则可以是使用Character.isJavaIdentifierPart方法返回真值的字符,问号(?)是EJB QL的保留字符。

以下是EJB QL的保留字列表:SELECT、FROM、WHERE、DISTINCT、OBJECT、NULL、TRUE、FALSE、NOT、AND、OR、BETWEEN、LIKE、IN、AS、UNKNOWN、EMPTY、MEMBER、OF、IS

保留字是大小写无关的。

建议不要在EJB QL查询语句中使用SQL的保留字作为标识符,EJB QL的以后版本可能会使用此类保留字。

44.7.2.5.2. 标记变量(indentification variables)

标记变量(indetification variable)是EJB QL查询的FROM子句中声明的一个有效标识符。标记变量(indetification variable)可以使用特殊操作符IN和可选的操作符AS进行声明。

标记变量(indetification variable)必须声明在FROM子句中。

一个标记变量(indetification variable)是一个标识符。标记变量(indetification variable)不能是保留字或:

  • 抽象模式名(abstract-schema-name)

  • EJB名字(ejb-name)

标记变量(indetification variable)是大小写有关的。

标记变量(indetification variable)的求值使用声明变量的表达式类型。如上面OrderEJB中finder方法查询语句:

SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE l.product.product_type = ‘office_supplies’

FROM子句中声明了IN(o.lineItems) l,则标记变量(indetification variable)l对Order直接关联的所有lineItem进行求值。cmr-field域lineItems是抽象模式类型LineItem的实例集合,l是集合中的一个元素,l的类型是LineItem的抽象模式类型。

标记变量(indetification variable)用于指定一个Entity Bean的抽象模式类型的实例或者Entity Bean的抽象模式类型实例集合中的一个元素。

因此,标记变量(indetification variable)代表对某个值的引用,并以以下两种方式进行声明:范围(range)变量或者集合成员变量:

  • 范围变量使用Entity Bean的抽象模式名进行声明;

  • 集合成员变量使用得到集合结果的路径表达式进行声明。

在FROM子句中,标记变量(indetification variable)声明由左到右进行求值。查询语句中后续的标记变量(indetification variable)声明可以可以使用前面标记变量(indetification variable)声明的结果。

44.7.2.5.3. 范围变量声明

一个标记变量(indetification variable)包括一个Entity Bean的抽象模式类型。将标记变量(indetification variable)声明为一个范围变量的EJB QL语法与SQL相同:使用可选的AS操作符进行声明。

关联到Entity Bean的对象和变量一般使用路径表达式进行定位。但是,定位不可能针对所有的对象。对通过定位不能到达的对象,范围变量声明允许开发者为其指派一个“root”。

因此,开发者可在查询中使用多个范围变量声明对相同抽象模式类型的多个值进行比较。参见第 44.7.2.6 节 “SELECT子句”

44.7.2.5.4. 集合成员变量声明

一个使用collection_member_declaration声明的标记变量(indetification variable)包括通过路径表达式定位取得的集合中的所有值。路径表达式表示包含Entity Bean抽象模式类型的cmr-field的定位操作。因为一个路径表达式可以基于另一个路径表达式,则定位操作可以使用关联的Entity Bean的cmr-field。参见第 44.7.2.5.6 节 “路径表达式”

集合成员变量声明使用特殊操作符IN进行声明。用IN操作符指定的功能表达式接收一个集合值类型的路径表达式参数。 路径表达式使用集合类型进行求值,此集合类型是对Entity Bean抽象模式类型中一个集合类型的cmr-field的定位操作,所得结果的类型。

例如,OrderEJB中finder方法的查询语句中,FROM子句包含如下标记变量(indetification variable)声明子句:

IN(o.lineItems) l

lineItems是一个cmr-field域的名字,其值是LineItemEJB的抽象模式类型LineItem的实例集合。标记变量(indetification variable)l是集合中的一个成员,一个单个的LineItem实例。本例中的o是抽象模式类型Order的一个标记变量(indetification variable)。

44.7.2.5.5. 范例

下面的FROM子句包含两个标记变量(indetification variable)的声明子句,第一个子句中声明的标记变量(indetification variable)被第二个子句使用,此子句声明两个变量o和l。Order AS o是一个范围变量声明,将变量o指定为范围变量,类型为抽象模式类型Order。变量l的类型为抽象模式类型LineItem。因子句从左到右求值,变量l可以使用o的结果进行定位操作。

FROM Order AS o, IN(o.lineItems) l
44.7.2.5.6. 路径表达式

路径表达式包含一个变量,变量后的定位操作符(.)和一个cmp-field或一个cmr-field三个部分。

根据其定位能力,指向一个cmr-field的路径表达式可以更进一步进行组合。如果基本的路径表达式是对某个cmr-field进行单一值类型(非集合)的求值,则路径表达式可以由其他路径表达式组成。路径表达式的类型是由定位操作的结果计算出来的。归结到一个cmp-field域的路径表达式是最终形式,不能进行更进一步的组合。

单一值路径表达式和集合值路径表达式的语法定义如下:

single_valued_path_expression ::= 
    {single_valued_navigation | identification_variable} . cmp_field |
    single_valued_navigation
single_valued_navigation ::=
    identification_variable . [single_valued_cmr_field .]* single_valued_cmr_field
collection_valued_path_expression ::=
    identification_variable . [single_valued_cmr_field .]* collection_valued_cmr_field 

一个single_valued_cmr_field的指定需要使用one-to-many或many-to-one关系中的一个cmr-field的名字。单一值cmr-field的路径表达式类型是关联的Entity Bean的抽象模式类型。

一个collection_valued_cmr_field的指定需要使用one-to-many或many-to-many关系中的一个cmr-field的名字。表达式的类型是关联的Entity Bean的抽象模式类型。collection_valued_cmr_field的类型是关联的Entity Bean的抽象模式类型的值的集合。

对关联Entity Bean进行定位操作得到的值,其类型是此关联Entity Bean的抽象模式类型。

例如,如l是表示LineItem类型实例的标记变量(indetification variable),路径表达式l.product的类型为抽象模式类型Product。

对路径表达式的求值以一个cmr-field终止,将得出一个此cmr-field的Java类型的值。如l.product.name得到的类型是java.lang.String。

一个路径表达式由一个类型为集合的路径表达式构成,在语法上是正确的。例如,如果o的类型是Order,路径表达式o.lineItems.product是正确的,因为对lineItems的定位将得到一个集合。当验证查询语句的时候,这种情况将产生错误。为处理这种定位,在FROM子句中必须声明一个用于包含lineItems集合元素的标记变量(indetification variable),必须在查询语句的WHERE子句中,使用另一个路径表达式,才能对每个元素进行定位操作。如下例:

SELECT OBJECT(o)
FROM Order AS o, IN(o.lineItems) l
WHERE l.product.name = ‘widget’

在Apusic应用服务器中,如果Entity Bean的抽象持久类型中包含辅助值对象(Dependent Value Object)时,可使用“.”运算符访问Entity Bean内部的辅助值对象的域。如上一章第 44.6 节 “容器管理持久性的Entity Bean”中使用的范例,Customer实体中包含了类型为ContactInfo的info域,则可以使用如下EJB QL查询语句查询ContactInfo中name为“Jim Clark”的实例:

SELECT OBJECT(o)
FROM Customer AS o
WHERE o.info.name = ‘Jim Clark’
44.7.2.5.7. WHERE子句与条件表达式

WHERE子句由用于选取对象和值的条件表达式构成。WHERE子句对查询的结果进行限制。

WHERE子句的定义如下:

where_clause ::= WHERE conditional_expression
44.7.2.5.7.1. 字面值(literals)

字符串的字面值由单引号括起,如:‘literal’。包含单引号的字符串字面值,将使用两个连续的单引号表示一个单引号,例如:‘literal‘‘s’。EJB QL中的字符串字面值如同Java中的字符串字面值一样,使用Unicode字符编码。

一个精确数字字面值是一个不带小数点的数字值,如57、-957、+62。精确数字字面值支持Java中long型的数值范围。精确数字字面值使用Java中的整数语法。

一个近似数字字面值是使用科学计数法表示的数字值,如7E3、-59.7E2,或是带小数点的数字值,如7.、-95.7、+6.2。精确数字字面值支持Java中double型的数值范围。精确数字字面值使用Java中的浮点数语法。

可以使用Java语言规范中规定的后缀指明字面的确切类型。

布尔值的字面值为大小写无关的TRUE和FALSE。

44.7.2.5.7.2. 标记变量(indetification variable)

在EJB QL查询语句中,所有的用在WHERE子句的标记变量(indetification variable)必须在FROM子句中声明,参见 第 44.7.2.5.2 节 “标记变量(indentification variables)”

在WHERE子句中,标记变量(indetification variable)被实际量化,即标记变量(indetification variable)代表Entity Bean的抽象模式类型的单个实例或实例集合中的成员。标记变量(indetification variable)不能被代表一个表示整个集合的值。

如标记变量(indetification variable)代表一个空集合中的成员,则标记变量(indetification variable)的值为unknown。

44.7.2.5.7.3. 路径表达式

在WHERE子句中,除了empty_collection_comparison_expression和collection_member_expression,使用collection_valued_path_expression作为条件表达式的一部分是不合法的。

如果路径表达式由一个代表unknown值的标记变量(indetification variable)构成,则路径表达式的值为unknown。

44.7.2.5.7.4. 输入参数

以下是关于输入参数的规则。输入参数只能用于查询中的WHERE子句。

  • 输入参数由问号(?) 前缀和一个后续整数表示,例如:?1。

  • 输入参数由1开始编号。

  • EJB QL查询中不同输入参数的编号不能超出finder或select方法的输入参数编号。EJB QL查询中不必使用所有的finder或select方法的参数。

  • 输入参数只能用于comparison_expressions或collection_member_expressions。参见第 44.7.4 节 “EJB QL BNF”

  • 输入参数使用查询关联的finder或select方法中的对应参数类型进行求值。

  • 如finder或select方法的输入参数是一个EJBObject类型或EJBLocalObject类型,输入参数将会对应到正确的抽象模式类型。

44.7.2.5.7.5. 条件表达式构成

一个条件表达式由其他条件表达式、比较操作、逻辑操作、求布尔值的路径表达式和布尔值的字面值构成。

算术表达式可用于比较表达式中。一个算术表达式由其他算术表达式、算术操作、求数值的路径表达式和数值的字面值构成。

算术操作使用Java中的数值提升(numeric promotion)规则。

支持使用括号()对表达式的求值顺序进行排序。

条件表达式定义如下:

conditional_expression ::= 
    conditional_term | conditional_expression OR conditional_term

conditional_term ::= 
    conditional_factor | conditional_term AND conditional_factor
conditional_factor ::= [ NOT ] conditional_test

conditional_test :: = conditional_primary

conditional_primary ::= 
    simple_cond_expression | (conditional_expression)

simple_cond_expression ::= 
    comparison_expression | between_expression | like_expression |
    in_expression | null_comparison_expression |
    empty_collection_comparison_expression | 
    collection_member_expression
44.7.2.5.7.6. 运算符与优先级

按照由高到低的优先级,运算符排列如下:

  • 定位运算符(.);

  • 算术运算符:

    +,-一元运算符

    *,/乘除运算符

    +,-加减运算符

  • 比较运算符:=、>、>=、<、<=、<> (不等于);

  • 逻辑运算符:NOT、AND、OR

下面描述在特殊表达式中的运算符。

44.7.2.5.7.7. BETWEEN表达式

在条件表达式中,使用比较运算符[NOT] BETWEEN的语法如下:

arithmetic_expression [NOT] BETWEEN arithmetic-expr AND arithmetic-expr

例如:

p.age BETWEEN 15 AND 19

等价于

p.age >= 15 AND p.age <= 19

如用在一个BETWEEN表达式中的算术表达式的值是NULL,则BETWEEN表达式的值为UNKNOWN。

44.7.2.5.7.8. IN表达式

在条件表达式中使用比较运算符[NOT]IN的语法如下:

single_valued_path_expression [NOT] IN (string-literal [, string-literal]*)

其中,single_valued_path_expression必须是字符串值。

例如:

o.country IN ('UK', 'US', 'France')

等价于

(o.country = ’UK’) OR (o.country = ’US’) OR (o.country = ’France’)

另一个例子:

o.country NOT IN (’UK’, ’US’, ’France’)

等价于

NOT ((o.country = ’UK’) OR (o.country = ’US’) OR (o.country = ’France’))

在IN表达式中定义的字符串字面值列表中,使用逗号分隔,必须包含至少一个字符串字面值。

如用在一个IN表达式中的single_valued_path_expression表达式的值是NULL,则IN表达式的值为UNKNOWN。

44.7.2.5.7.9. LIKE表达式

在条件表达式中,使用比较运算符[NOT] LIKE的语法如下:

single_valued_path_expression [NOT] LIKE pattern-value [ESCAPE escape-character]

其中,single_valued_path_expression必须是字符串值。pattern-value是一个字符串字面值,pattern-value中的下划线(_)表示一个单个的字符、百分号(%)表示字符串序列(包括一个空序列),所有的其他字符表示自身。可选的escape-character是一个单字符的字符串字面值,用于将pattern-value中出现的下划线(_)和百分号(%)进行转义。

例如:

address.phone LIKE '12%3'

当address.phone代表的值为“123”,“12443”等值时,此表达式为真;当address.phone代表的值为“124”,“1234”等值时,此表达式为假。

asentence.word LIKE 'l_se'

当asentence.word代表的值为“lose”时,此表达式为真;当asendence.word代表的值为“loose”时,此表达式为假。

aword.underscored LIKE '\_%' ESCAPE '\'

当aword.underscored代表的值为“_foo”时,此表达式为真;当aword.underscored代表的值为“bar”时,此表达式为假;

address.phone NOT LIKE '12%3'

当address.phone代表的值为“12”,“1234”等值时,此表达式为真;当address.phone代表的值为“123”,“12443”等值时,此表达式为假。

如用single_valued_path_expression表达式的值是NULL,则表达式的值为UNKNOWN。

44.7.2.5.7.10. NULL比较表达式

在条件表达式中,使用比较运算符IS NULL的语法如下:

single_valued_path_expression IS 
						[NOT] NULL
					

NULL比较表达式检查single_valued_path_expression代表的值是否为NULL值。

包含NULL值的路径表达式在求值期间返回NULL值。

44.7.2.5.7.11. 空集合比较表达式

在empty_collection_comparison_expression表达式中,使用比较运算符IS [NOT] EMPTY的语法如下:

collection_valued_path_expression IS [NOT] EMPTY
					

此表达式检查集合值路径表达式代表的集合是否包含元素。

如果一个集合值路径表达式代表的集合被用在一个空集合比较表达式中,则不能在FROM子句中声明此集合为一个标记变量(indetification variable)。如果标记变量(indetification variable)被显式声明为一个集合中的元素,则表示存在一个非空的关系,即集合一定包含元素,检查这样的的一个集合是否为空是与前提矛盾的。因此,类似于下例的查询无效:

SELECT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE o.lineItems IS EMPTY

如用在一个空集合比较表达式中的集合值路径表达式的值不包含元素,则空集合比较表达式的值为UNKNOWN。

44.7.2.5.7.12. 集合成员表达式

在collection_member_expression(集合成员表达式)中,使用比较运算符IS MEMBER OF的语法如下:

{single_valued_navigation | identification_variable | input_parameter}
    [NOT] MEMBER [OF] collection_valued_path_expression

表达式检查指定的值是否是一个集合的成员,此集合由集合值路径表达式指定。

如果在集合成员表达式中的集合值路径表达式代表的集合不包含元素,则此表达式的值为FALSE。

44.7.2.5.7.13. 功能表达式

EJB QL包含以下内置的功能表达式。

字符串函数:

  • CONCAT(String, String),返回一个字符串;

  • SUBSTRING(String, start, length),返回一个字符串;

  • LOCATE(String, String [, start]),返回一个字符串;

  • LENGTH(String) ,返回一个整型值;

start和length是int型的参数,用于指定字符串中的位置。

算术函数:

  • ABS(number)

  • SQRT(double)

44.7.2.6. SELECT子句

SELECT子句表示查询的结果。因为finder方法不能返回任意类型的值,定义fnder方法的查询语句中的SELECT子句必须对应finder方法返回的Entity Bean的抽象模式类型;对于select方法,可以返回Entity Bean的抽象模式类型和cmp-field域的值。

SELECT子句的语法如下:

select_clause ::=SELECT [DISTINCT ] {select_expression |OBJECT (identification_variable)}
select_expression ::= single_valued_path_expression | aggregate_select_expression
aggregate_select_expression ::=
{AVG |MAX |MIN |SUM |COUNT }( [DISTINCT ] cmp_path_expression) |
COUNT ( [DISTINCT ] identification_variable | single_valued_cmr_path_expression)

SELECT子句中所有的标记变量(indetification variable)必须使用OBJECT运算符进行限制。SELECT子句中不能使用OBJECT运算符对路径表达式进行限制。

DISTINCT关键字从查询结果中删除重复的值。

如应用查询语句的方法返回值类型是java.util.Collection,如查询语句中未使用DISTINCT关键字,则返回的集合中可包含重复的元素;如方法的返回值类型是java.util.Set,查询语句中未使用DISTINCT关键字,则返回的集合中也不包含重复的元素。

例子:

SELECT l.product FROM Order AS o, IN(o.lineItems) l

注意SELECT子句必须指定返回的是一个单值表达式。下例是无效的查询语句:

SELECT o.lineItems FROM Order AS o

如希望通过比较多个Entity Bean抽象模式类型来选取值,则需要在FROM子句中声明多个抽象模式类型的标记变量(indetification variable)。

下面的finder方法的查询语句返回订单数量大于John Smith的订单数量的订单。本例中使用了两个不同的标记变量(indetification variable),都使用Order抽象模式类型。

SELECT DISTINCT OBJECT(o1)
FROM Order o1, Order o2
WHERE o1.quantity > o2.quantity AND
    o2.customer.lastname = ‘Smith’ AND
    o2.customer.firstname= ‘John’

下例返回订单的所有订单项:

SELECT OBJECT(l)
FROM Order o, IN(o.lineItems) l

下例返回所有的订单项:

SELECT OBJECT(l)
FROM LineItems AS l

44.7.2.7. ORDER BY字句

ORDER BY字句的作用是对查询返回的对象或值进行排序。

ORDER BY字句的语法如下:

orderby_clause ::=ORDER BY orderby_item [, orderby_item]*
orderby_item ::= cmp_path_expression [ASC | DESC]

当使用了ORDER BY字句时,SELECT字句必须符合以下形式:

  • 为标记变量(indetification variable) ,如:OBJECT(o);

  • single_valued_cmr_path_expression;

  • cmp_path_expression;

ORDER BY字句的排序项必须是SELECT 字句中的返回项或返回对象所包含的字段。

举例:以下前两个EJB QL表达式是正确的,后两个是错误的

SELECT OBJECT(o)
FROM Customer c, IN(c.orders) o
WHERE c.address.state = ‘CA’
ORDER BY o.quantity, o.totalcost

SELECT o.quantity
FROM Customer c, IN(c.orders) o
WHERE c.address.state = ‘CA’
ORDER BY o.quantity

SELECT l.product.product_name
FROM Order o, IN(o.lineItems) l
WHERE o.customer.lastname = ‘Smith’ AND o.customer.firstname = ‘John’
ORDER BY l.product.price

SELECT l.product.product_name
FROM Order o, IN(o.lineItems) l
WHERE o.customer.lastname = ‘Smith’ AND o.customer.firstname = ‘John’
ORDER BY o.quantity

如果在ORDER BY字句中有多个排序项(对应sql中有多个排序字段),排序规则是从左到右进行处理的。

ASC表示升序,DESC表示降序,默认是ASC

当记录中排序项的值存在null值时,这些记录排在所有非空值记录的前面或者排在所有非空值记录后面。

44.7.2.8. NULL值

当引用的对象不存在持久存储中,其值被视为NULL。SQL92中的NULL的语义定义了对包含NULL值的条件表达式的求值方式。

下面是这些语义的简短说明:

  • 使用NULL值的算术操作或比较操作总是产生一个UNKNOWN值;

  • 两个NULL值并不相等,两个NULL值的比较产生一个UNKNOWN值;

  • 使用UNKNOWN值的算术操作或比较操作总是产生一个UNKNOWN值;

  • 在求值期间包含NULL值的路径表达式返回NULL值;

  • IS NULL和IS NOT NULL运算符将一个包含NULL值的cmp-field或单值类型的cmr-field的值转换为TRUE或FALSE;

  • 布尔运算符的三个计算逻辑如下:

    AND运算符的定义

    ANDTFU
    TTFU
    FFFF
    UUFU

    OR运算符的定义

    ORTFU
    TTTT
    FTFU
    UTUU

    NOT运算符的定义

    NOT 
    TF
    FT
    UU

    [注意]注意

    EJB QL中,空字符串‘’,是零长度的字符串,不等于NULL。但当查询被映射到持久存储时,空字符串和NULL值并不总是可以明确分辨的。因此,开发者不应依赖EJB QL中涉及空字符串和NULL值的比较操作。

44.7.2.9. 相等语义

EJB QL只允许能对其使用like运算符的类型的值进行比较。但这个规则有一个例外,近似数字和精确数字是可以比较的(Java的数值提升提供了必要的类型转换)。

包括上文中提到的NULL值的例外,值的比较必须考察Java语言中的相关语义。例如,使用Java基本(primitive type)类型定义的cmp-field,不能假定其为NULL值。如此cmp-field域需要使用NULL值,则必须使用对应的引用(reference type)类型,如使用Integer替代int。持久存储中的存储机制不影响对字符串的比较,如数据填充(padding)的因素。当且仅当字符串包含同样的字符序列时,才可以决定两个字符串相等,这点与标准的SQL不同。

当且仅当同样抽象模式类型的Entity对象的Primary Key相等,才可以决定它们是相等的。

44.7.2.10. 查询语句的限制

日期和时间值应使用Java标准中的long类型的值,使用毫秒为单位。生成毫秒值的标准方法是使用java.util.Carlendar。

尽管SQL支持在算术表达式中,进行定点十进制数的比较。但是EJB QL并不支持。因为EJB QL限制精确数字类型的数字字面值中不可包含小数点(并且使用小数点作为近似类型数字的表示)。

字符串和布尔值的比较只能使用=,和<>。

在EJB QL查询语句中不能使用注释。

容器管理持久性的数据模型目前不支持继承。因此,不同类型的Entity对象或值不能进行比较。包含此类比较操作的查询语句无效。

44.7.3. 范例

以下例子用于演示EJB QL的语法与语义。例子基于本节开始时介绍的范例

44.7.3.1. 简单查询

查找所有订单:

SELECT OBJECT(o)
FROM Order o

查找所有需要运到加里福利亚的订单:

SELECT OBJECT(o)
FROM Order o
WHERE o.shipping_address.state = ‘CA’

查找所有订单涉及的州:

SELECT DISTINCT o.shipping_address.state
FROM Order o

44.7.3.2. 使用关系的查询

查找有line item的订单:

SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l

查找没有line item的订单:

SELECT OBJECT(o)
FROM Order o
WHERE o.lineItems IS EMPTY

查找未完成订单:

SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE l.shipped = FALSE

查找所有发货地址与收款地址不一样的订单,本例假定发货地址与收款地址都使用Entity Bean:

SELECT OBJECT(o)
FROM Order o
WHERE
    NOT (o.shipping_address.state = o.billing_address.state AND
    o.shipping_address.city = o.billing_address.city AND
    o.shipping_address.street = o.billing_address.street)

如果在两个不同的关系中发货地址与收款地址使用相同的Entity Bean表示,基于第 44.7.2.5.7.10 节 “NULL比较表达式”中的定义,以上查询可以简化为:

SELECT OBJECT(o)
FROM Order o
WHERE o.shipping_address <> o.billing_address

查询所有包含书名为“EJB deveplopment”的书的订单:

SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE l.product.type = ‘book’ AND
    l.product.name = ‘Applying Enterprise JavaBeans:
    Component-Based Development for the J2EE Platform’

44.7.3.3. 使用输入参数的查询

查询名字由输入参数指定的所有订单:

SELECT DISTINCT OBJECT(o)
FROM Order o, IN(o.lineItems) l
WHERE l.product.name = ?1

对于此查询,参数必须是产品名称的类型,java.lang.String。

44.7.3.4. 定义select方法的查询

下面查询演示值的选取而非Entity Bean的选取:

选取所有已被定购的产品的名称:

SELECT DISTINCT l.product.name
FROM Order o, IN(o.lineItems) l

以下查询在一个指定订单号码的订单中查找所有包含产品的产品名称。订单号码由一个输入参数指定,并且订单号码是订单的Primary Key。

SELECT l.product.name
FROM Order o, IN(o.lineItems) l
WHERE o.ordernumber = ?1

44.7.3.5. EJB QL与SQL

EJB QL与SQL相似,将FROM子句作为笛卡尔积,与SQL一样,即使未使用WHERE子句,已声明的标记变量(indetification variable)也会影响查询的结果。必须小心地定义标记变量,因为已声明类型的值是否存在,决定了查询的范围。

例如,下面的FROM子句定义了对订单的查询,订单必须包含line item和包含已有产品。如持久存储中不存在产品实例,查询的范围为空且不选取订单。

SELECT OBJECT(o)
FROM Order AS o, IN(o.lineItems) l, Product p

44.7.4. EJB QL BNF

EJB QL标记概述:

  • {...} 组

  • [...] 可选结构

  • 黑体

    关键字

EJB QL的完整BNF定义:

EJB QL ::= select_clause from_clause [where_clause]

from_clause ::=FROM identification_variable_declaration
    [, identification_variable_declaration]*

identification_variable_declaration ::= 
    collection_member_declaration |
    range_variable_declaration

collection_member_declaration ::=
    IN (collection_valued_path_expression) [AS ] identifier

range_variable_declaration ::= 
    abstract_schema_name [AS ] identifier

single_valued_path_expression ::=
    {single_valued_navigation | identification_variable}.cmp_field |
    single_valued_navigation

single_valued_navigation ::=
    identification_variable.[single_valued_cmr_field.]* 
    single_valued_cmr_field

collection_valued_path_expression ::=
    identification_variable.[single_valued_cmr_field.]*
    collection_valued_cmr_field

select_clause ::=
    SELECT [DISTINCT ] {single_valued_path_expression |
    OBJECT (identification_variable)}

where_clause ::=WHERE conditional_expression

conditional_expression ::= 
    conditional_term | conditional_expression OR conditional_term

conditional_term ::= 
    conditional_factor | conditional_term AND conditional_factor

conditional_factor ::= [NOT ] conditional_test

conditional_test :: = conditional_primary

conditional_primary ::= 
    simple_cond_expression | (conditional_expression)

simple_cond_expression ::= 
    comparison_expression | between_expression | like_expression |
    in_expression | null_comparison_expression |
    empty_collection_comparison_expression |
    collection_member_expression

between_expression ::=
    arithmetic_expression [NOT ]BETWEEN
    arithmetic_expression AND arithmetic_expression

in_expression ::=
    single_valued_path_expression [NOT ]IN (string_literal 
    [, string_literal]* )

like_expression ::=
    single_valued_path_expression [NOT ]LIKE 
    pattern_value [ESCAPE escape-character]

null_comparison_expression ::= 
    single_valued_path_expression IS [NOT ] NULL

empty_collection_comparison_expression ::=
    collection_valued_path_expression IS [NOT] EMPTY

collection_member_expression ::=
    {single_valued_navigation | 
    identification_variable | 
    input_parameter}
    [NOT ]MEMBER [OF ] collection_valued_path_expression

comparison_expression ::=
    string_value { =|<>} string_expression |
    boolean_value { =|<>} boolean_expression} |
   datetime_value { = | <> | > | < } datetime_expression |
    entity_bean_value { = | <> } entity_bean_expression |
    arithmetic_value comparison_operator single_value_designator

arithmetic_value ::= 
    single_valued_path_expression | functions_returning_numerics

single_value_designator ::= scalar_expression

comparison_operator ::=
    = | > | >= | < | <= | <>

scalar_expression ::= arithmetic_expression

arithmetic_expression ::= 
    arithmetic_term | arithmetic_expression 
    { + | - } arithmetic_term

arithmetic_term ::= 
    arithmetic_factor | arithmetic_term { * | / } arithmetic_factor

arithmetic_factor ::= { + |- } arithmetic_primary

arithmetic_primary ::= 
    single_valued_path_expression | literal | 
    (arithmetic_expression) |
    input_parameter | functions_returning_numerics

string_value ::= 
    single_valued_path_expression | functions_returning_strings

string_expression ::= string_primary | input_expression

string_primary ::= 
    single_valued_path_expression | literal | 
    (string_expression) | functions_returning_strings

datetime_value ::= single_valued_path_expression

datetime_expression ::= datetime_value | input_parameter

boolean_value ::= single_valued_path_expression

boolean_expression ::= 
    single_valued_path_expression | literal | input_parameter

entity_bean_value ::= 
    single_valued_navigation | identification_variable

entity_bean_expression ::= entity_bean_value | input_parameter

functions_returning_strings ::=
    CONCAT (string_expression, string_expression) |
    SUBSTRING (string_expression, arithmetic_expression, arithmetic_expression)

functions_returning_numerics::=
    LENGTH (string_expression) |
    LOCATE (string_expression, string_expression[, arithmetic_expression]) |
    ABS (arithmetic_expression) |
    SQRT (arithmetic_expression)