查询语言de改进是JDO2.0规范中de重要环节,本文从较高de层面阐述JDO2.0所提供de一些新功能.由于JDO2.0规范还未进入公开草案状态,目前还没有任何内容敲定下来,一切都还可能面临变化.不过,JDO2.0将会很快进入最后阶段,而这里提到de查询特性是JDO2.0专家组(译者注:David Jordan就是专家组重要成员)花费时间最多,并且相对来说最为稳定.因此,我有足够理由相信,最终规范与这里de描述将会基本一致.
如果各位读者觉得本文遗漏了某些重要de特性,建议立即到JDO论坛(http://www.jdocentral.com/forums/index.php?showforum=10 )去提出并讨论.这里我需要感谢JDO2.0规范领导人Craig Russell授权给我公开这些JDO2.0查询语言de新特性.
查询结果
我首先从最深入de改进开始介绍.在JDO1.0中,查询结果总是您所指定de类de实例集合.考虑下面deUML类图,它表达了A、B、C、D四个类及之间de关系:

您可以创建一个对A类de查询,通过contains()引用到B类,再通过又一层contains()引用到C类,最后再使用一个“.”操作符引用到D类.但最终返回de集合中只会包含A类de对象实例,如果要从结果中获得其它类,就必须通过A类de引用来逐个获取相关de其它类对象.如果您de查询条件里面包含了B、C或D类de约束,那么在结果集中通过A类对象引用其它类对象时,必须重新将这些约束在Java相关代码中重复一遍,也就是说,您不得不在Java和JDOQL中重复声明限制条件.再者,您可能只关心满足查询条件deD类对象,而不希望中间deB、C类对象被JDO底层创建从而节省内存或相关资源.
在JDO2.0中,您再也不受缚于这些限制了.您可以返回:
数据类(PersistentCapable)de一个或多个字段
候选类以外de其它类对象
统计数据
这意味着您可以返回A、B、C、D类对象,或者它们de某些字段,或者二者de混和结果.您还可以计算类似min或max之类de统计结果.基本上,您想返回什么结果都可以.
当您创建一个查询时,您可以指定一个“结果定义(Result Specification)”来指定返回什么样de内容.它是一个包含一个或多个以逗号分隔de“结果表达式(Result Expression)”.结果表达式可以是:
this关键字,表示返回候选类de对象实例.这与JDO1.0是一样de字段,标明候选类或引用类de某个字段de值,如 address.street.name 字段表达式,代表对多个字段进行JDO预定义de几种算术运算而获得de结果变量,代表查询条件中出现de某个中间变量引用表达式,也就是JDO1.0中de通过“.”操作符进行de对象之间de引用 统计表达式通过对以上这几种结果表达式de组合运用,您可以获得任何您想要de结果.
JDO2.0支持下面de统计函数:
count(表达式),表达式可以是this
sum(数字型字段表达式),“数字型字段表达式”可以是通过字段或字段de运算得到de数字型de结果
min(数字型字段表达式)
max(数字型字段表达式)
avg(数字型字段表达式)
对查询结果de指定是通过下面deAPI:
| void javax.jdo.Query.setResult(String result) |
如果您不调用这个方法,或者参数是null,则返回候选类de对象实例(相当于设置为“this”),即JDO1.0de返回结果.如果您只指定了唯一结果表达式,则返回集合de元素类型与该结果de类型一致.另外,在默认方式下,如果指定了多个结果表达式,则返回de集合元素类型将是 Object[].
您可以在结果定义字符串de开头标上distinct来保证结果不会重复.而如果结果定义串中包含好几个表达式,那么distinct可以保证结果集中不会有重复de数据组.
每个结果表达式可以指定一个名称,对于简单de字段,系统会默认以该字段de名称作为结果中该项de名称.对复杂de表达式,您可以使用下面de语法指定名称:
| result_expression as name |
名称de使用可以让结果中de该项作为结果类中de一个属性来进行设置和使用.您可以指定一个结果类(result class),用来返回查询结果.如果查询结果是一个单值,结果类可以是任何JDO支持de类(Integer, Double, String, BigInteger, BigDecimal, java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp).Query中设置结果类de方法是:
| void setResultClass(Class resultClass) |
如果查询结果包含多个结果表达式,您可以定义一个结果类来保留结果中de各项数据,这个类必须有一个无参数de构造器.此外,每个结果表达式必须对应此类中de一个属性,不论是一个publicde字段,还是一个publicdesetXxx()方法,并且这种直接de或bean风格de属性名称与查询结果中各项结果表达式de名称保持一致.
组操作(Grouping)
统计功能可用于一个分组操作中.JDO2.0提供类似SQL中deGROUP BY和HAVING子句.Query方法:
| void setGrouping(String groupSpec) |
用于指定分组de原则.groupSpec参数包含一个或多个以逗号分隔de分组表达式,还可以跟上一个以“having ”开头de过滤条件.调用此方法后,setResult()参数de每个结果表达式项必须是groupSpec中de一项,或者是groupSpec中de一项或多项de运算结果.所有groupSpec项de值均相同de结果被归在同一组中(同一条结果记录).having子句de过滤条件可以包含boolean结果de判断语句或者是对分组表达式de统计运算.与SQL一样,having子句用于对分组后de结果集进行条件过滤.
唯一性(Uniqueness) 很多人一直奇怪为什么Queryde执行(execute)结果是一个Object类型de对象,因为这样给开发人员造成必须手工强制将结果转换成Collectionde不便.JDO1.0中查询结果一般有多个元素,但JDO专家组计划在JDO2.0中加入对返回单值结果de查询de支持,于是将Queryde执行结果定义为Object类型.
您有时会执行一个您确定结果只会有一条de查询(比如统计总数或者按具有唯一索引de会员帐号查找对象等等),在JDO2.0中,您可以调用Query方法来声明:
| void setUnique(boolean unique) |
当您传入“true”后,Query执行de结果将是一个单独de值对象,如果无任何结果返回,则结果是null.如果JDO发现查询结果返回了多条记录,则会扔出一个异常.
限制返回结果de大小 设计用户界面de时候,我常常会显示结果集de某一部分子集(比如分页显示或只显示前十条之类).为了性能和效率,您多半会需要限制返回结果de范围.Queryde方法可以完成这一点:
| void setRange(int fromInclusive, int toExclusive) |
该方法返回de结果集只包含原结果集de第fromInclusive条到第toExclusive-1条.
新de过滤条件操作符 一些新de操作符被加到JDOQL中,以便执行针对引用、Map、字符串和数字de操作.instanceof操作符返回一个boolean值,可以让您过滤某个指定类de对象;同样返回boolean值decontainsKey(Object)函数和containsValue(Object)用于访问Map元素.
字符串处理方面加入了很多函数,toLowerCase()和toUpperCase()分别完成大小写转换,另外还有下列函数用于查找子串位置和获得子串:
int indexOf(String) int indexOf(String, int) String substring(int) String substring(int,int) |
另外,Stringde方法:
| boolean matches(String pattern) |
用于执行
正则式匹配.目前只能提供有限de匹配功能.“.”和“.*”可表示通配符,而“(?i)”表示匹配不区分大小写.
对数字型de字段,JDO2.0增加了两个函数:
| Math.abs(numeric) Math.sqrt(numeric) |
预定义查询(Named Queries) 您可以在JDO描述符(metadata)中声明常用deJDOQL查询语句,这样就不用将查询嵌入到Java源相关代码中.这样可以提供一些灵活性,比如将查询语句写到一个配置好de文本文件中,而需要修改时可以直接改该文件,而不用更改Java源码.描述符中de每个查询都有一个名字,而要执行某个查询时,可以使用下面de方法来创建查询:
| Query newNamedQuery(Class cls, String queryName) |
JDO将会搜索描述符来找到对应de预定义查询语句并生成相应deQuery对象.
访问静态字段(static fields) 您将可以在JDOQL中访问数据类中以public static final方式声明de常量.比如
public static final int FEMALE = 0; public static final int MALE = 1; public static final int UNKNOWN = 2; |
而在查询中使用类似“salary > 5000.0 && gender == MALE”de过滤条件.
批量删除(Deletion by Query) 在JDO1.0中,要删除一个对象,必须先将其载入内存,再删除,然而很多情况下,您在删除之前并不需要访问该对象,这样de做法比较低效.在JDO2.0中,提供了Queryde几个方法来删除符合查询条件de一组对象:
| Object deletePersistentAll(Object[] parameters) Object deletePersistentAll(Map parameters) Object deletePersistentAll() |
此查询结果de对象会被全部从数据库删除.这些方法返回被删除对象de集合.您de程序可以决定是否需要对被删除de对象逐个访问.如果您不访问这些对象,Query执行de性能将不会受到任何影响,换句话说,这些对象将不会在内存中生成.
厂商扩展de查询特性
JDO厂商可以给JDOQL提供各种各样de特殊查询功能.每个厂商可以定义一组属于该厂商自己de扩展功能.如果您需要使用其中de功能,您需要将该厂商de扩展加到程序运行环境中.每个扩展有一个名字和一个可选de值.您可以单独设置每个扩展,或者一次性设置多个扩展.Queryde下列方法用于设置厂商扩展特性:
| void setExtensions(Map extensions) void addExtension(String key, Object value) |
SQL直接访问 如果JDO2.0de查询功能不能满足需要,并且以下条件都满足:
您de应用运行在关系数据库上
您需要de查询有SQL语句可以实现
您知道从您de类模型到数据库de映射细节
您就可以创建一个SQL查询,方法是调用PersistentMangerde方法:
| Query newQuery(String language, Object query) |
每一个参数需要设置为“javax.jdo.query.SQL”,而query是一个具体deSQL语句.如果您de查询需要返回类de实例,则这个SQL语句必须返回该类de相应主键字段.使用直接deSQL时,JDOQL提供de函数将不能被使用,否则将会扔出异常JDOUserException,比如,您不能对该Query设置过滤条件字符串、排序声明或变量声明.使用SQL查询时,参数都是未指定类型de,在SQL语句中以“?”表示,并且按出现de顺序被绑定.
结束语 各位读者已经看到,JDO2.0对查询语句增加了很多新de功能和特性,我认为其中de查询结果定义是最大de改进.正如我在文章开头说de,这一部分变化目前来说是JDO2.0中最稳定de部分,不会有大de变化.厂商可以开始提供很多这样de功能,不用考虑专家组还在讨论deJDO2.0其余部分将如何变化.
在JDO2.0提供标准de对象/关系映射de前提下,这里描述de查询功能和一个用于多层结构deJDO应用de脱钩/挂钩(detach/attach)机制,将使JDO占领更多de市场.对这一点有深切体会de厂商将不遗余力地争取率先推出稳定、完整、高效deJDO2.0产品.JDO将是基于数据库存储和管理de应用开发de首先API,开发人员们将享受面向对象de模型设计和高效de数据存储管理系统.