HIbernate 使用 Oracle 触发器从序列生成 id 的问题

问题描述 投票:0回答:5

我们有一个插入之前触发器,可以从序列中获取下一个值。当使用 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;
     /////////////////
 }

当光标到达第1行和第2行时,用户对象的id属性为空。在第2行之后,它具有序列中的nextval - 假设1.在第4行上,我已将用户的id = 1添加到useraudit对象中..当在第5行之后提交事务时,2被插入到用户的id列中,1被插入到UserAudit的userId列中。这对我来说没有任何意义:(我该如何避免这个问题?谢谢!

oracle-database hibernate spring-mvc triggers sequence
5个回答
13
投票

只需将触发器更新为仅在未给出 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;

7
投票

上面的解决方案很棒,它让我在这个问题上省去了很多麻烦。

我唯一的抱怨是它为用户/代码打开了插入任何 ID 值的大门,而无需实际查询序列。

我发现以下解决方案也有效。它让 Hibernate 找到最大 ID,并在每次执行插入语句时将其递增。但是当它到达数据库时,该 ID 会被忽略并被触发器生成的 ID 所取代,因此集群问题中不存在唯一性

    @Id
    @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    private Long id;

最大的缺点是@GenericGenerator是一个Hibernate注释,所以你失去了JPA的可移植性。程序员也不清楚该 ID 实际上是否链接到序列,而事实上,它是使用序列的最紧密耦合的解决方案之一。


0
投票

理想情况下,您应该删除 BEFORE INSERT TRIGGER。如果不这样做,Hibernate 就无法知道主键,而且它确实需要该信息。如果您不关心插入后的对象,这可能没问题(二级缓存仍然是一个问题),但如果您需要立即使用它,那就是一个真正的问题。在后一种情况下,你可以尝试这种讨厌的方法:

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

0
投票
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 时,这将在触发器中引发异常,最终重置序列值。这确实导致数据库方面的模式设计结构很糟糕。


0
投票

您可以使用

GenerationType.IDENTITY
策略而无需修改任何触发器。

当您使用

GenerationType.IDENTITY
策略时,您是在告诉 JPA 依靠数据库自动生成主键,通常是使用自动增量 nextval() 或数据库提供的等效机制。

但是,您应该注意以下事项:

  1. 它需要往返数据库: 由于主键是在插入操作(由数据库)之后生成的,因此在完成flush()或提交之前,ID值是未知的,这意味着在访问生成的ID之前需要与数据库进行往返。 如果您在创建实体后立即需要 ID 值,这可能并不理想。

  2. 可能出现性能瓶颈: 当插入率很高时,数据库的自动增量机制可能会成为瓶颈。这是因为数据库必须维护自增值的锁,这在高并发场景下会导致延迟。

© www.soinside.com 2019 - 2024. All rights reserved.