EJB3 持久性规范要求实现者支持 EJB-QL(EJB 查询语言)中的 批量操作
。作为 Hibernate 实现 EJB3 持久性的一部分,HQL(Hibernate 查询语言:它是 EJB-QL 的超集)需要支持这些 批量操作
。现在,这种支持已经完成编码,甚至超出了 EJB3 持久性规范提供的范围。对于 HQL 中的这种批量操作支持,仍有一个待解决的问题,但这完全超出了 EJB3 持久性规范所要求的支持范围。我将在以后的博文中讨论这个问题,因为它真是太棒了;)
那么,什么是 批量操作
呢?对于那些熟悉 SQL 的人来说,它类似于 数据操作语言
(DML),但是,就像 HQL 和 EJB-QL 一样,它是以对象模型为定义的。什么是 DML?DML 是那些实际操作表格数据状态的 SQL 语句:INSERT、UPDATE 和 DELETE。
本质上,这意味着 EJB-QL 和 HQL 现在支持 UPDATE 和 DELETE 语句(HQL 也支持 INSERT 语句,但稍后再详细讨论)。
在基本形式上,这种支持并不太难。我的意思是,Hibernate 已经知道所有与表和列相关的信息;它已经知道如何解析 WHERE 子句等。那么,有什么大不了的?好吧,在实现过程中,我们遇到了一些使这种支持更具挑战性的主题;这当然使实现过程更加有趣;)
更新语句
来自 EJB3 持久性规范
Bulk update and delete operations apply to entities of a single entity class (together with its subclasses, if any). Only one entity abstract schema type may be specified in the FROM or UPDATE clause.
规范定义的更新语法的伪语法
update_statement ::= update_clause [where_clause] update_clause ::=UPDATE abstract_schema_name [[AS ] identification_variable] SET update_item {, update_item}* update_item ::= [identification_variable.]state_field = new_value new_value ::= simple_arithmetic_expression | string_primary | datetime_primary | boolean_primary
基本内容是
- 更新子句中只能指定一个实体(抽象的模式名称),它可以有选择地使用别名。如果实体名称有别名,则任何属性引用都必须使用该别名进行限定;如果实体名称没有别名,则对任何属性引用进行限定是不合法的。
- 更新操作中不能指定任何连接(无论是隐式还是显式)。在WHERE子句中可以使用子查询;子查询本身也可以包含连接。
- WHERE子句也是可选的。
有两个有趣的地方需要指出
- 根据规范,对版本化实体执行UPDATE不应导致版本号
增加
- 根据规范,分配的新值不允许使用子查询;HQL支持这一点!
尽管规范禁止在版本化实体的更新中增加版本号,但这往往是期望的行为。由于规范,Hibernate无法默认执行此操作,因此我们引入了新的语法关键字VERSIONED。语法是update versioned MyEntity ...
,这将导致任何受影响实体的版本列值增加。
删除语句
来自 EJB3 持久性规范
Bulk update and delete operations apply to entities of a single entity class (together with its subclasses, if any). Only one entity abstract schema type may be specified in the FROM or UPDATE clause. A delete operation only applies to entities of the specified class and its subclasses. It does not cascade to related entities.
规范定义的删除语法的伪语法
delete_statement ::= delete_clause [where_clause] delete_clause ::= DELETE FROM abstract_schema_name [[AS ] identification_variable]
基本内容是
- FROM子句中只能指定一个实体(抽象的模式名称),它可以有选择地使用别名。如果实体名称有别名,则任何属性引用都必须使用该别名进行限定;如果实体名称没有别名,则对任何属性引用进行限定是不合法的。
- 删除操作中不能指定任何连接(无论是隐式还是显式)。在WHERE子句中可以使用子查询;子查询本身也可以包含连接。
- WHERE子句也是可选的。
有一个非常有趣的地方需要指出。规范明确禁止将删除操作级联到相关实体(不包括显然的数据库级别级联)。
缓存
自动和透明对象/关系映射涉及到对象状态的管理。这意味着对象状态在内存中可用。批量操作
在很大程度上破坏了这一关注点。最大的问题是ORM工具/EJB3持久化实现者执行的缓存。
规范甚至对此提出了警告
Caution should be used when executing bulk update or delete operations because they may result in inconsistencies between the database and the entities in the active persistence context. In general, bulk update and delete operations should only be performed within a separate transaction or at the beginning of a transaction (before entities have been accessed whose state might be affected by such operations).
在Hibernate中,确保在将实体拉入会话之前执行任何需要的批量操作
,因为不这样做可能导致会话(/活动持久化上下文/)和数据库之间不一致的风险。
Hibernate还提供,正如大多数ORM工具一样,一个共享缓存(二级缓存)。执行批量操作
也会导致共享缓存和数据库之间不一致的风险。Hibernate实际上承担了管理这种风险的责任。在完成批量操作
后,Hibernate会无效化共享缓存中的任何需要区域以保持一致性。必须通过无效化来完成,因为UPDATE或DELETE仅在实际的数据库服务器上执行;因此Hibernate无法知道任何受影响实体的id,以及(在更新的情况下)新状态可能是什么。
结论
批量操作
是ORM工具提供的功能的补充。特别是在批处理过程中,批量操作
与新的StatelessSession功能(从3.1beta1可用)结合使用,提供了比基于行的ORM关注点更高效的替代方案。
其他事项
跨越多个表(不包括关联)的实体引起特定的挑战,我将在以后的博客中讨论。
请参阅参考手册 以讨论HQL中的这些批量操作。
对于熟悉ANTLR及其语法定义的您来说,HQL所支持的语法权威来源就是这些语法文件本身。