Welcome 微信登录

首页 / 软件开发 / JAVA / Hibernate中新的TableGenerator 机制

Hibernate中新的TableGenerator 机制2014-10-22 ibm 吕 焱飞从 initialValue 说起

问题的发现源自对 JPA 中 TableGenerator 的测试。测试的环境有这样几个条件:

为方便查询的测试,Employee 表格在初始化时会导入部分记录,这部分记录的主键在初始脚本中手动写好,比如 1、2、3、4。(参看文章所附示例代码中的import_data.sql 文件)。

Employee 实体使用 TableGenerator 主键生成器,initialValue 的值设置为 10。

在单元测试中添加新的 Employee 记录。

Employee 实体类的代码参看清单 1:

清单 1. Employee 实体类

@Entity @Table(name="emp3") public class Employee3 {@TableGenerator(name="id_gen",table="id_gen",initialValue=10) @Id@GeneratedValue(strategy=TABLE,generator="id_gen") private long id; private String firstName; private String lastName; ...... }
@TableGenerator 的配置参数 initialValue 指的是主键生成列的初始值,这在 @TableGenerator 的 API 文档中写得很清楚。现在 initialValue 值设置为 10, 那么在单元测试中用 JPA 添加新的 Employee 记录时,新记录的主键会从 11 开始,不会与已有的数据发生冲突(参看文章所附示例代码中 src/java/test/sample/case3/OldInitialValue.java)。执行的结果出乎意料,测试报错,说是主键重复。错误信息如清单 2 所示:
清单 2. 主键重复错误信息

11:23:40,220 ERROR SqlExceptionHelper:144 - Duplicate entry "1" for key "PRIMARY"

这实在令人困惑。如果 initialValue 的含义不是初始值,那还能是什么呢?

问题其实出在程序所用的 JPA 提供者(Hibernate)上面。如果改用其他 JPA 提供者,估计不会出现上面的问题(未验证)。Hibernate 之所以会出现这种情况,并非无知,也不是不尊重标准,而有它自身的原因,这在文章后面会提到。现在,为了把问题讲清楚, 有必要先谈谈 JPA 主键生成器选型的问题,了解一下 @TableGenerator 在 JPA 中的特殊地位。

JPA 主键生成器选型

JPA 提供了四种主键生成器,参看表 1:

SequenceStyleGenerator

Hibernate 的 SequenceStyleGenerator 允许在不支持 Sequence 对象的数据库中模拟使用 SEQUENCE 主键生成器,这种模拟的 SEQUENCE 主键生成器本质上其实还是 TABLE 生成器。默认情况下不启用该生成器。具体配置与新 TableGenerator 相似。参考资源中有相关资料。

一般来说,支持 IDENTITY 的数据库,如 MySQL、SQL Server、DB2 等,AUTO 的效果与 IDENTITY 相同。IDENTITY 主键生成器最大的特点是:在表中插入记录以后主键才会生成。这意味着,实体对象只有在保存到数据库以后,才能得到主键值。用 EntityManager 的 persist 方法来保存实体时必须在数据库中插入纪录,这种主键生成机制大大限制了 JPA 提供者优化性能的可能性。在 Hibernate 中通过设置 FlushMode 为 MANUAL,可以将记录的插入延迟到长事务提交时再执行,从而减少对数据库的访问频率。实施这种系统性能提升方案的前提就是不能使用 IDENTITY 主键生成器。

SEQUENCE 主键生成器主要用在 PostgreSQL、Oracle 等自带 Sequence 对象的数据库管理系统中,它每次从数据库 Sequence 对象中取出一段数值分配给新生成的实体对象,实体对象在写入数据库之前就会分配到相应的主键。

上面的分析中,我们把现实世界中的关系数据库分成了两大类:一是支持 IDENTITY 的数据库,二是支持 SEQUENCE 的数据库。对支持 IDENTITY 的数据库来说,使用 JPA 时变得有点麻烦:出于性能考虑,它们在选用主键生成策略时应当避免使用 IDENTITY 和 AUTO,同时,他们不支持 SEQUENCE。看起来,四个主键生成器里面排除了三个,剩下唯一的选择就是 TABLE。由此可见,TABLE 主键生成机制在 JPA 中地位特殊。它是在不影响性能情况下,通用性最强的 JPA 主键生成器。

本栏目更多精彩内容:http://www.bianceng.cn/Programming/Java/