Hibernate, JPA, and Sequences

这篇文章主要介绍Hibernate 和 JPA 在Stack 3.x下如何与数据库的sequences 交互。这里介绍了一些common的用例, 但并不打算详细介绍Hibernate 或JPA的 ID 生成策略。这篇文章可以帮助你避免常见的陷阱以及可以解答下面的这些问题:
[list]
[*] 当我调用entityManager.persist(obj), 同时数据库的sequences返回的值是正数时,但为什么持久的对象ID是负数。
[*] 为什么发生ORA-00001错误: 持久化对象时发生违反唯一性限制
[*] console上的提示信息 "Pooled optimizer source reported -x as the initial value; use of 1 or greater highly recommended" 是什么意思?
[/list]

[b]基础[/b]

Oracle中, 常用sequences代理主键生成策略。 在对象持久化时,Hibernate/JPA 可以配置成用sequence 赋值给对应的fields。

[b]实践小结[/b]
在JPA中, 把注解SequenceGenerator中的allocationSize 的值设置为默认50。这个值往往不是我们所期望的。而且这个值大家都应该显示设置。如果数据库中sequence的步长为1时 (这个值是数据库的默认值), 应该把allocationSize 值设置为1.

[b]Hibernate中新的主键策略和优化性能[/b]
在Hibernate 3.2.3 中, 引进了两种提高主键生成策略的可移植性和性能。 其中的一种就是SequenceStyleGenerator. 当Hibernate被配置成用这种新的映射时, 并且后台是Oracle数据库, 生成策略就是上面描述的那样。 此外, 用这种增前的主键生成策略, 如果数据库中的sequence步长大于1, 并且allocationSize 的值与它一样。Hibernate will fill in the holes between the values returned by the sequence and therefore save trips to the database. (也就是sequence步长之间的间隙值, 有Hibernate补上, 这样就不用到数据库中请求真实的sequence的值了 )

[b]建议[/b]
新的主键生成策略与老的有很大的区别, 当使用老的生成策略时, allocatiionSize 的值并不鼓励设置的比1大。否则的话, 如果所有插入数据的地方这个配置不相同的话, 会导致主键冲突。 当使用新的策略时, 我们也建议你把它设置为1, 除非你需要这些微小的性能提升。

[b]用例[/b]
这些例子, 假设你使用LDS Java Stack version 3.x 配置成主键增强。 具体配置请参考[url]http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/ch01.html#ann-setup-properties[/url]


[b]例1 Increment By 1[/b]
如果你并不需要性能提高, 或者不能忍受主键的不连续。把数据库中的sequence, JPA/Hiebernate生成器的步长都设为1.

数据库:
[table]
CREATE SEQUENCE example_sq START WITH 1 INCREMENT BY 1;
[/table]


PO类:
[table]
@Id
@SequenceGenerator(name = "ExampleSequence", sequenceName = "SEQ_EXAMPLE_PK", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ExampleSequence")
private Long id;
[/table]

使用这种配置, JPA/Hibernate在持久化事例之前, 会从数据库中的sequence中取出下一个值并用这个值去设置ID。

[b]例2 Increment By 50[/b]
如果你需要提高性能, 并且可以容忍ID的不连续性, 那么就把sequence和 JPA/Hibernate的生成步长大于2. 这样可以使用Hiebernate的优化。


数据库:
[table]
CREATE SEQUENCE example_sq START WITH 50 INCREMENT BY 50;
[/table]


PO类:
[table]
@Id
@SequenceGenerator(name = "ExampleSequence", sequenceName = "SEQ_EXAMPLE_PK", allocationSize=50)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ExampleSequence")
private Long id;
[/table]

使用这个配置后, Hibernate从数据库中的sequence取得下一个值(假设是250), 减去allocationSize的值(250-50=200), 每次增长1, 并且把结果(201)作为一个新实体类持久化的ID值。 对于下一个新的持久化对象, Hibernate会用之前的值加1且不用去数据库中调用sequence值。 它一直持续到等于开始从数据库中sequence返回的值 (250), 然后才再次从数据库中去取sequence的下一个值。 如此反复。

[b]不一致性问题[/b]
If you create the sequence with the default "increment by" of 1, but do not set the incrementBy to 1 on the SequenceGenerator annotation, you will end up with a unique constraint violation trying to persist new objects to the database. The hibernate generator operates under the assumption that it can behave as described above, using a pool of id's. It doesn't verify that the sequence is not incrementing by 50. The first 50 rows will insert properly with id's ranging from -48 to 1. Hibernate will attempt to insert the 51st record with an id of -47 which will generate an ORA-00001: unique constraint violated.

如果创建的sequence步长是默认的1, 但没有在SequenceGenerator 注解中显示设置步长, 那么就会导致持久化一个PO对象时发生唯一性冲突。 Hiebernate主键生成器默认假设能用我们上面描述的 ID池。 它并不会去验证sequence是不是步长为50的。 前面的50行记录的范围会从-48到1且可以正确插入。 当Hiebernate试图插入第51个记录时(2-50+1=-47)会发生ORA-00001错误: 唯一性冲突。

英文原文: [url]https://tech.lds.org/wiki/Hibernate,_JPA,_and_Sequences[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值