我们有一个插入之前触发器,可以从序列中获取下一个值。当使用 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列中。这对我来说没有任何意义:(我该如何避免这个问题?谢谢!
只需将触发器更新为仅在未给出 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;
上面的解决方案很棒,它让我在这个问题上省去了很多麻烦。
我唯一的抱怨是它为用户/代码打开了插入任何 ID 值的大门,而无需实际查询序列。
我发现以下解决方案也有效。它让 Hibernate 找到最大 ID,并在每次执行插入语句时将其递增。但是当它到达数据库时,该 ID 会被忽略并被触发器生成的 ID 所取代,因此集群问题中不存在唯一性:
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
private Long id;
最大的缺点是@GenericGenerator是一个Hibernate注释,所以你失去了JPA的可移植性。程序员也不清楚该 ID 实际上是否链接到序列,而事实上,它是使用序列的最紧密耦合的解决方案之一。
理想情况下,您应该删除 BEFORE INSERT TRIGGER。如果不这样做,Hibernate 就无法知道主键,而且它确实需要该信息。如果您不关心插入后的对象,这可能没问题(二级缓存仍然是一个问题),但如果您需要立即使用它,那就是一个真正的问题。在后一种情况下,你可以尝试这种讨厌的方法:
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 时,这将在触发器中引发异常,最终重置序列值。这确实导致数据库方面的模式设计结构很糟糕。
您可以使用
GenerationType.IDENTITY
策略而无需修改任何触发器。
当您使用
GenerationType.IDENTITY
策略时,您是在告诉 JPA 依靠数据库自动生成主键,通常是使用自动增量 nextval() 或数据库提供的等效机制。
但是,您应该注意以下事项:
它需要往返数据库: 由于主键是在插入操作(由数据库)之后生成的,因此在完成flush()或提交之前,ID值是未知的,这意味着在访问生成的ID之前需要与数据库进行往返。 如果您在创建实体后立即需要 ID 值,这可能并不理想。
可能出现性能瓶颈: 当插入率很高时,数据库的自动增量机制可能会成为瓶颈。这是因为数据库必须维护自增值的锁,这在高并发场景下会导致延迟。