Hibernate Session#merge在接收具有ID的实体时是否应该插入?

Dav*_*est 10 java concurrency hibernate jpa

这似乎经常出现,但我用Google搜索无济于事.

假设你有一个Hibernate实体User.您User的数据库中有一个ID为1.

你有两个运行的线程,A和B.它们执行以下操作:

  • A获取用户1和closesSession
  • B获得用户1和delete
  • A更改用户1上的字段
  • A获得了一次新的Sessionmerge的用户1

我的所有测试都表明merge尝试在数据库中找到用户1(显然不能),因此它会插入一个id为2的新用户.

另一方面,我的期望是Hibernate会看到合并的用户不是新的(因为它有一个ID).它会尝试在DB中找到失败的用户,因此不会尝试插入或更新.理想情况下,它会抛出某种并发异常.

请注意,我使用乐观锁定@Version,这无济于事.

所以,问题:

  1. 我观察到的Hibernate行为是否是预期的行为?
  2. 如果是这样,在调用mergeJPA EntityManager而不是Hibernate 时是否有相同的行为Session
  3. 如果对2的答案是肯定的,为什么没人抱怨呢?

Dav*_*est 1

我一直在研究 JSR-220,声称Session#merge 可以从中获取其语义。遗憾的是,我发现 JSR 含糊不清。

它确实说:

乐观锁定是一种技术,用于确保仅当自读取实体状态以来没有干预事务更新该数据时,才会对与实体状态相对应的数据库数据进行更新。

如果您将“更新”包括数据库数据的一般突变(包括删除),而不仅仅是 SQL UPDATE,我认为您可以提出一个论点,即观察到的行为不符合乐观锁定。

考虑到对我的问题的评论以及随后发现的这个错误,很多人都同意。

从纯粹实用的角度来看,这种行为,无论是否合规,都可能导致相当多的错误,因为它与许多开发人员的期望相反。似乎没有一个简单的解决办法。事实上,Spring Data JPA 似乎完全忽略了这个问题,盲目使用EM#merge. 也许其他 JPA 提供商会以不同的方式处理此问题,但对于 Hibernate,这可能会导致问题。

我实际上正在通过使用Session#updatecurrent 来解决这个问题。它真的很难看,并且当您尝试update分离实体并且已经有它的托管副本时,需要代码来处理这种情况。但是,它也不会导致虚假插入。