【问题标题】:HIbernate issue with Oracle Trigger for generating id from a sequence用于从序列生成 id 的 Oracle 触发器的 HIbernate 问题
【发布时间】: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 列中。这对我没有任何意义:(我该如何避免这个问题?谢谢!

【问题讨论】:

    标签: oracle hibernate spring-mvc triggers sequence


    【解决方案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 之后的对象,那可能没问题(二级缓存仍然是一个问题),但如果您需要立即使用它,那就是一个真正的问题。在后一种情况下,您可以尝试这种讨厌的方法:

    1. 告诉 Hibernate 您自己管理主键。
    2. 创建新对象并将任意值放入主键。
    3. 刷新您的 Hibernate 会话和 SELECT sequence.CURRVAL(假设只有一个 INSERT)。
    4. 使用获取的当前序列值加载对象,不要使用上面的实例。

    【讨论】:

    • @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 时,这将在触发器中引发异常,以重置序列值结束。这实际上以数据库方面的架构设计结构不良而告终。

    【讨论】:

      猜你喜欢
      • 2012-11-05
      • 1970-01-01
      • 2016-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-06
      • 2015-12-10
      • 2011-01-10
      相关资源
      最近更新 更多