用于从序列生成 id 的 Oracle 触发器的 HIbernate 问题
Posted
技术标签:
【中文标题】用于从序列生成 id 的 Oracle 触发器的 HIbernate 问题【英文标题】:HIbernate issue with Oracle Trigger for generating id from a sequence 【发布时间】:2011-12-21 13:46:55 【问题描述】:我们有一个插入前触发器,可以从序列中获取下一个值。当对象使用 save() 方法持久化时,hibernate 从序列中获取值并将其添加到对象中。当事务从 Spring 的服务层提交时,ID 值在数据库上再次增加。如果对象已经有一个 id,我如何避免得到 nextval() ..
这就是我想做的事情..
用户道
public User saveUser(User user)
session.getCurrentSession.save(user);//line2
return user;//line3
用户服务
public void saveUserAndWriteToAudit(User user, UserAudit userAudit)
userDao.saveUser(user);//line1
userAudit.setUserId(user.getId);//line4
userAudit.saveUserAudit(userAudit);//line5
还有用户类
@Entity
public class User
@Id
@GeneratedValue(strategy=GenerationType.AUTO, generator="a1")
@SequenceGenerator(name="a1", sequenceName="usersequence")
private Long id;
/////////////////
当光标到达 line1 并且 line2 用户对象的 id 属性为 null 时。在第 2 行之后,它具有序列中的下一个值 - 比如说 1。在第 4 行,我已将用户的 id=1 添加到 useraudit 对象。当在第 5 行之后提交事务时,将 2 插入到用户的 id 列中,将 1 插入到 UserAudit 的 userId 列中。这对我没有任何意义:(我该如何避免这个问题?谢谢!
【问题讨论】:
【参考方案1】:只需将您的触发器更新为仅在未指定 ID 时触发。
create or replace
trigger sa.my_trigger
before insert on sa.my_table
for each row
when (new.id is null)
begin
select sa.my_sequence.nextval
into :new.id
from dual;
end;
【讨论】:
试过了,但是当我从 JPA 进行一些插入,然后通过 SQL 进行一些插入时,序列不是最新的,并且在使用 SQL 时出现“违反唯一约束”。我在实体中有@GeneratedValue
和@SequenceGenerator
注释。【参考方案2】:
上面的解决方案很棒,它让我在这个问题上省了很多麻烦。
我唯一的抱怨是它为用户/代码打开了插入任何 ID 值而不实际查询序列的大门。
我发现以下解决方案也有效。它让 Hibernate 找到最大 ID 并在每次执行插入语句时增加它。但是当它到达数据库时,ID被忽略并被触发器生成的ID替换,因此没有uniqueness in a cluster problem:
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
private Long id;
最大的缺点是@GenericGenerator 是一个Hibernate 注解,所以你失去了JPA 的可移植性。程序员也不清楚这个 ID 实际上是链接到一个序列,而事实上,它是使用序列的最紧密耦合的解决方案之一。
【讨论】:
感谢 Christopher,我一直在努力使用数据库触发器来填充主键,这解决了问题。 这也适用于我。无需更改 Oracle DB 中的 DB 触发器。非常感谢! 已更新!在我收到“违反唯一约束”错误之前,这一切正常。如前所述,执行此操作后 DB 序列不同步。同样在同一个表中插入数据的 Web 服务出现错误,因为 nextval 正在获取一个已经存在的值。【参考方案3】:理想情况下,您应该删除 BEFORE INSERT TRIGGER。如果你不这样做,Hibernate 就无法知道主键,它确实需要这些信息。如果您不关心 INSERT 之后的对象,那可能没问题(二级缓存仍然是一个问题),但如果您需要立即使用它,那就是一个真正的问题。在后一种情况下,您可以尝试这种讨厌的方法:
-
告诉 Hibernate 您自己管理主键。
创建新对象并将任意值放入主键。
刷新您的 Hibernate 会话和 SELECT sequence.CURRVAL(假设只有一个 INSERT)。
使用获取的当前序列值加载对象,不要使用上面的实例。
【讨论】:
@FelixM.. 感谢您的回复.. 如果我们修改触发器以检查 :new.id=null 与否,这行得通吗?.. 我无法测试它,因为我没有 db修改触发器的权限..在要求 dba 修改它之前,我需要确保它足够 如果您按照其他响应中的说明修改触发器,则可以告诉 Hibernate 使用该序列。【参考方案4】:create or replace
trigger sa.my_trigger
before insert on sa.my_table
for each row
when (new.id is null)
begin
select sa.my_sequence.nextval
into :new.id
from dual;
end;
这在数据库世界中确实有问题。
考虑到上述解决方案,假设 sa.my_sequence.nextval 为 51,您的休眠框架将毫无问题地工作。但是,如果有人使用您的主键值作为 66 覆盖序列(例如,当前值为 52)进行直接 jdbc 插入,则触发器只会插入。
真正的问题是当序列值增加到 66 时,这将在触发器中引发异常,以重置序列值结束。这实际上以数据库方面的架构设计结构不良而告终。
【讨论】:
以上是关于用于从序列生成 id 的 Oracle 触发器的 HIbernate 问题的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 @ID 和 @GeneratedValue 从 Hibernate + JPA 中的序列中获取 Oracle 生成的值