标识变量
一个标识变量就是在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的抽象模式:
一个范围变量声明可以包括可选的AS操作符:
在绝大多数情况下,为了获得对象,查询会使用路径表达式以跨越关联关系。但是对于那些不能通过跨越获得的对象,你可以使用一个范围变量声明以指定一个起点(或root)。
如果查询对同一抽象模式名的多个值进行比较,那么FROM子句需要为这个抽象模式声明多个标识变量:
FROM Player p1, Player p2
|
集合成员声明
在一个一对多的关联关系中,多端由一个entity
bean的集合组成。一个标识变量可以反映这个集合中的一个成员。要访问一个集合成员,变量声明中的路径表达式将跨越抽象模式中的关联关系。(在下面我们将详细介绍路径表达式。)因为一个路径表达式可以基于另一个路径表达式,所以可以实现对几个关联关系的跨越。
一个集合成员声明必须包含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
|
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子句指定了一个通配符模型中的一个转义字符。
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、布尔操作
等同性规定
在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不能进行比较。
|