转载:http://bbs.51cto.com/thread-39001-1.html
EJB QL查询语言
EnterpriseJavaBeansQueryLanguage,简称EJBQL,是用于对容器管理持久性的EntityBean的查询进行定义的语言。EJBQL使EntityBean的查询方法的语义定义更具可移植性。
1.概述
EJBQL是用于容器管理持久性EntityBean中finder方法和select方法的标准查询语言。EJBQL可被编译成为目标语言,如数据库或其他持久存储所使用的SQL语言,从而将查询转移到由持久存储提供的本地(native)语言运行环境。因此,EJBQL在提供可移植能力的同时可提高执行效率。
EJBQL使用EntityBean的抽象持久模式(abstractpersistenceschema)和关系作为数据模型。在这些数据模型的基础上定义了运算符和表达式。
基于在部署描述中定义的抽象持久模式和关系,开发者使用EJBQL编写查询。相互关联的EntityBean中定义的cmp-field和cmr-field决定EntityBean之间进行定位(navigation)与选取(selection)的能力,EJBQL则取决于这种能力。通过在EJBQL中使用cmr-field域的名字,开发者可以从一个EntityBean定位到其他的EntityBean。
如在同一个部署描述文件中定义了EntityBean的抽象持久模式与EJBQL查询,则开发者可以在查询中使用EntityBean的抽象模式类型。
EJBQL有以下两种使用方式:
通过定义在EntityBean的Home接口中的finder方法,EJBQL可用于编写选取(select)Entity对象的查询。查询的结果可被EntityBean的客户端使用。
通过定义在EntityBean的组件类中的select方法,EJBQL可用于编写选取(select)Entity对象或从EntityBean的抽象模式类型派生的其他值的查询。开发者可以使用select方法查找对象或与EntityBean状态相关的值,且结果不会暴露给客户端。
一个EJBQL查询语句必须包含一个SELECT子句和一个FROM子句,可以包含一个WHERE子句。
2.定义
EJBQL使用类似于SQL的语法,基于EntityBean的抽象模式类型和关系,对值或对象进行选取。对于使用EntityBean的cmr-field定义的关系,可使用EJBQL中的路径表达式(pathexpressions)在关系中进行定位。
本节提供EJBQL语言的完整定义。
EJBQL是包含以下三个部分的字符串:
SELECT子句,用于定义被选取对象或值的类型;
FROM子句,对于在SELECT子句中表达式需要指定的范围和应用于WHERE子句的查询范围,可使用FROM子句进行声明;
可选的WHERE子句,用于限制查询返回的结果。
EJBQL可使用BNF语法定义如下:
EJBQL::=select_clausefrom_clause[where_clause]
EJBQL查询必须有一个SELECT子句和一个FROM子句,方括号表示WHERE是可选的。
对应于使用EJBQL查询定义语义的finder方法和select方法,查询可以使用输入参数。
EJBQL查询使用ejb-ql部署描述元素进行定义。
2.1.抽象持久类型与查询范围
EJBQL是基于EJB2.0容器管理持久性类型模型而设计的类型表达式语言。每个EJBQL表达式都拥有类型。表达式的类型取决于表达式的结构、标记变量声明(indetificationvariabledeclaration)的抽象模式类型、对cmp-field和cmr-field的进行求值的类型和字面值的类型。EJBQL中容许类型是EntityBean的抽象模式类型和cmp-field的定义类型。
由EntityBean类和部署描述中提供的信息得到EntityBean的抽象模式类型。抽象模式类型和EntityBean之间存在一对一的关系。定义在abstract-schema-name部署描述元素中的抽象模式的名字,用于在EJBQL中标识抽象模式类型。
抽象模式类型拥有以下特征:
对于每个对应部署描述中的一个cmp-field元素的EntityBean类的getter访问方法,存在一个域,其类型与cmp-field元素指定的类型相同。
EntityBean类中,对于每个对应部署描述中的cmr-field元素的getter访问方法,存在一个域,其类型是EntityBean的抽象模式类型。此EntityBean由包含在对应的ejb-relationship-role元素中的ejb-name子元素指定,如角色的多重性(multiplicity)为many,则类型是包含此EntityBean抽象模式类型实例的集合。
EJBQL查询的范围,由同一个部署描述中的所有容器管理持久性的EntityBean组成。
EJBQL查询的可定位范围,由EntityBean抽象模式类型中的cmr-field决定。使用cmr-field,查询可以选取关联的EntityBean和在查询中使用关联的EntityBean的抽象模式类型。
2.2.命名
在EJBQL查询字符串中使用EntityBean的抽象模式名来指定EntityBean。
在部署过程中,开发者需要为每个容器管理持久性的EntityBean分配一个唯一的抽象模式名字,以在查询中使用此名字来指定EntityBean。名字的作用域范围在整个部署描述文件之内。
2.3.范例
本节中所使用的EntityBean范例名字遵循如下约定:EntityBean使用EJB方式进行命名,EntityBean类和抽象模式类型使用方式进行命名。如表示订单的EntityBean命名为OrderEJB,其EntityBean类和抽象模式类型名称为Order。
例如某个部署描述文件中包含如下几个EntityBean,OrderEJB、ProductEJB、LineItemEJB、ShippingAddressEJB和BillingAddressEJB,对应的抽象模式类型名字为Order、Product、LineItem、ShippingAddress和BillingAddress。只有OrderEJB和ProductEJB拥有远程和远程Home接口。
以上EntityBean之间的关系如下图:
开发者可以通过Order和LineItem中定义的cmr-field和cmp-field,为OrderEJB定义finder查询,查找所有未完成的Order的finder查询可用如下方式编写:
SELECTDISTINCTOBJECT(o)
FROMOrderASo,IN(o.lineItems)ASl
WHEREl.shipped=FALSE
查询定位到Order抽象模式类型中的cmr-field域lineItems,并在其中中遍历对lineItems进行查找,并使用LineItem中的cmp-field域shipped选择至少有一个lineItem未发送的Order的集合。
尽管上面的例子使用了如SELECT、DISTINCT、OBJECT、FROM、AS、IN、WHERE和FALSE等大写的保留字,但是,EJBQL中的保留字是大小写无关的。
上例中的SELECT子句指定了查询的返回值类型是Order,如这个查询语句用于定义一个在EntityBean的远程Home接口中的finder方法,则对应于由查询所选取的抽象模式类型实例,方法返回值类型为EntityBean的远程接口类型。如这个查询语句用于定义一个在EntityBean的本地Home接口中的finder方法,则方法返回值类型为EntityBean的本地接口类型。
因为同一个部署描述中定义了相关的EntityBean的抽象持久模式,所以同样可以在OrderEJB的查询中使用ProductEJB的抽象模式类型,也可使用Order和Product抽象模式类型中的cmp-field和cmr-field。例如,Product抽象模式类型中包含一个product_type的cmp-field,OrderEJB中的finder查询可以使用这个cmp-field。如“查找包含产品种类为‘办公用品’的订单”,则可以用如下查询语句定义查询方法:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.product_type=‘office_supplies’
通过Order与LineItem、LineItem和Product的关联,Order关联到Product,为表达此查询,必须通过lineItem和product这两个cmr-field进行定位(navigation)。通过在整个语句范围内指定使用OrderEJB的抽象模式名称Order指定了查询的类型。
本部分文档中的余下部分将对本节中的例子进行扩展,以详细说明EJBQL的特性和用法。
2.4.返回值类型
EJBQL查询使用EntityBean的抽象模式类型进行编写。使用SELECT子句指定的返回值类型,应该是EntityBean的抽象模式类型或者是cmp-field类型。finder和select方法和在部署描述文件中定义的查询语句,决定了finder和select方法返回的结果被映射到何种Java类型。
查询结果如何映射取决于查询语句定义的查询方法是finder方法还是select方法,也取决于finder方法是定义在远程Home接口中还是定义在本地Home接口中。
如前面的例子:“查找包含产品种类为‘办公用品’的订单”
如查询被用于远程Home接口中的finder方法,finder方法的结果是EntityBean的远程接口(或是远程接口的集合)。如查询被用于本地Home接口中的finder方法,finder方法的结果是EntityBean的本地接口(或是本地接口的集合)。
同样的查询如被用于select方法,返回值类型是EntityBean的远程接口或本地接口实例,最终返回的何种接口则取决于包含在query元素中的部署描述元素result-type-mapping的值是Local还是Remote,缺省为Local。
2.5.FROM子句与定位声明
EJBQL的FROM子句通过声明标记变量(indetificationvariable)定义查询的范围。范围可以使用路径表达式进行限制。
标记变量(indetificationvariable)指定特定的EntityBean的抽象模式类型实例。通过在标记变量(indetificationvariable)之间使用逗号分隔,FROM子句可以包含多个标记变量(indetificationvariable)。
from_clause::=
FROMidentification_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子句的构成。
2.5.1.标识符(Identifier)
标识符是不限长的字符序列,首字符必须使用Java标识符的首字符(startcharacter)开头,其他字母必须是Java标识符的组成字符(partcharacter)。首字符可以是任何使用Character.isJavaIdentifierStart方法返回真值的字符,包括下划线(_)字符和美元符($)。其他字符则可以是使用Character.isJavaIdentifierPart方法返回真值的字符,问号(?)是EJBQL的保留字符。
以下是EJBQL的保留字列表:SELECT、FROM、WHERE、DISTINCT、OBJECT、NULL、TRUE、FALSE、NOT、AND、OR、BETWEEN、LIKE、IN、AS、UNKNOWN、EMPTY、MEMBER、OF、IS
保留字是大小写无关的。
建议不要在EJBQL查询语句中使用SQL的保留字作为标识符,EJBQL的以后版本可能会使用此类保留字。
2.5.2.标记变量(indentificationvariables)
标记变量(indetificationvariable)是EJBQL查询的FROM子句中声明的一个有效标识符。标记变量(indetificationvariable)可以使用特殊操作符IN和可选的操作符AS进行声明。
标记变量(indetificationvariable)必须声明在FROM子句中。
一个标记变量(indetificationvariable)是一个标识符。标记变量(indetificationvariable)不能是保留字或:
抽象模式名(abstract-schema-name)
EJB名字(ejb-name)
标记变量(indetificationvariable)是大小写有关的。
标记变量(indetificationvariable)的求值使用声明变量的表达式类型。如上面OrderEJB中finder方法查询语句:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.product_type=‘office_supplies’
FROM子句中声明了IN(o.lineItems)l,则标记变量(indetificationvariable)l对Order直接关联的所有lineItem进行求值。cmr-field域lineItems是抽象模式类型LineItem的实例集合,l是集合中的一个元素,l的类型是LineItem的抽象模式类型。
标记变量(indetificationvariable)用于指定一个EntityBean的抽象模式类型的实例或者EntityBean的抽象模式类型实例集合中的一个元素。
因此,标记变量(indetificationvariable)代表对某个值的引用,并以以下两种方式进行声明:范围(range)变量或者集合成员变量:
范围变量使用EntityBean的抽象模式名进行声明;
集合成员变量使用得到集合结果的路径表达式进行声明。
在FROM子句中,标记变量(indetificationvariable)声明由左到右进行求值。查询语句中后续的标记变量(indetificationvariable)声明可以可以使用前面标记变量(indetificationvariable)声明的结果。
2.5.3.范围变量声明
一个标记变量(indetificationvariable)包括一个EntityBean的抽象模式类型。将标记变量(indetificationvariable)声明为一个范围变量的EJBQL语法与SQL相同:使用可选的AS操作符进行声明。
关联到EntityBean的对象和变量一般使用路径表达式进行定位。但是,定位不可能针对所有的对象。对通过定位不能到达的对象,范围变量声明允许开发者为其指派一个“root”。
因此,开发者可在查询中使用多个范围变量声明对相同抽象模式类型的多个值进行比较。参见select子句。
2.5.4.集合成员变量声明
一个使用collection_member_declaration声明的标记变量(indetificationvariable)包括通过路径表达式定位取得的集合中的所有值。路径表达式表示包含EntityBean抽象模式类型的cmr-field的定位操作。因为一个路径表达式可以基于另一个路径表达式,则定位操作可以使用关联的EntityBean的cmr-field。参见路径表达式。
集合成员变量声明使用特殊操作符IN进行声明。用IN操作符指定的功能表达式接收一个集合值类型的路径表达式参数。路径表达式使用集合类型进行求值,此集合类型是对EntityBean抽象模式类型中一个集合类型的cmr-field的定位操作,所得结果的类型。
例如,OrderEJB中finder方法的查询语句中,FROM子句包含如下标记变量(indetificationvariable)声明子句:
IN(o.lineItems)l
lineItems是一个cmr-field域的名字,其值是LineItemEJB的抽象模式类型LineItem的实例集合。标记变量(indetificationvariable)l是集合中的一个成员,一个单个的LineItem实例。本例中的o是抽象模式类型Order的一个标记变量(indetificationvariable)。
2.5.5.范例
下面的FROM子句包含两个标记变量(indetificationvariable)的声明子句,第一个子句中声明的标记变量(indetificationvariable)被第二个子句使用,此子句声明两个变量o和l。OrderASo是一个范围变量声明,将变量o指定为范围变量,类型为抽象模式类型Order。变量l的类型为抽象模式类型LineItem。因子句从左到右求值,变量l可以使用o的结果进行定位操作。
FROMOrderASo,IN(o.lineItems)l
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的路径表达式类型是关联的EntityBean的抽象模式类型。
一个collection_valued_cmr_field的指定需要使用one-to-many或many-to-many关系中的一个cmr-field的名字。表达式的类型是关联的EntityBean的抽象模式类型。collection_valued_cmr_field的类型是关联的EntityBean的抽象模式类型的值的集合。
对关联EntityBean进行定位操作得到的值,其类型是此关联EntityBean的抽象模式类型。
例如,如l是表示LineItem类型实例的标记变量(indetificationvariable),路径表达式l.product的类型为抽象模式类型Product。
对路径表达式的求值以一个cmr-field终止,将得出一个此cmr-field的Java类型的值。如l.product.name得到的类型是java.lang.String。
一个路径表达式由一个类型为集合的路径表达式构成,在语法上是正确的。例如,如果o的类型是Order,路径表达式o.lineItems.product是正确的,因为对lineItems的定位将得到一个集合。当验证查询语句的时候,这种情况将产生错误。为处理这种定位,在FROM子句中必须声明一个用于包含lineItems集合元素的标记变量(indetificationvariable),必须在查询语句的WHERE子句中,使用另一个路径表达式,才能对每个元素进行定位操作。如下例:
SELECTOBJECT(o)
FROMOrderASo,IN(o.lineItems)l
WHEREl.product.name=‘widget’
在Apusic应用服务器中,如果EntityBean的抽象持久类型中包含辅助值对象(DependentValueObject)时,可使用“.”运算符访问EntityBean内部的辅助值对象的域。如上一章中容器管理持久型的EntityBean中使用的范例,Customer实体中包含了类型为ContactInfo的info域,则可以使用如下EJBQL查询语句查询ContactInfo中name为“JimClark”的实例:
SELECTOBJECT(o)
FROMCustomerASo
WHEREo.info.name=‘JimClark’
2.5.7.WHERE子句与条件表达式
WHERE子句由用于选取对象和值的条件表达式构成。WHERE子句对查询的结果进行限制。
WHERE子句的定义如下:
where_clause::=WHEREconditional_expression
字面值(literals)
字符串的字面值由单引号括起,如:‘literal’。包含单引号的字符串字面值,将使用两个连续的单引号表示一个单引号,例如:‘literal‘‘s’。EJBQL中的字符串字面值如同Java中的字符串字面值一样,使用Unicode字符编码。
一个精确数字字面值是一个不带小数点的数字值,如57、-957、+62。精确数字字面值支持Java中long型的数值范围。精确数字字面值使用Java中的整数语法。
一个近似数字字面值是使用科学计数法表示的数字值,如7E3、-59.7E2,或是带小数点的数字值,如7.、-95.7、+6.2。精确数字字面值支持Java中double型的数值范围。精确数字字面值使用Java中的浮点数语法。
可以使用Java语言规范中规定的后缀指明字面的确切类型。
布尔值的字面值为大小写无关的TRUE和FALSE。
标记变量(indetificationvariable)
在EJBQL查询语句中,所有的用在WHERE子句的标记变量(indetificationvariable)必须在FROM子句中声明,参见标记变量(indetificationvariable)。
在WHERE子句中,标记变量(indetificationvariable)被实际量化,即标记变量(indetificationvariable)代表EntityBean的抽象模式类型的单个实例或实例集合中的成员。标记变量(indetificationvariable)不能被代表一个表示整个集合的值。
如标记变量(indetificationvariable)代表一个空集合中的成员,则标记变量(indetificationvariable)的值为unknown。
路径表达式
在WHERE子句中,除了empty_collection_comparison_expression和collection_member_expression,使用collection_valued_path_expression作为条件表达式的一部分是不合法的。
如果路径表达式由一个代表unknown值的标记变量(indetificationvariable)构成,则路径表达式的值为unknown。
输入参数
以下是关于输入参数的规则。输入参数只能用于查询中的WHERE子句。
输入参数由问号(?)前缀和一个后续整数表示,例如:?1。
输入参数由1开始编号。
EJBQL查询中不同输入参数的编号不能超出finder或select方法的输入参数编号。EJBQL查询中不必使用所有的finder或select方法的参数。
输入参数只能用于comparison_expressions或collection_member_expressions。参见EJBQLBNF。
输入参数使用查询关联的finder或select方法中的对应参数类型进行求值。
如finder或select方法的输入参数是一个EJBObject类型或EJBLocalObject类型,输入参数将会对应到正确的抽象模式类型。
条件表达式构成
一个条件表达式由其他条件表达式、比较操作、逻辑操作、求布尔值的路径表达式和布尔值的字面值构成。
算术表达式可用于比较表达式中。一个算术表达式由其他算术表达式、算术操作、求数值的路径表达式和数值的字面值构成。
算术操作使用Java中的数值提升(numericpromotion)规则。
支持使用括号()对表达式的求值顺序进行排序。
条件表达式定义如下:
conditional_expression::=
conditional_term|conditional_expressionORconditional_term
conditional_term::=
conditional_factor|conditional_termANDconditional_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
运算符与优先级
按照由高到低的优先级,运算符排列如下:
定位运算符(.);
算术运算符:
+,-一元运算符
*,/乘除运算符
+,-加减运算符
比较运算符:=、>、>=、(不等于);
逻辑运算符:NOT、AND、OR
下面描述在特殊表达式中的运算符。
BETWEEN表达式
在条件表达式中,使用比较运算符[NOT]BETWEEN的语法如下:
arithmetic_expression[NOT]BETWEENarithmetic-exprANDarithmetic-expr
例如:
p.ageBETWEEN15AND19
等价于
p.age>=15ANDp.age
如用在一个BETWEEN表达式中的算术表达式的值是NULL,则BETWEEN表达式的值为UNKNOWN。
IN表达式
在条件表达式中使用比较运算符[NOT]IN的语法如下:
single_valued_path_expression[NOT]IN(string-literal[,string-literal]*)
其中,single_valued_path_expression必须是字符串值。
例如:
o.countryIN('UK','US','France')
等价于
(o.country=’UK’)OR(o.country=’US’)OR(o.country=’France’)
另一个例子:
o.countryNOTIN(’UK’,’US’,’France’)
等价于
NOT((o.country=’UK’)OR(o.country=’US’)OR(o.country=’France’))
在IN表达式中定义的字符串字面值列表中,使用逗号分隔,必须包含至少一个字符串字面值。
如用在一个IN表达式中的single_valued_path_expression表达式的值是NULL,则IN表达式的值为UNKNOWN。
LIKE表达式
在条件表达式中,使用比较运算符[NOT]LIKE的语法如下:
single_valued_path_expression[NOT]LIKEpattern-value[ESCAPEescape-character]
其中,single_valued_path_expression必须是字符串值。pattern-value是一个字符串字面值,pattern-value中的下划线(_)表示一个单个的字符、百分号(%)表示字符串序列(包括一个空序列),所有的其他字符表示自身。可选的escape-character是一个单字符的字符串字面值,用于将pattern-value中出现的下划线(_)和百分号(%)进行转义。
例如:
address.phoneLIKE'12%3'
当address.phone代表的值为“123”,“12443”等值时,此表达式为真;当address.phone代表的值为“124”,“1234”等值时,此表达式为假。
asentence.wordLIKE'l_se'
当asentence.word代表的值为“lose”时,此表达式为真;当asendence.word代表的值为“loose”时,此表达式为假。
aword.underscoredLIKE'\_%'ESCAPE'\'
当aword.underscored代表的值为“_foo”时,此表达式为真;当aword.underscored代表的值为“bar”时,此表达式为假;
address.phoneNOTLIKE'12%3'
当address.phone代表的值为“12”,“1234”等值时,此表达式为真;当address.phone代表的值为“123”,“12443”等值时,此表达式为假。
如用single_valued_path_expression表达式的值是NULL,则表达式的值为UNKNOWN。
NULL比较表达式
在条件表达式中,使用比较运算符ISNULL的语法如下:
single_valued_path_expressionIS
[NOT]NULL
NULL比较表达式检查single_valued_path_expression代表的值是否为NULL值。
包含NULL值的路径表达式在求值期间返回NULL值。
空集合比较表达式
在empty_collection_comparison_expression表达式中,使用比较运算符IS[NOT]EMPTY的语法如下:
collection_valued_path_expressionIS[NOT]EMPTY
此表达式检查集合值路径表达式代表的集合是否包含元素。
如果一个集合值路径表达式代表的集合被用在一个空集合比较表达式中,则不能在FROM子句中声明此集合为一个标记变量(indetificationvariable)。如果标记变量(indetificationvariable)被显式声明为一个集合中的元素,则表示存在一个非空的关系,即集合一定包含元素,检查这样的的一个集合是否为空是与前提矛盾的。因此,类似于下例的查询无效:
SELECTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREo.lineItemsISEMPTY
如用在一个空集合比较表达式中的集合值路径表达式的值不包含元素,则空集合比较表达式的值为UNKNOWN。
集合成员表达式
在collection_member_expression(集合成员表达式)中,使用比较运算符ISMEMBEROF的语法如下:
{single_valued_navigation|identification_variable|input_parameter}
[NOT]MEMBER[OF]collection_valued_path_expression
表达式检查指定的值是否是一个集合的成员,此集合由集合值路径表达式指定。
如果在集合成员表达式中的集合值路径表达式代表的集合不包含元素,则此表达式的值为FALSE。
功能表达式
EJBQL包含以下内置的功能表达式。
字符串函数:
CONCAT(String,String),返回一个字符串;
SUBSTRING(String,start,length),返回一个字符串;
LOCATE(String,String[,start]),返回一个字符串;
LENGTH(String),返回一个整型值;
start和length是int型的参数,用于指定字符串中的位置。
算术函数:
ABS(number)
SQRT(double)
2.6.SELECT子句
SELECT子句表示查询的结果。因为finder方法不能返回任意类型的值,定义fnder方法的查询语句中的SELECT子句必须对应finder方法返回的EntityBean的抽象模式类型;对于select方法,可以返回EntityBean的抽象模式类型和cmp-field域的值。
SELECT子句的语法如下:
select_clause::=
SELECT[DISTINCT]{single_valued_path_expression|
OBJECT(identification_variable)}
SELECT子句中所有的标记变量(indetificationvariable)必须使用OBJECT运算符进行限制。SELECT子句中不能使用OBJECT运算符对路径表达式进行限制。
DISTINCT关键字从查询结果中删除重复的值。
如应用查询语句的方法返回值类型是java.util.Collection,如查询语句中未使用DISTINCT关键字,则返回的集合中可包含重复的元素;如方法的返回值类型是java.util.Set,查询语句中未使用DISTINCT关键字,则返回的集合中也不包含重复的元素。
例子:
SELECTl.productFROMOrderASo,IN(o.lineItems)l
注意SELECT子句必须指定返回的是一个单值表达式。下例是无效的查询语句:
SELECTo.lineItemsFROMOrderASo
如希望通过比较多个EntityBean抽象模式类型来选取值,则需要在FROM子句中声明多个抽象模式类型的标记变量(indetificationvariable)。
下面的finder方法的查询语句返回订单数量大于JohnSmith的订单数量的订单。本例中使用了两个不同的标记变量(indetificationvariable),都使用Order抽象模式类型。
SELECTDISTINCTOBJECT(o1)
FROMOrdero1,Ordero2
WHEREo1.quantity>o2.quantityAND
o2.customer.lastname=‘Smith’AND
o2.customer.firstname=‘John’
下例返回订单的所有订单项:
SELECTOBJECT(l)
FROMOrdero,IN(o.lineItems)l
下例返回所有的订单项:
SELECTOBJECT(l)
FROMLineItemsASl
2.7.NULL值
当引用的对象不存在持久存储中,其值被视为NULL。SQL92中的NULL的语义定义了对包含NULL值的条件表达式的求值方式。
下面是这些语义的简短说明:
使用NULL值的算术操作或比较操作总是产生一个UNKNOWN值;
两个NULL值并不相等,两个NULL值的比较产生一个UNKNOWN值;
使用UNKNOWN值的算术操作或比较操作总是产生一个UNKNOWN值;
在求值期间包含NULL值的路径表达式返回NULL值;
ISNULL和ISNOTNULL运算符将一个包含NULL值的cmp-field或单值类型的cmr-field的值转换为TRUE或FALSE;
布尔运算符的三个计算逻辑如下:
AND运算符的定义
ANDTFU
TTFU
FFFF
UUFU
OR运算符的定义
ORTFU
TTTT
FTFU
UTUU
NOT运算符的定义
NOT
TF
FT
UU
注意:
EJBQL中,空字符串‘’,是零长度的字符串,不等于NULL。但当查询被映射到持久存储时,空字符串和NULL值并不总是可以明确分辨的。因此,开发者不应依赖EJBQL中涉及空字符串和NULL值的比较操作。
2.8.相等语义
EJBQL只允许能对其使用like运算符的类型的值进行比较。但这个规则有一个例外,近似数字和精确数字是可以比较的(Java的数值提升提供了必要的类型转换)。
包括上文中提到的NULL值的例外,值的比较必须考察Java语言中的相关语义。例如,使用Java基本(primitivetype)类型定义的cmp-field,不能假定其为NULL值。如此cmp-field域需要使用NULL值,则必须使用对应的引用(referencetype)类型,如使用Integer替代int。持久存储中的存储机制不影响对字符串的比较,如数据填充(padding)的因素。当且仅当字符串包含同样的字符序列时,才可以决定两个字符串相等,这点与标准的SQL不同。
当且仅当同样抽象模式类型的Entity对象的PrimaryKey相等,才可以决定它们是相等的。
2.9.查询语句的限制
日期和时间值应使用Java标准中的long类型的值,使用毫秒为单位。生成毫秒值的标准方法是使用java.util.Carlendar。
尽管SQL支持在算术表达式中,进行定点十进制数的比较。但是EJBQL并不支持。因为EJBQL限制精确数字类型的数字字面值中不可包含小数点(并且使用小数点作为近似类型数字的表示)。
字符串和布尔值的比较只能使用=,和。
在EJBQL查询语句中不能使用注释。
容器管理持久性的数据模型目前不支持继承。因此,不同类型的Entity对象或值不能进行比较。包含此类比较操作的查询语句无效。
3.范例
以下例子用于演示EJBQL的语法与语义。例子基于本节开始时介绍的范例。
3.1.简单查询
查找所有订单:
SELECTOBJECT(o)
FROMOrdero
查找所有需要运到加里福利亚的订单:
SELECTOBJECT(o)
FROMOrdero
WHEREo.shipping_address.state=‘CA’
查找所有订单涉及的州:
SELECTDISTINCTo.shipping_address.state
FROMOrdero
3.2.使用关系的查询
查找有lineitem的订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
查找没有lineitem的订单:
SELECTOBJECT(o)
FROMOrdero
WHEREo.lineItemsISEMPTY
查找未完成订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.shipped=FALSE
查找所有发货地址与收款地址不一样的订单,本例假定发货地址与收款地址都使用EntityBean:
SELECTOBJECT(o)
FROMOrdero
WHERE
NOT(o.shipping_address.state=o.billing_address.stateAND
o.shipping_address.city=o.billing_address.cityAND
o.shipping_address.street=o.billing_address.street)
如果在两个不同的关系中发货地址与收款地址使用相同的EntityBean表示,基于NULL比较表达式中的定义,以上查询可以简化为:
SELECTOBJECT(o)
FROMOrdero
WHEREo.shipping_addresso.billing_address
查询所有包含书名为“EJBdeveplopment”的书的订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.type=‘book’AND
l.product.name=‘ApplyingEnterpriseJavaBeans:
Component-BasedDevelopmentfortheJ2EEPlatform’
3.3.使用输入参数的查询
查询名字由输入参数指定的所有订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.name=?1
对于此查询,参数必须是产品名称的类型,java.lang.String。
3.4.定义select方法的查询
下面查询演示值的选取而非EntityBean的选取:
选取所有已被定购的产品的名称:
SELECTDISTINCTl.product.name
FROMOrdero,IN(o.lineItems)l
以下查询在一个指定订单号码的订单中查找所有包含产品的产品名称。订单号码由一个输入参数指定,并且订单号码是订单的PrimaryKey。
SELECTl.product.name
FROMOrdero,IN(o.lineItems)l
WHEREo.ordernumber=?1
3.5.EJBQL与SQL
EJBQL与SQL相似,将FROM子句作为笛卡尔积,与SQL一样,即使未使用WHERE子句,已声明的标记变量(indetificationvariable)也会影响查询的结果。必须小心地定义标记变量,因为已声明类型的值是否存在,决定了查询的范围。
例如,下面的FROM子句定义了对订单的查询,订单必须包含lineitem和包含已有产品。如持久存储中不存在产品实例,查询的范围为空且不选取订单。
SELECTOBJECT(o)
FROMOrderASo,IN(o.lineItems)l,Productp
4.EJBQLBNF
EJBQL标记概述:
{...}组
[...]可选结构
黑体关键字
EJBQL的完整BNF定义:
EJBQL::=select_clausefrom_clause[where_clause]
from_clause::=FROMidentification_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::=WHEREconditional_expression
conditional_expression::=
conditional_term|conditional_expressionORconditional_term
conditional_term::=
conditional_factor|conditional_termANDconditional_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_expressionANDarithmetic_expression
in_expression::=
single_valued_path_expression[NOT]IN(string_literal
[,string_literal]*)
like_expression::=
single_valued_path_expression[NOT]LIKE
pattern_value[ESCAPEescape-character]
null_comparison_expression::=
single_valued_path_expressionIS[NOT]NULL
empty_collection_comparison_expression::=
collection_valued_path_expressionIS[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{=||>|
entity_bean_value{=|}entity_bean_expression|
arithmetic_valuecomparison_operatorsingle_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)
1.概述
EJBQL是用于容器管理持久性EntityBean中finder方法和select方法的标准查询语言。EJBQL可被编译成为目标语言,如数据库或其他持久存储所使用的SQL语言,从而将查询转移到由持久存储提供的本地(native)语言运行环境。因此,EJBQL在提供可移植能力的同时可提高执行效率。
EJBQL使用EntityBean的抽象持久模式(abstractpersistenceschema)和关系作为数据模型。在这些数据模型的基础上定义了运算符和表达式。
基于在部署描述中定义的抽象持久模式和关系,开发者使用EJBQL编写查询。相互关联的EntityBean中定义的cmp-field和cmr-field决定EntityBean之间进行定位(navigation)与选取(selection)的能力,EJBQL则取决于这种能力。通过在EJBQL中使用cmr-field域的名字,开发者可以从一个EntityBean定位到其他的EntityBean。
如在同一个部署描述文件中定义了EntityBean的抽象持久模式与EJBQL查询,则开发者可以在查询中使用EntityBean的抽象模式类型。
EJBQL有以下两种使用方式:
通过定义在EntityBean的Home接口中的finder方法,EJBQL可用于编写选取(select)Entity对象的查询。查询的结果可被EntityBean的客户端使用。
通过定义在EntityBean的组件类中的select方法,EJBQL可用于编写选取(select)Entity对象或从EntityBean的抽象模式类型派生的其他值的查询。开发者可以使用select方法查找对象或与EntityBean状态相关的值,且结果不会暴露给客户端。
一个EJBQL查询语句必须包含一个SELECT子句和一个FROM子句,可以包含一个WHERE子句。
2.定义
EJBQL使用类似于SQL的语法,基于EntityBean的抽象模式类型和关系,对值或对象进行选取。对于使用EntityBean的cmr-field定义的关系,可使用EJBQL中的路径表达式(pathexpressions)在关系中进行定位。
本节提供EJBQL语言的完整定义。
EJBQL是包含以下三个部分的字符串:
SELECT子句,用于定义被选取对象或值的类型;
FROM子句,对于在SELECT子句中表达式需要指定的范围和应用于WHERE子句的查询范围,可使用FROM子句进行声明;
可选的WHERE子句,用于限制查询返回的结果。
EJBQL可使用BNF语法定义如下:
EJBQL::=select_clausefrom_clause[where_clause]
EJBQL查询必须有一个SELECT子句和一个FROM子句,方括号表示WHERE是可选的。
对应于使用EJBQL查询定义语义的finder方法和select方法,查询可以使用输入参数。
EJBQL查询使用ejb-ql部署描述元素进行定义。
2.1.抽象持久类型与查询范围
EJBQL是基于EJB2.0容器管理持久性类型模型而设计的类型表达式语言。每个EJBQL表达式都拥有类型。表达式的类型取决于表达式的结构、标记变量声明(indetificationvariabledeclaration)的抽象模式类型、对cmp-field和cmr-field的进行求值的类型和字面值的类型。EJBQL中容许类型是EntityBean的抽象模式类型和cmp-field的定义类型。
由EntityBean类和部署描述中提供的信息得到EntityBean的抽象模式类型。抽象模式类型和EntityBean之间存在一对一的关系。定义在abstract-schema-name部署描述元素中的抽象模式的名字,用于在EJBQL中标识抽象模式类型。
抽象模式类型拥有以下特征:
对于每个对应部署描述中的一个cmp-field元素的EntityBean类的getter访问方法,存在一个域,其类型与cmp-field元素指定的类型相同。
EntityBean类中,对于每个对应部署描述中的cmr-field元素的getter访问方法,存在一个域,其类型是EntityBean的抽象模式类型。此EntityBean由包含在对应的ejb-relationship-role元素中的ejb-name子元素指定,如角色的多重性(multiplicity)为many,则类型是包含此EntityBean抽象模式类型实例的集合。
EJBQL查询的范围,由同一个部署描述中的所有容器管理持久性的EntityBean组成。
EJBQL查询的可定位范围,由EntityBean抽象模式类型中的cmr-field决定。使用cmr-field,查询可以选取关联的EntityBean和在查询中使用关联的EntityBean的抽象模式类型。
2.2.命名
在EJBQL查询字符串中使用EntityBean的抽象模式名来指定EntityBean。
在部署过程中,开发者需要为每个容器管理持久性的EntityBean分配一个唯一的抽象模式名字,以在查询中使用此名字来指定EntityBean。名字的作用域范围在整个部署描述文件之内。
2.3.范例
本节中所使用的EntityBean范例名字遵循如下约定:EntityBean使用EJB方式进行命名,EntityBean类和抽象模式类型使用方式进行命名。如表示订单的EntityBean命名为OrderEJB,其EntityBean类和抽象模式类型名称为Order。
例如某个部署描述文件中包含如下几个EntityBean,OrderEJB、ProductEJB、LineItemEJB、ShippingAddressEJB和BillingAddressEJB,对应的抽象模式类型名字为Order、Product、LineItem、ShippingAddress和BillingAddress。只有OrderEJB和ProductEJB拥有远程和远程Home接口。
以上EntityBean之间的关系如下图:
开发者可以通过Order和LineItem中定义的cmr-field和cmp-field,为OrderEJB定义finder查询,查找所有未完成的Order的finder查询可用如下方式编写:
SELECTDISTINCTOBJECT(o)
FROMOrderASo,IN(o.lineItems)ASl
WHEREl.shipped=FALSE
查询定位到Order抽象模式类型中的cmr-field域lineItems,并在其中中遍历对lineItems进行查找,并使用LineItem中的cmp-field域shipped选择至少有一个lineItem未发送的Order的集合。
尽管上面的例子使用了如SELECT、DISTINCT、OBJECT、FROM、AS、IN、WHERE和FALSE等大写的保留字,但是,EJBQL中的保留字是大小写无关的。
上例中的SELECT子句指定了查询的返回值类型是Order,如这个查询语句用于定义一个在EntityBean的远程Home接口中的finder方法,则对应于由查询所选取的抽象模式类型实例,方法返回值类型为EntityBean的远程接口类型。如这个查询语句用于定义一个在EntityBean的本地Home接口中的finder方法,则方法返回值类型为EntityBean的本地接口类型。
因为同一个部署描述中定义了相关的EntityBean的抽象持久模式,所以同样可以在OrderEJB的查询中使用ProductEJB的抽象模式类型,也可使用Order和Product抽象模式类型中的cmp-field和cmr-field。例如,Product抽象模式类型中包含一个product_type的cmp-field,OrderEJB中的finder查询可以使用这个cmp-field。如“查找包含产品种类为‘办公用品’的订单”,则可以用如下查询语句定义查询方法:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.product_type=‘office_supplies’
通过Order与LineItem、LineItem和Product的关联,Order关联到Product,为表达此查询,必须通过lineItem和product这两个cmr-field进行定位(navigation)。通过在整个语句范围内指定使用OrderEJB的抽象模式名称Order指定了查询的类型。
本部分文档中的余下部分将对本节中的例子进行扩展,以详细说明EJBQL的特性和用法。
2.4.返回值类型
EJBQL查询使用EntityBean的抽象模式类型进行编写。使用SELECT子句指定的返回值类型,应该是EntityBean的抽象模式类型或者是cmp-field类型。finder和select方法和在部署描述文件中定义的查询语句,决定了finder和select方法返回的结果被映射到何种Java类型。
查询结果如何映射取决于查询语句定义的查询方法是finder方法还是select方法,也取决于finder方法是定义在远程Home接口中还是定义在本地Home接口中。
如前面的例子:“查找包含产品种类为‘办公用品’的订单”
如查询被用于远程Home接口中的finder方法,finder方法的结果是EntityBean的远程接口(或是远程接口的集合)。如查询被用于本地Home接口中的finder方法,finder方法的结果是EntityBean的本地接口(或是本地接口的集合)。
同样的查询如被用于select方法,返回值类型是EntityBean的远程接口或本地接口实例,最终返回的何种接口则取决于包含在query元素中的部署描述元素result-type-mapping的值是Local还是Remote,缺省为Local。
2.5.FROM子句与定位声明
EJBQL的FROM子句通过声明标记变量(indetificationvariable)定义查询的范围。范围可以使用路径表达式进行限制。
标记变量(indetificationvariable)指定特定的EntityBean的抽象模式类型实例。通过在标记变量(indetificationvariable)之间使用逗号分隔,FROM子句可以包含多个标记变量(indetificationvariable)。
from_clause::=
FROMidentification_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子句的构成。
2.5.1.标识符(Identifier)
标识符是不限长的字符序列,首字符必须使用Java标识符的首字符(startcharacter)开头,其他字母必须是Java标识符的组成字符(partcharacter)。首字符可以是任何使用Character.isJavaIdentifierStart方法返回真值的字符,包括下划线(_)字符和美元符($)。其他字符则可以是使用Character.isJavaIdentifierPart方法返回真值的字符,问号(?)是EJBQL的保留字符。
以下是EJBQL的保留字列表:SELECT、FROM、WHERE、DISTINCT、OBJECT、NULL、TRUE、FALSE、NOT、AND、OR、BETWEEN、LIKE、IN、AS、UNKNOWN、EMPTY、MEMBER、OF、IS
保留字是大小写无关的。
建议不要在EJBQL查询语句中使用SQL的保留字作为标识符,EJBQL的以后版本可能会使用此类保留字。
2.5.2.标记变量(indentificationvariables)
标记变量(indetificationvariable)是EJBQL查询的FROM子句中声明的一个有效标识符。标记变量(indetificationvariable)可以使用特殊操作符IN和可选的操作符AS进行声明。
标记变量(indetificationvariable)必须声明在FROM子句中。
一个标记变量(indetificationvariable)是一个标识符。标记变量(indetificationvariable)不能是保留字或:
抽象模式名(abstract-schema-name)
EJB名字(ejb-name)
标记变量(indetificationvariable)是大小写有关的。
标记变量(indetificationvariable)的求值使用声明变量的表达式类型。如上面OrderEJB中finder方法查询语句:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.product_type=‘office_supplies’
FROM子句中声明了IN(o.lineItems)l,则标记变量(indetificationvariable)l对Order直接关联的所有lineItem进行求值。cmr-field域lineItems是抽象模式类型LineItem的实例集合,l是集合中的一个元素,l的类型是LineItem的抽象模式类型。
标记变量(indetificationvariable)用于指定一个EntityBean的抽象模式类型的实例或者EntityBean的抽象模式类型实例集合中的一个元素。
因此,标记变量(indetificationvariable)代表对某个值的引用,并以以下两种方式进行声明:范围(range)变量或者集合成员变量:
范围变量使用EntityBean的抽象模式名进行声明;
集合成员变量使用得到集合结果的路径表达式进行声明。
在FROM子句中,标记变量(indetificationvariable)声明由左到右进行求值。查询语句中后续的标记变量(indetificationvariable)声明可以可以使用前面标记变量(indetificationvariable)声明的结果。
2.5.3.范围变量声明
一个标记变量(indetificationvariable)包括一个EntityBean的抽象模式类型。将标记变量(indetificationvariable)声明为一个范围变量的EJBQL语法与SQL相同:使用可选的AS操作符进行声明。
关联到EntityBean的对象和变量一般使用路径表达式进行定位。但是,定位不可能针对所有的对象。对通过定位不能到达的对象,范围变量声明允许开发者为其指派一个“root”。
因此,开发者可在查询中使用多个范围变量声明对相同抽象模式类型的多个值进行比较。参见select子句。
2.5.4.集合成员变量声明
一个使用collection_member_declaration声明的标记变量(indetificationvariable)包括通过路径表达式定位取得的集合中的所有值。路径表达式表示包含EntityBean抽象模式类型的cmr-field的定位操作。因为一个路径表达式可以基于另一个路径表达式,则定位操作可以使用关联的EntityBean的cmr-field。参见路径表达式。
集合成员变量声明使用特殊操作符IN进行声明。用IN操作符指定的功能表达式接收一个集合值类型的路径表达式参数。路径表达式使用集合类型进行求值,此集合类型是对EntityBean抽象模式类型中一个集合类型的cmr-field的定位操作,所得结果的类型。
例如,OrderEJB中finder方法的查询语句中,FROM子句包含如下标记变量(indetificationvariable)声明子句:
IN(o.lineItems)l
lineItems是一个cmr-field域的名字,其值是LineItemEJB的抽象模式类型LineItem的实例集合。标记变量(indetificationvariable)l是集合中的一个成员,一个单个的LineItem实例。本例中的o是抽象模式类型Order的一个标记变量(indetificationvariable)。
2.5.5.范例
下面的FROM子句包含两个标记变量(indetificationvariable)的声明子句,第一个子句中声明的标记变量(indetificationvariable)被第二个子句使用,此子句声明两个变量o和l。OrderASo是一个范围变量声明,将变量o指定为范围变量,类型为抽象模式类型Order。变量l的类型为抽象模式类型LineItem。因子句从左到右求值,变量l可以使用o的结果进行定位操作。
FROMOrderASo,IN(o.lineItems)l
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的路径表达式类型是关联的EntityBean的抽象模式类型。
一个collection_valued_cmr_field的指定需要使用one-to-many或many-to-many关系中的一个cmr-field的名字。表达式的类型是关联的EntityBean的抽象模式类型。collection_valued_cmr_field的类型是关联的EntityBean的抽象模式类型的值的集合。
对关联EntityBean进行定位操作得到的值,其类型是此关联EntityBean的抽象模式类型。
例如,如l是表示LineItem类型实例的标记变量(indetificationvariable),路径表达式l.product的类型为抽象模式类型Product。
对路径表达式的求值以一个cmr-field终止,将得出一个此cmr-field的Java类型的值。如l.product.name得到的类型是java.lang.String。
一个路径表达式由一个类型为集合的路径表达式构成,在语法上是正确的。例如,如果o的类型是Order,路径表达式o.lineItems.product是正确的,因为对lineItems的定位将得到一个集合。当验证查询语句的时候,这种情况将产生错误。为处理这种定位,在FROM子句中必须声明一个用于包含lineItems集合元素的标记变量(indetificationvariable),必须在查询语句的WHERE子句中,使用另一个路径表达式,才能对每个元素进行定位操作。如下例:
SELECTOBJECT(o)
FROMOrderASo,IN(o.lineItems)l
WHEREl.product.name=‘widget’
在Apusic应用服务器中,如果EntityBean的抽象持久类型中包含辅助值对象(DependentValueObject)时,可使用“.”运算符访问EntityBean内部的辅助值对象的域。如上一章中容器管理持久型的EntityBean中使用的范例,Customer实体中包含了类型为ContactInfo的info域,则可以使用如下EJBQL查询语句查询ContactInfo中name为“JimClark”的实例:
SELECTOBJECT(o)
FROMCustomerASo
WHEREo.info.name=‘JimClark’
2.5.7.WHERE子句与条件表达式
WHERE子句由用于选取对象和值的条件表达式构成。WHERE子句对查询的结果进行限制。
WHERE子句的定义如下:
where_clause::=WHEREconditional_expression
字面值(literals)
字符串的字面值由单引号括起,如:‘literal’。包含单引号的字符串字面值,将使用两个连续的单引号表示一个单引号,例如:‘literal‘‘s’。EJBQL中的字符串字面值如同Java中的字符串字面值一样,使用Unicode字符编码。
一个精确数字字面值是一个不带小数点的数字值,如57、-957、+62。精确数字字面值支持Java中long型的数值范围。精确数字字面值使用Java中的整数语法。
一个近似数字字面值是使用科学计数法表示的数字值,如7E3、-59.7E2,或是带小数点的数字值,如7.、-95.7、+6.2。精确数字字面值支持Java中double型的数值范围。精确数字字面值使用Java中的浮点数语法。
可以使用Java语言规范中规定的后缀指明字面的确切类型。
布尔值的字面值为大小写无关的TRUE和FALSE。
标记变量(indetificationvariable)
在EJBQL查询语句中,所有的用在WHERE子句的标记变量(indetificationvariable)必须在FROM子句中声明,参见标记变量(indetificationvariable)。
在WHERE子句中,标记变量(indetificationvariable)被实际量化,即标记变量(indetificationvariable)代表EntityBean的抽象模式类型的单个实例或实例集合中的成员。标记变量(indetificationvariable)不能被代表一个表示整个集合的值。
如标记变量(indetificationvariable)代表一个空集合中的成员,则标记变量(indetificationvariable)的值为unknown。
路径表达式
在WHERE子句中,除了empty_collection_comparison_expression和collection_member_expression,使用collection_valued_path_expression作为条件表达式的一部分是不合法的。
如果路径表达式由一个代表unknown值的标记变量(indetificationvariable)构成,则路径表达式的值为unknown。
输入参数
以下是关于输入参数的规则。输入参数只能用于查询中的WHERE子句。
输入参数由问号(?)前缀和一个后续整数表示,例如:?1。
输入参数由1开始编号。
EJBQL查询中不同输入参数的编号不能超出finder或select方法的输入参数编号。EJBQL查询中不必使用所有的finder或select方法的参数。
输入参数只能用于comparison_expressions或collection_member_expressions。参见EJBQLBNF。
输入参数使用查询关联的finder或select方法中的对应参数类型进行求值。
如finder或select方法的输入参数是一个EJBObject类型或EJBLocalObject类型,输入参数将会对应到正确的抽象模式类型。
条件表达式构成
一个条件表达式由其他条件表达式、比较操作、逻辑操作、求布尔值的路径表达式和布尔值的字面值构成。
算术表达式可用于比较表达式中。一个算术表达式由其他算术表达式、算术操作、求数值的路径表达式和数值的字面值构成。
算术操作使用Java中的数值提升(numericpromotion)规则。
支持使用括号()对表达式的求值顺序进行排序。
条件表达式定义如下:
conditional_expression::=
conditional_term|conditional_expressionORconditional_term
conditional_term::=
conditional_factor|conditional_termANDconditional_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
运算符与优先级
按照由高到低的优先级,运算符排列如下:
定位运算符(.);
算术运算符:
+,-一元运算符
*,/乘除运算符
+,-加减运算符
比较运算符:=、>、>=、(不等于);
逻辑运算符:NOT、AND、OR
下面描述在特殊表达式中的运算符。
BETWEEN表达式
在条件表达式中,使用比较运算符[NOT]BETWEEN的语法如下:
arithmetic_expression[NOT]BETWEENarithmetic-exprANDarithmetic-expr
例如:
p.ageBETWEEN15AND19
等价于
p.age>=15ANDp.age
如用在一个BETWEEN表达式中的算术表达式的值是NULL,则BETWEEN表达式的值为UNKNOWN。
IN表达式
在条件表达式中使用比较运算符[NOT]IN的语法如下:
single_valued_path_expression[NOT]IN(string-literal[,string-literal]*)
其中,single_valued_path_expression必须是字符串值。
例如:
o.countryIN('UK','US','France')
等价于
(o.country=’UK’)OR(o.country=’US’)OR(o.country=’France’)
另一个例子:
o.countryNOTIN(’UK’,’US’,’France’)
等价于
NOT((o.country=’UK’)OR(o.country=’US’)OR(o.country=’France’))
在IN表达式中定义的字符串字面值列表中,使用逗号分隔,必须包含至少一个字符串字面值。
如用在一个IN表达式中的single_valued_path_expression表达式的值是NULL,则IN表达式的值为UNKNOWN。
LIKE表达式
在条件表达式中,使用比较运算符[NOT]LIKE的语法如下:
single_valued_path_expression[NOT]LIKEpattern-value[ESCAPEescape-character]
其中,single_valued_path_expression必须是字符串值。pattern-value是一个字符串字面值,pattern-value中的下划线(_)表示一个单个的字符、百分号(%)表示字符串序列(包括一个空序列),所有的其他字符表示自身。可选的escape-character是一个单字符的字符串字面值,用于将pattern-value中出现的下划线(_)和百分号(%)进行转义。
例如:
address.phoneLIKE'12%3'
当address.phone代表的值为“123”,“12443”等值时,此表达式为真;当address.phone代表的值为“124”,“1234”等值时,此表达式为假。
asentence.wordLIKE'l_se'
当asentence.word代表的值为“lose”时,此表达式为真;当asendence.word代表的值为“loose”时,此表达式为假。
aword.underscoredLIKE'\_%'ESCAPE'\'
当aword.underscored代表的值为“_foo”时,此表达式为真;当aword.underscored代表的值为“bar”时,此表达式为假;
address.phoneNOTLIKE'12%3'
当address.phone代表的值为“12”,“1234”等值时,此表达式为真;当address.phone代表的值为“123”,“12443”等值时,此表达式为假。
如用single_valued_path_expression表达式的值是NULL,则表达式的值为UNKNOWN。
NULL比较表达式
在条件表达式中,使用比较运算符ISNULL的语法如下:
single_valued_path_expressionIS
[NOT]NULL
NULL比较表达式检查single_valued_path_expression代表的值是否为NULL值。
包含NULL值的路径表达式在求值期间返回NULL值。
空集合比较表达式
在empty_collection_comparison_expression表达式中,使用比较运算符IS[NOT]EMPTY的语法如下:
collection_valued_path_expressionIS[NOT]EMPTY
此表达式检查集合值路径表达式代表的集合是否包含元素。
如果一个集合值路径表达式代表的集合被用在一个空集合比较表达式中,则不能在FROM子句中声明此集合为一个标记变量(indetificationvariable)。如果标记变量(indetificationvariable)被显式声明为一个集合中的元素,则表示存在一个非空的关系,即集合一定包含元素,检查这样的的一个集合是否为空是与前提矛盾的。因此,类似于下例的查询无效:
SELECTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREo.lineItemsISEMPTY
如用在一个空集合比较表达式中的集合值路径表达式的值不包含元素,则空集合比较表达式的值为UNKNOWN。
集合成员表达式
在collection_member_expression(集合成员表达式)中,使用比较运算符ISMEMBEROF的语法如下:
{single_valued_navigation|identification_variable|input_parameter}
[NOT]MEMBER[OF]collection_valued_path_expression
表达式检查指定的值是否是一个集合的成员,此集合由集合值路径表达式指定。
如果在集合成员表达式中的集合值路径表达式代表的集合不包含元素,则此表达式的值为FALSE。
功能表达式
EJBQL包含以下内置的功能表达式。
字符串函数:
CONCAT(String,String),返回一个字符串;
SUBSTRING(String,start,length),返回一个字符串;
LOCATE(String,String[,start]),返回一个字符串;
LENGTH(String),返回一个整型值;
start和length是int型的参数,用于指定字符串中的位置。
算术函数:
ABS(number)
SQRT(double)
2.6.SELECT子句
SELECT子句表示查询的结果。因为finder方法不能返回任意类型的值,定义fnder方法的查询语句中的SELECT子句必须对应finder方法返回的EntityBean的抽象模式类型;对于select方法,可以返回EntityBean的抽象模式类型和cmp-field域的值。
SELECT子句的语法如下:
select_clause::=
SELECT[DISTINCT]{single_valued_path_expression|
OBJECT(identification_variable)}
SELECT子句中所有的标记变量(indetificationvariable)必须使用OBJECT运算符进行限制。SELECT子句中不能使用OBJECT运算符对路径表达式进行限制。
DISTINCT关键字从查询结果中删除重复的值。
如应用查询语句的方法返回值类型是java.util.Collection,如查询语句中未使用DISTINCT关键字,则返回的集合中可包含重复的元素;如方法的返回值类型是java.util.Set,查询语句中未使用DISTINCT关键字,则返回的集合中也不包含重复的元素。
例子:
SELECTl.productFROMOrderASo,IN(o.lineItems)l
注意SELECT子句必须指定返回的是一个单值表达式。下例是无效的查询语句:
SELECTo.lineItemsFROMOrderASo
如希望通过比较多个EntityBean抽象模式类型来选取值,则需要在FROM子句中声明多个抽象模式类型的标记变量(indetificationvariable)。
下面的finder方法的查询语句返回订单数量大于JohnSmith的订单数量的订单。本例中使用了两个不同的标记变量(indetificationvariable),都使用Order抽象模式类型。
SELECTDISTINCTOBJECT(o1)
FROMOrdero1,Ordero2
WHEREo1.quantity>o2.quantityAND
o2.customer.lastname=‘Smith’AND
o2.customer.firstname=‘John’
下例返回订单的所有订单项:
SELECTOBJECT(l)
FROMOrdero,IN(o.lineItems)l
下例返回所有的订单项:
SELECTOBJECT(l)
FROMLineItemsASl
2.7.NULL值
当引用的对象不存在持久存储中,其值被视为NULL。SQL92中的NULL的语义定义了对包含NULL值的条件表达式的求值方式。
下面是这些语义的简短说明:
使用NULL值的算术操作或比较操作总是产生一个UNKNOWN值;
两个NULL值并不相等,两个NULL值的比较产生一个UNKNOWN值;
使用UNKNOWN值的算术操作或比较操作总是产生一个UNKNOWN值;
在求值期间包含NULL值的路径表达式返回NULL值;
ISNULL和ISNOTNULL运算符将一个包含NULL值的cmp-field或单值类型的cmr-field的值转换为TRUE或FALSE;
布尔运算符的三个计算逻辑如下:
AND运算符的定义
ANDTFU
TTFU
FFFF
UUFU
OR运算符的定义
ORTFU
TTTT
FTFU
UTUU
NOT运算符的定义
NOT
TF
FT
UU
注意:
EJBQL中,空字符串‘’,是零长度的字符串,不等于NULL。但当查询被映射到持久存储时,空字符串和NULL值并不总是可以明确分辨的。因此,开发者不应依赖EJBQL中涉及空字符串和NULL值的比较操作。
2.8.相等语义
EJBQL只允许能对其使用like运算符的类型的值进行比较。但这个规则有一个例外,近似数字和精确数字是可以比较的(Java的数值提升提供了必要的类型转换)。
包括上文中提到的NULL值的例外,值的比较必须考察Java语言中的相关语义。例如,使用Java基本(primitivetype)类型定义的cmp-field,不能假定其为NULL值。如此cmp-field域需要使用NULL值,则必须使用对应的引用(referencetype)类型,如使用Integer替代int。持久存储中的存储机制不影响对字符串的比较,如数据填充(padding)的因素。当且仅当字符串包含同样的字符序列时,才可以决定两个字符串相等,这点与标准的SQL不同。
当且仅当同样抽象模式类型的Entity对象的PrimaryKey相等,才可以决定它们是相等的。
2.9.查询语句的限制
日期和时间值应使用Java标准中的long类型的值,使用毫秒为单位。生成毫秒值的标准方法是使用java.util.Carlendar。
尽管SQL支持在算术表达式中,进行定点十进制数的比较。但是EJBQL并不支持。因为EJBQL限制精确数字类型的数字字面值中不可包含小数点(并且使用小数点作为近似类型数字的表示)。
字符串和布尔值的比较只能使用=,和。
在EJBQL查询语句中不能使用注释。
容器管理持久性的数据模型目前不支持继承。因此,不同类型的Entity对象或值不能进行比较。包含此类比较操作的查询语句无效。
3.范例
以下例子用于演示EJBQL的语法与语义。例子基于本节开始时介绍的范例。
3.1.简单查询
查找所有订单:
SELECTOBJECT(o)
FROMOrdero
查找所有需要运到加里福利亚的订单:
SELECTOBJECT(o)
FROMOrdero
WHEREo.shipping_address.state=‘CA’
查找所有订单涉及的州:
SELECTDISTINCTo.shipping_address.state
FROMOrdero
3.2.使用关系的查询
查找有lineitem的订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
查找没有lineitem的订单:
SELECTOBJECT(o)
FROMOrdero
WHEREo.lineItemsISEMPTY
查找未完成订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.shipped=FALSE
查找所有发货地址与收款地址不一样的订单,本例假定发货地址与收款地址都使用EntityBean:
SELECTOBJECT(o)
FROMOrdero
WHERE
NOT(o.shipping_address.state=o.billing_address.stateAND
o.shipping_address.city=o.billing_address.cityAND
o.shipping_address.street=o.billing_address.street)
如果在两个不同的关系中发货地址与收款地址使用相同的EntityBean表示,基于NULL比较表达式中的定义,以上查询可以简化为:
SELECTOBJECT(o)
FROMOrdero
WHEREo.shipping_addresso.billing_address
查询所有包含书名为“EJBdeveplopment”的书的订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.type=‘book’AND
l.product.name=‘ApplyingEnterpriseJavaBeans:
Component-BasedDevelopmentfortheJ2EEPlatform’
3.3.使用输入参数的查询
查询名字由输入参数指定的所有订单:
SELECTDISTINCTOBJECT(o)
FROMOrdero,IN(o.lineItems)l
WHEREl.product.name=?1
对于此查询,参数必须是产品名称的类型,java.lang.String。
3.4.定义select方法的查询
下面查询演示值的选取而非EntityBean的选取:
选取所有已被定购的产品的名称:
SELECTDISTINCTl.product.name
FROMOrdero,IN(o.lineItems)l
以下查询在一个指定订单号码的订单中查找所有包含产品的产品名称。订单号码由一个输入参数指定,并且订单号码是订单的PrimaryKey。
SELECTl.product.name
FROMOrdero,IN(o.lineItems)l
WHEREo.ordernumber=?1
3.5.EJBQL与SQL
EJBQL与SQL相似,将FROM子句作为笛卡尔积,与SQL一样,即使未使用WHERE子句,已声明的标记变量(indetificationvariable)也会影响查询的结果。必须小心地定义标记变量,因为已声明类型的值是否存在,决定了查询的范围。
例如,下面的FROM子句定义了对订单的查询,订单必须包含lineitem和包含已有产品。如持久存储中不存在产品实例,查询的范围为空且不选取订单。
SELECTOBJECT(o)
FROMOrderASo,IN(o.lineItems)l,Productp
4.EJBQLBNF
EJBQL标记概述:
{...}组
[...]可选结构
黑体关键字
EJBQL的完整BNF定义:
EJBQL::=select_clausefrom_clause[where_clause]
from_clause::=FROMidentification_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::=WHEREconditional_expression
conditional_expression::=
conditional_term|conditional_expressionORconditional_term
conditional_term::=
conditional_factor|conditional_termANDconditional_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_expressionANDarithmetic_expression
in_expression::=
single_valued_path_expression[NOT]IN(string_literal
[,string_literal]*)
like_expression::=
single_valued_path_expression[NOT]LIKE
pattern_value[ESCAPEescape-character]
null_comparison_expression::=
single_valued_path_expressionIS[NOT]NULL
empty_collection_comparison_expression::=
collection_valued_path_expressionIS[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{=||>|
entity_bean_value{=|}entity_bean_expression|
arithmetic_valuecomparison_operatorsingle_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)