我们都知道使用@SequenceGenerator
时Hibernate的默认行为 - 它通过一个增加实际数据库序列,将此值乘以50(默认allocationSize
值) - 并且然后将此值用作实体ID。
这是不正确的行为并与specification冲突,其中包含:
allocationSize - (可选)从序列中分配序列号时的增量。
要明确:我不打算生成ID之间的差距。
我关心与基础数据库序列不一致的ID。例如:任何其他应用程序(例如使用普通JDBC)可能希望在从序列获取的ID下插入新行 - 但所有这些值可能已被Hibernate使用!疯狂。
有人知道这个问题的任何解决方案(没有设置allocationSize=1
从而降低性能)?
修改
说清楚。
如果最后插入的记录具有ID = 1
,则HB同时对其新实体使用值51, 52, 53...
但是:数据库中的序列值将设置为2
。当其他应用程序使用该序列时,这很容易导致错误。
另一方面:规范说(根据我的理解)数据库序列应该设置为51
,同时HB应该使用范围2, 3 ... 50
更新:
正如Steve Ebersole在下面提到的那样:我可以通过设置hibernate.id.new_generator_mappings=true
来启用我描述的行为(也是最直观的行为)。
谢谢大家。
更新2:
对于未来的读者,您可以在下面找到一个有效的例子。
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
@SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
private Long id;
}
的persistence.xml
<persistence-unit name="testPU">
<properties>
<property name="hibernate.id.new_generator_mappings" value="true" />
</properties>
</persistence-unit>
答案 0 :(得分:34)
绝对清楚......你所描述的不以任何方式与规范冲突。该规范讨论了Hibernate为您的实体分配的值,而不是实际存储在数据库序列中的值。
但是,您可以选择获取所需的行为。首先在Is there a way to dynamically choose a @GeneratedValue strategy using JPA annotations and Hibernate?上看到我的回复。这将为您提供基础知识。只要您设置为使用SequenceStyleGenerator,Hibernate将使用SequenceStyleGenerator中的“池化优化器”来解释allocationSize
。 “池化优化器”用于允许在序列创建时使用“增量”选项的数据库(并非所有支持序列的数据库都支持增量)。无论如何,请阅读那里的各种优化策略。
答案 1 :(得分:12)
allocationSize=1
在获取查询之前,这是一个微优化Hibernate尝试在allocationSize的范围内分配值,因此尽量避免查询数据库的序列。但是如果将其设置为1,则每次都会执行此查询。这几乎没有任何区别,因为如果您的数据库被其他应用程序访问,那么如果另一个应用程序同时使用相同的ID,则会产生问题。
下一代Sequence Id基于allocationSize。
通过defualt保持为50
太多了。如果您在一个会话中有大约50
个记录,这些记录不会持续存在,并且将使用此特定会话和转换来保留,那么它也会有所帮助。
因此,在使用allocationSize=1
时,您应始终使用SequenceGenerator
。对于大多数底层数据库,序列总是递增1
。
答案 2 :(得分:1)
if ( lo > maxLo ) {
lastSourceValue = callback.getNextValue();
lo = lastSourceValue.eq( 0 ) ? 1 : 0;
hi = lastSourceValue.copy().multiplyBy( maxLo+1 );
}
value = hi.copy().add( lo++ );
每当它到达if语句的内部时,hi值变得越来越大。因此,在频繁重启服务器的测试过程中,我的id生成以下序列ID:
1,2,3,4,19,250,251,252,400,550,750,751,752,850,1100,1150。
我知道你已经说它与规范没有冲突,但我相信这对大多数开发者来说都是非常意外的情况。
任何人的输入都会有所帮助。
Jihwan
更新: ne1410s:感谢您的编辑 克里克:好的。我去做。这是我在这里的第一篇文章,并不确定如何使用它。
现在,我更好地理解为什么maxLo用于两个目的:由于hibernate一次调用DB序列,继续增加Java级别的id,并将其保存到DB,Java级别id值应考虑多少是下次调用序列时,在不调用DB序列的情况下进行了更改。
例如,序列id在某一点为1,而休眠则为5,6,7,8,9(其中allocationSize = 5)。下次,当我们得到下一个序列号时,DB返回2,但是hibernate需要使用10,11,12 ...所以,这就是为什么&#34; hi = lastSourceValue.copy()。multiplyBy(maxLo + 1) )&#34;用于从DB序列返回的2中获取下一个id 10。似乎只是困扰的事情是在频繁的服务器重启期间,这是我的问题与较大的差距。
因此,当我们使用SEQUENCE ID时,表中插入的id将与DB中的SEQUENCE编号不匹配。
答案 3 :(得分:1)
深入挖掘hibernate源代码后 下面的配置在50次插入后转到Oracle db以获取下一个值。因此,每次调用INST_PK_SEQ时都要增加50。
Hibernate 5用于以下策略
另请参见下文
rank
@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@org.hibernate.annotations.Parameter(
name = "optimizer", value = "pooled-lo"),
@org.hibernate.annotations.Parameter(
name = "initial_value", value = "1"),
@org.hibernate.annotations.Parameter(
name = "increment_size", value = "50"),
@org.hibernate.annotations.Parameter(
name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
}
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;
答案 4 :(得分:0)
我会检查DDL中的模式中的序列。 JPA实现仅负责创建具有正确分配大小的序列。因此,如果分配大小为50,那么您的序列在其DDL中必须具有50的增量。
这种情况通常可能在创建分配大小为1的序列时发生,然后配置为分配大小50(或默认值)但序列DDL未更新。
答案 5 :(得分:0)
我在Hibernate 5中也遇到了这个问题:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;
收到如下警告:
发现使用了不推荐使用的基于序列的[org.hibernate.id.SequenceHiLoGenerator] ID生成器;请改用org.hibernate.id.enhanced.SequenceStyleGenerator。有关详细信息,请参见《 Hibernate域模型映射指南》。
然后将我的代码更改为SequenceStyleGenerator:
@Id
@GenericGenerator(name="cmrSeq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "SEQUENCE")}
)
@GeneratedValue(generator = "sequence_name")
private Long titId;
这解决了我的两个问题:
1)已弃用的警告已修复2)现在,根据oracle序列生成了id。