EJB QL大全(zz)- -
Enterprise JavaBeans查询语言(EJB QL)定义了CMP的finder方法和select方法的查询。作为SQL92的一个子集,EJB QL进行了扩展,它可以使用entity bean的抽象模式中定义的关联。一个EJB QL查询可以跨越封装在同一个EJB JAR文件中的所有相关的entity bean的抽象模式。
你可以在entity bean中部署描述中定义EJB QL查询。典型的情况是,一个工具会将这些查询转换成低层数据存储所使用的查询语言。因为这种转换的存在,使用容器管理持续化的entity bean具有可移植性--它们的代码不受具体的数据存储的类型的影响。
要学习这一章,你必须掌握前几章的内容。有关概念,请参阅容器管理持续化。第六章有示例的源代码。
术语
下面列出了本章中用到的一些术语的定义。
1、Abstract schema--抽象模式:一个entity bean的部署描述的一部分,它定义了bean的持续化字段和关联关系。
2、Abstract schema name--抽象模式名:在EJB QL查询中引用的一个逻辑命名。你需要为每个容器管理持续化的entity bean指定一个抽象模式名。
3、Abstract schema type--抽象模式类型:所有的EJB QL表达式都对应一个类型。如果表达式是一个抽象模式名,那么它的类型默认是定义了这个抽象模式名的entity bean的local接口。
4、Backus-Naur Form (BNF)--巴科斯-诺尔范式:一种描述高级语言语法的一种表示法。这一章的语法图表使用了BNF表示法。
5、navigation--跨越:在一个EJB QL表达式中对关联关系的使用。跨越操作符是一个句点。
6、path expression--路径表达式:一个跨越到相关entity bean的表达式。
7、persistent field--持续化字段:一个容器管理持续化的entity bean中的一个虚拟字段;它存储在一个数据库中。
8、relationship field--关联字段:一个容器管理持续化的entity bean中的一个虚拟字段;它确定了一个相关的entity bean。
简单的语法
这里简要地描述了EJB QL的语法,这样你可以快速进入下面查询示例的学习。如果你要对此进行更深入的学习,请阅读完整的语法。
一个EJB QL查询由三个子句组成:SELECT子句、FROM子句和WHERE子句,SELECT子句和FROM子句是必选的,而WHERE子句是可选的。下面是一个EJB QL查询的简单BNF定义:
EJB QL :: = select_clause from_clause [where_clause]
SELECT子句定义了对象的类型或查询的返回值。返回类型可是一个local接口、一个remote接口或是一个持续化字段。
FROM子句通过声明一个或多个标识变量定义了查询的范围,这些标识变量可能会在SELECT子句和WHERE子句中被引用。一个标识变量反映了下列元素之一:
1、一个entity bean的抽象模式名
2、一个集合的一个成员,这个集合是一个一对多关系中的多端
WHERE子句是一个限制查询找到的对象或数值的条件表达式。尽管它是可选的,但是绝大多数查询都会有一个WHERE子句。
查询示例
下面的查询来自于RosterApp J2EE应用程序的PlayerEJB entity bean,在第六章中我们对这个应用程序作了介绍。RosterApp中bean之间的关联关系,请参看图6-1。
简单的Finder查询
如果你对EJB QL不熟悉,这些简单的查询将有助于你尽快入门。
示例 1
SELECT OBJECT(p)
FROM Player p
获得的数据:所有队员。
Finder方法: findall()
说明:FROM子句声明了一个名为p的标识变量,其中省略了可选的AS关键字。如果使用了AS关键字,这个子句将是这样的:
FROM Player AS p
Player元素是PlayerEJB entity bean的抽象模式名。因为bean在LocalPlayerHome接口中定义了findall方法,所以这个查询返回的对象具有LocalPlayer类型。
参见: 标识变量
示例 2
SELECT DISTINCT OBJECT(p)
FROM Player p
WHERE p.position = ?1
获得的数据:由finder方法的参数指定位置的队员。
Finder方法: findByPosition(String position)
说明:在一个SELECT子句中,在例如p这样的独立的标识变量前面要加OBJECT关键字。DISTINCT关键字排除了重复的数据。
WHERE子句通过检查队员的position这个PlayerEJB entity bean的持续化字段对获得的队员进行了限制。?1元素表示了了findByPosition方法中的输入参数。
参见: 输入参数, DISTINCT关键字和OBJECT关键字
示例 3
SELECT DISTINCT OBJECT(p)
FROM Player p
WHERE p.position = ?1 AND p.name = ?2
获得的数据:指定位置和姓名的队员。
Finder方法: findByPositionAndName(String position, String name)
说明:position元素和name元素是PlayerEJB entity bean的持续化字段。WHERE子句将这些字段与findByPositionAndName方法的输入参数进行比较。EJB QL使用一个后面带有整数的问号表示输入参数。第一个输入参数中?1,第二个是?2,依此类推。
跨越相关Bean的Finder查询
在EJB QL中,一个表达式可以跨越相关的bean。这些表达式的存在中EJB QL和SQL的主要不同点。EJB QL跨越到相关的bean,而SQL是使用表的连接。
示例 4
SELECT DISTINCT OBJECT(p)
FROM Player p, IN (p.teams) AS t
WHERE t.city = ?1
获得的数据:属于指定城市的运动队的队员。
Finder方法: findByCity(String city)
说明:FROM子句声明了两个标识变量:p和t。p变量表示PlayerEJB entity bean,而t变量表示相关的TeamEJB bean。在t的声明中引用了之前声明的p变量。IN关键记号表示teams是一个相关bean的集合。p.teams表示式实现了从PlayerEJB bean到它的相关TeamEJB bean的跨越。p.teams表达式中的句点是跨越操作符。
在WHERE子句中,标识变量city之前的句点是一个分隔符,而不是一个跨越操作符。严格说来,表达式可以跨越到关联字段(相关的bean),而不是持续化字段。要访问一个持续化字段,一个表达式必须使用句点作为分隔符。
表达式不可以对作为集合的关联字段作更进一步的跨越。在一个表达式的语法中,collection-value字段是一个终结符。因为teams字段是一个集合,所以WHERE子句不可以指定 p.teams.city--这是一个非法的表达式。
参见: 路径表达式
示例 5
SELECT DISTINCT OBJECT(p)
FROM Player p, IN (p.teams) AS t
WHERE t.league = ?1
获得的数据:属于指定运动联盟的队员。
Finder方法: findByLeague(LocalLeague league)
说明:这个查询中的表达式实现了两个关联关系的跨越。p.teams表达式跨越了PlayerEJB-TeamEJB关联关系,而t.league表达式跨越了TeamEJB-LeagueEJB关系。
在其它示例中,输入参数是String对象,但是在这个示例中参数是一个类型为一个LocalLeague接口的对象。这个类型与WHERE的比较表达式中的league关联字段匹配。
示例 6
SELECT DISTINCT OBJECT(p)
FROM Player p, IN (p.teams) AS t
WHERE t.league.sport = ?1
获得的数据:参与指定运动的队员。
Finder方法: findBySport(String sport)
说明:sport持续化字段属于LeagueEJB bean。要访问sport字段,查询必须首先实现从PlayerEJB bean到TeamEJB bean的跨越(p.teams),然后实现从TeamEJB bean到LeagueEJB bean的跨越(t.league)。因为league关联字段不是一个集合,在它后面可以跟随sport持续化字段。
使用其它条件表达式的Finder查询
每一个WHERE子句都必须指定一个条件表达式,这样的条件表达式可以有几种。在之前的示例中,条件表达式是检测是否相等的比较表达式。在下面的例子中,使用了一些其它种类的条件表达式。对于所有条件表达式的介绍,请参看WHERE子句。
示例 7
SELECT OBJECT(p)
FROM Player p
WHERE p.teams IS EMPTY
获得的数据:所有不属于任何运动队的队员。
Finder方法: findNotOnTeam()
说明:PlayerEJB bean的teams关联字段是一个集合。如果一个队员不属于任何运动队,那么teams集合为空,条件表达式的结果将为TRUE。
参见: 空集合比较表达式
示例 8
SELECT DISTINCT OBJECT(p)
FROM Player p
WHERE p.salary BETWEEN ?1 AND ?2
获得的数据:薪水位于指定范围的队员。
Finder方法: findBySalaryRange(double low, double high)
说明:BETWEEN表达式是一个三元表达式:一个持续化字段(p.salary)和两个输入参数(?1和?2)。下面的表达式与之前的BETWEEN表达式等价:
p.salary >= ?1 AND p.salary <= ?2
参见: BETWEEN表达式
示例 9
SELECT DISTINCT OBJECT(p1)
FROM Player p1, Player p2
WHERE p1.salary > p2.salary AND p2.name = ?1
获得的数据:所有薪水高于指定姓名的队员薪水的队员。
Finder方法: findByHigherSalary(String name)
说明:FROM子句声明了两个标识变量(p1和p2),这两个标识变量具有同一类型(Player)。之所以在这里需要两个标识变量是因为以WHERE子句中将一个队员(p2)的薪水与另一个队员(p1)进行了比较。
参见: 标识变量
Select查询
这一部分的查询是select方法所使用的。与finder方法不同,一个select方法可以返回持续化字段或其它entity bean。
示例 10
SELECT DISTINCT t.league
FROM Player p, IN (p.teams) AS t
WHERE p = ?1
获得的数据:指定队员所属的联盟。
Select方法:ejbSelectLeagues(LocalPlayer player)
说明:这个查询的返回类型是LeagueEJB entity bean的抽象模式类型。这个抽象模式类型映射到LocalLeagueHome接口。因为表达式t.league不是一个独立的标识变量,OBJECT关键字被省略了。
参见: SELECT子句
示例 11
SELECT DISTINCT t.league.sport
FROM Player p, IN (p.teams) AS t
WHERE p = ?1
获得的数据:指定队员所参与的运动。
Select方法: ejbSelectSports(LocalPlayer player)
说明:这个查询返回了一个名为sport的String named sport,这是LeagueEJB entity bean的一个持续化字段。
完整的语法
在这一部分我们讨论Enterprise JavaBean规范中定义的EJB QL语法。下面的素材中的大多数直接取自Enterprise JavaBean规范或是对规范的解释。
EJB QL的BNF语法
这里是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)
BNF符号
表8-1介绍了在这一章中使用的BNF符号。
表8-1 BNF 符号概览
符号:描述
::=:该符号左边的元素被该符号右边的结构所定义
*:该符号前面的结构可以重复零次或多次
{...}:在这个花括号中的结构一起组合成一组
[...]:方括号中的结构是可选的
|:一个具有排它性的OR
黑体字:一个关键字(尽管在这个BNF定义中使用了大写,但是事实上关键字并不是大小写敏感的)
空白区域:一个空白区域可以是一个空格、横表符或是换页符
FROM子句
FROM子句通过声明标识变量定义了查询的范围。这里是FROM子句的语法:
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
标识符
一个标识符是由一个或多个字符组成的序列。在Java编程语言(以下简称"Java")中的标识符的首字符必须是一个有效的首字符(字母、$和_)。在一外Java标识符中的每一个子序列字符都必须是一个有效的非首字符(字母、数字、$和_)。(详细情况请参阅Character类的isJavaIdentifierStart方法和isJavaIdentifierPart方法的J2SE API文档。在EJB QL中问号(?)是一个保留字符,它不可以在一个标识符中使用。与一个Java变量不同,一个EJB QL标识符是对大小写不敏感的。
一个标识符不能与EJB QL关键字发生冲突:
AND
AS
BETWEEN
DISTINCT
EMPTY
FALSE
FROM
IN
IS
LIKE
MEMBER
NOT
NULL
OBJECT
OF
OR
SELECT
TRUE
UNKNOWN
WHERE
EJB QL的关键字同时也是SQL的保留字。在将来,EJB QL关键字将扩充到包含其它SQL保留字。Enterprise JavaBeans规范推荐你不要在EJB QL标识符中使用其它SQL保留字。
标识变量
一个标识变量就是在FROM子句中声明的一个标识符。尽管SELECT子句和WHERE子句中可以引用标识变量,但是不可以声明它。所有的标识变量都只能在FROM子句中声明。
因为一个标识变量就是一个标识符,所以它与标识符具有同样的命名约定和约束。例如,一个标识变量是对大小写不敏感的,它不可以与一个EJB QL关键字相同。(更多的命名规则请参见上一段。)同样,在一个给定的EJB JAR文件中,一个标识命名也不能与任何entity bean的命名或抽象模式名重复。
FROM子句可以包含用逗号隔开的多个声明。一个声明可以引用另一个在前面(左边)已经声明的标识变量。在下面的FROM子句中,变量t引用了之前声明的变量p:
FROM Player p, IN (p.teams) AS t
即使一个标识变量没有在WHERE子句中引用,它的声明也可以影响到查询的结果。我们可以来比较下面的两个查询。下面的查询返回所有的队员,不论他们是否属于一个运动队:
SELECT OBJECT(p)
FROM Player p
与此相反,因为下一个查询声明了t标识变量,它可以获得属于一个运动队的所有队员:
SELECT OBJECT(p)
FROM Player p, IN (p.teams) AS t
下面的查询返回了与上一个查询相同的结果,但是WHERE子句的存在使得这个查询更容易理解:
SELECT OBJECT(p)
FROM Player p
WHERE p.teams IS NOT EMPTY
一个标识变量总是指向一个单独的值,它的类型是声明中使用的表达式。有两个类型的声明:范围变量和集合成员。
范围变量声明
要将一个标识变量声明为一个抽象模式类型,你必须指定一个范围变量申明。换句话说,一个标识变量可以包括一个entity bean的抽象模式类型。在下面的例子中,一个名为p的标识变量表示了一个名为Player的抽象模式:
FROM Player p
一个范围变量声明可以包括可选的AS操作符:
FROM Player AS p
在绝大多数情况下,为了获得对象,查询会使用路径表达式以跨越关联关系。但是对于那些不能通过跨越获得的对象,你可以使用一个范围变量声明以指定一个起点(或root)。
如果查询对同一抽象模式名的多个值进行比较,那么FROM子句需要为这个抽象模式声明多个标识变量:
FROM Player p1, Player p2
作为这样一个查询的示例,请参看示例 9.
集合成员声明
在一个一对多的关联关系中,多端由一个entity bean的集合组成。一个标识变量可以反映这个集合中的一个成员。要访问一个集合成员,变量声明中的路径表达式将跨越抽象模式中的关联关系。(在下面我们将详细介绍路径表达式。)因为一个路径表达式可以基于另一个路径表达式,所以可以实现对几个关联关系的跨越。参见示例 6。
一个集合成员声明必须包含IN操作符,不过它可以省略可选的AS操作符。
在下面的例子中,抽象模式名为Player的entity bean有一个名为teams的关联字段。名为t的标识变量代表了teams集合中的一个成员。
FROM Player p, IN (p.teams) AS t
路径表达式
从几个方面说,路径表达式都是EJB QL语法中的重要的构造。首先,它们定义了在抽象模式的关联关系中的跨越路径。这些路径的定义既影响查询的范围,也影响查询的结果。其次,它们可以在一个EJB QL查询的三个子句(SELECT、WHERE和FROM)的任何一个中出现。最后,尽管EJB QL中的很大部分都是SQL的子集,但是路径表达式却不是SQL中所具有的。
语法
有两种类型的路径表达式:single-valued和collection-valued。下面是路径表达式的语法:
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
在前面的语法结构中,cmp_field元素表示了一个持续化字段,而cmr_field元素表示了一个关联字段。术语single_valued限制关联字段只能是一对一关系或一对多关系中"一"端;反之,术语collection_valued限制关联字段为多端。
路径表达式中的句点(.)有两个功能。如果句点出现在一个持续化字段之前,它是字段和标识变量之间的分隔符。而如果句点是出现在一个关联字段之前,那么它是跨越操作符。
示例
在下面的查询中,WHERE子句包含了一了single-valued表达式。p是一个标识变量,而salary是Player的一个持续化字段。
SELECT DISTINCT OBJECT(p)
FROM Player p
WHERE p.salary BETWEEN ?1 AND ?2
下一个例子的WHERE子句也包含了一个single-valued表达式。t是一个标识变量,league是一个single-valued关联字段,而sport是league的一个持续化字段。
SELECT DISTINCT OBJECT(p)
FROM Player p, IN (p.teams) AS t
WHERE t.league.sport = ?1
在下面的查询中,WHERE子句包含了一个collection-valued表达式。p是一个标识变量,而teams是一个collection-valued关联字段。
SELECT DISTINCT OBJECT(p)
FROM Player p
WHERE p.teams IS EMPTY
表达式类型
一个表达式的类型就是最后的元素所对应的对象的类型,它可以有以下几种:
1、持续化字段
2、Single-valued关联字段
3、Collection-valued关联字段
例如,表达式p.salary的类型是double,这是因为最后的持续化字段(salary)的类型为double。
在表达式p.teams中,最后的元素是一个collection-valued关联字段(teams)。那么这个表达式的值就是一个名为Team的抽象模式类型的集合。因为Team是TeamEJB entity bean的抽象模式命名,所以这个类型映射到bean的local接口LocalTeam。有关抽象模式映射的类型,参见返回类型。
跨越
路径表达式的存在使得查询可以跨越到相关的entity bean。一个表达式中最后的元素决定了是否可以进行遗址。如果一个表达式包含了一个single-valued关联字段,那么可以继续跨越到与该字段相关的对象。然而,一个表达式不能作超过一个持续化字段或是一个collection-valued关联字段的跨越。例如,表达式p.teams.league.sport就是非法的,因为teams是一个collection-valued关联字段。要访问sport字段,FROM子句必须为teams字段定义一个名为t的标识变量:
FROM Player AS p, IN (p.teams) t
WHERE t.league.sport = 'soccer'
WHERE子句
WHERE子句指定了一个限制查询返回值的条件表达式。查询返回所有数据存储中使得条件表达式为TRUE的值。尽管经常使用,但是事实上WHERE子句是可选的。如果WHERE子句被省略,那么查询会返回所有的值。WHERE子句的高级语法如下:
Where_clause ::= WHERE conditional_expression
常量
有三种类型的常量:字符串型、数值型和布尔型。
字符串型常数
一个字符串型常量用一组单引号引出:
'Duke'
如果一个字符串本身就包含一个单引号,你可以通过使用两个单引号来表示它:
'Duke''s'
和一个Java String一样,一个EJB QL中的字符串常量使用Unicode字符编码。
数值型常量
有两种类型的数值型常量:精确型和近似型。
一个精确型数值常量是一个没有小数点的数字,例如65、-233和+12。使用Java整数的语法,精确型数值常量可以支持Java long范围内的数字。
一个近似型数值常量是一个使用科学记数法的数字,例如57.、-85.7和+2.1。使用Java浮点常量的语法,近似型数值常量可以支持Java double范围内的数字。
布尔型常量
一个布尔型常量是TRUE或FALSE。这些关键字是对大小写不敏感的。
输入参数
一个输入参数标志为问号(?)后加整数。例如,第一个输入参数是?1,第二个是?2,依此类推。
输入参数必须遵守以下规则:
1、它们只可以应用于一个WHERE子句中。
2、它们的使用只限于一个条件表达式中的single-valued路径表达式。
3、它们必须是有限的,并以整数1开始。
4、WHERE子句中输入参数的数目不可以超过相应的finder方法或select方法中的输入参数的数目。
5、WHERE子句中输入参数的类型必须与相应的finder方法和select方法的参数匹配。
条件表达式
一个WHERE子句由一个条件表达式组成,它的优先级顺序为从左至右。你可以通过使用圆括号改变这个顺序。
下面是条件表达式的语法:
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
操作符和它们的优先级
表8-2按照优先级递减的顺序列出了EJB QL的操作符。
表8-2 EJB QL操作符优先级
类型:优先级
跨越:. (一个句点)
算术:+ - (一元的)
* / (乘和除)
+ - (加和减)
比较:=
>
>=
<
<=
<> (不等于)
逻辑:NOT
AND
OR
BETWEEN表达式
一个BETWEEN表达式确定一个算术表达式是否属于某一数值范围。BETWEEN表达式的语法如下:
between_expression ::=
arithmetic_expression [NOT] BETWEEN
arithmetic_expression AND arithmetic_expression
这两个表达式是等价的:
p.age BETWEEN 15 AND 19
p.age >= 15 AND p.age <= 19
这两个表达式也是等价的:
p.age NOT BETWEEN 15 AND 19
p.age < 15 OR p.age > 19
如果一个算术表达式有一个NULL值,那么BETWEEN表达式的值未知。
IN表达式
一个IN表达式确定一个字符串是否属于一组字符串常量。下面是IN表达式的语法:
in_expression ::=
single_valued_path_expression
[NOT] IN (string_literal [, string_literal]* )
上述的single-valued path expression必须有一个String值。如果该single-valued path expression有一个NULL值,那么IN表达式的值未知。
在下面的例子中,如果国家是UK,那么表达式是TRUE。如果国家是Peru,则表达式是FALSE。
o.country IN ('UK', 'US', 'France')
LIKE表达式
一个LIKE表达式确定一个通配符模型是否匹配一个字符串。下面是语法:
like_expression ::=
single_valued_path_expression
[NOT] LIKE pattern_value [ESCAPE escape-character]
上述的single-valued path expression必须有一个String值。如果这个值是NULL,那么LIKE表达式的值未知。通配符模型是一个可以包含通配符的字符串常量。下划线(_)表示任何一个单独的字符。百分号(%) 表示零个或多个字符。ESCAPE子句指定了一个通配符模型中的一个转义字符。
表8-3中给出了一些简单的LIKE表达式。TRUE列和FALSE列表明了对于一个single-valued路径表达式LIKE表达式的值。
表8-3 LIKE表达式示例
表达式:TRUE:FALSE
address.phone LIKE '12%3':'123','12993':'1234'
asentence.word LIKE 'l_se':'lose':'loose'
aword.underscored LIKE '\_%' ESCAPE '':'_foo':'bar'
address.phone NOT LIKE '12%3':1234:'123','12993'
NULL比较表达式
一个NULL比较表达式检验一个single-valued路径表达式是否有一个NULL值。通常,这个表达式用来检验一个single-valued关联关系是否被设定。如果一个路径在运算过程中包含了一个NULL值,那么它将返回NULL值。下面是一个NULL比较表达式的语法:
null_comparison_expression ::=
single_valued_path_expression IS [NOT] NULL
空集比较表达式
一个空集比较表达式检验一个collection-valued路径表达式是否包含元素。换句话说,它检验一个collection-valued关联关系是否被建立。下面是语法:
empty_collection_comparison_expression ::= collection_valued_path_expression IS
[NOT] EMPTY
如果collection-valued路径表达式是NULL,那么空集比较表达式的值也为NULL。
集合成员表达式
集合成员表达式确定一个值是否是集合中的成员。这个值必须与集合成员是同一类型。表达式的语法如下:
collection_member_expression ::=
{single_valued_navigation | identification_variable |
input_parameter}
[NOT] MEMBER [OF] collection_valued_path_expression
如果collection-valued路径表达式是未知的,那么集合成员表达式的结果也是未知的。如果collection-valued路径对应一个空集,那么集合成员表达式的结果是FALSE。
函数表达式
EJB QL包含几个字符串和算术函数,在下面的表中列出了这几个函数。在表8-4中,start和length参数的类型是int。它们对应了String参数中的位置。在表8-5中,number参数可以是int型、float型或是double型。
表8-4 字符串表达式
函数语法:返回值类型
CONCAT(String, String):String
SUBSTRING(String, start, length):String
LOCATE(String, String [, start]):int
LENGTH(String):int
表8-5 算术表达式
函数语法:返回值类型
ABS(number):int, float, or double
SQRT(double):double
NULL值
如果索引所指向的在持续化存储中无法找到,那么这就是NULL。对于包含NULL的条件表达式,EJB QL沿用SQL92中的规定。简单地说,有如下规定:
1、如果一个比较或算术操作包含一个未知值,它产生一个NULL值。
2、如果一个路径表达式包含一个NULL值,它返回一个NULL值。
3、在进行IS NULL检验时,NULL持续化字段或NULLsingle-valued关联字段的结果为TRUE。反之,在进行IS NOT NULL检验时结果为FALSE。
4、布尔操作或条件测试使用下表中定义的三值逻辑。(在这些表中,T代表TRUE,F代表FALSE,U代表未知(译者注:也就是NULL)。)
表8-6 逻辑与
AND:T:F:U
T :T:F:U
F :F:F:F
U :U:F:U
表8-7 逻辑或
OR:T:F:U
T :T:T:T
F :T:F:U
U :T:U:U
表8-8 逻辑反
NOT
T:F
F:T
U:U
表8-9 条件测试
条件测试:T:F:U
表达式IS TRUE:T:F:F
表达式IS FALSE:F:T:F
表达式未知:F:F:T
等同性规定
在EJB QL中,只有同一类型的值可以进行比较。但是,这个规则有一个例外:精确型数值与近似型数值之间可以进行比较。在这样的比较中,所需的类型转换遵守Java的有关规定。
EJB QL对数值进行比较时,将它们视为Java类型,而不是它们在底层数据存储中的持续化类型。例如,如果一个持续化字段既可以是整型也可以是NULL,那么它必须定义为一个Integer对象,而不是一个int简单变量。只是因为只有Java对象才能为NULL,而一个简单变量不可以。
两个字符串只有在它们包含完全相等的字符序列时才被视为相等。这时你注意空格的存在变得很重要;例如,字符串'abc'和'abc '就是不相等的。
两个具有相同抽象模式类型的entity bean只有在它们的主键的值相同时才被视为相等。
SELECT子句
SELECT子句定义了查询返回的对象或值的类型。SELECT子句的语法如下:
select_clause ::= SELECT [DISTINCT]
{single_valued_path_expression |
OBJECT(identification_variable)}
返回类型
SELECT子句定义的返回类型必须与这个查询为之定义的finder方法或select方法匹配。
对于finder方法查询,SELECT子句的返回类型是定义这个finder方法的entity bean的抽象模式类型。这个抽象模式类型映射到一个remote接口或local接口。如果是bean的remote home接口定义了这个finder方法,返回类型就是remote接口(或remote接口的结果集)。同样的,如果local home接口定义了这个finder方法,返回类型就是local接口(或local接口的结果集)。例如,PlayerEJB entity bean的LocalPlayerHome接口定义了findall方法:
public Collection findAll() throws FinderException;
findall方法的EJB QL查询返回LocalPlayer接口类型的结果集:
SELECT OBJECT(p)
FROM Player p
对于select方法查询,SELECT子句的返回类型可以是以下情况中的一种:
1、包含这个select方法entity bean的抽象模式。
2、相关entity bean的抽象模式。(默认的情况是每一个这样的抽象模式类型映射到entity bean的local接口,尽管不常见,但是在这种情况下,你可能需要通过在部署描述中指定一个remote接口以重载这个默认的映射。)
3、一个持续化字段
例如,PlayerEJB entity bean实现的ejbSelectSports方法返回一个相应于sport的String对象的结果集。sport就是LeagueEJB entity bean的一个持续化字段。参见示例 11.
一个SELECT子句不能指定一个collection-valued表达式。例如,在SELECT子句中,p.teams就是非法的,这是因为teams是一个集合但是下面查询中的SELECT子句是有效的,这是因为t是teams集合中的单个元素:
SELECT t
FROM Player p, IN (p.teams) AS t
DISTINCT关键字和OBJECT关键字
DISTINCT关键字用来去除返回值中的重复值。如果这个查询的方法返回一个java.util.Collection--它允许重复--那么你必须指定DISTINCT关键字以去除重复值。但是,如果方法返回一个java.util.Set,DISTINCT关键字就是多余的,这是因为java.util.Set不可以包含重复值。
在一个单独的标识变量前必须使用OBJECT关键字,但是在一个single-valued路么表达式前不需要使用它。如果一个标识变量是一个single-valued路径表达式的一部分,那么它不被认为是单独的。
EJB QL约束
EJB QL有以下约束:
1、不允许用注释。
2、日期和时间值精确到毫秒级并使用Java long类型。一个日期和时间常量必须是一个整型常量。要生成毫秒值,你可以使用java.util.Calendar类。
3、通常情况下。容器管理持续化不支持继承。因为这个原因,两个不同类型的entity bean不能进行比较。