Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当接收到ID为的实体时,Hibernate会话#merge是否应该执行插入操作?_Java_Hibernate_Jpa_Concurrency - Fatal编程技术网

Java 当接收到ID为的实体时,Hibernate会话#merge是否应该执行插入操作?

Java 当接收到ID为的实体时,Hibernate会话#merge是否应该执行插入操作?,java,hibernate,jpa,concurrency,Java,Hibernate,Jpa,Concurrency,这似乎会经常出现,但我在谷歌上搜索了一下,却一无所获 假设您有一个Hibernate实体用户。您的数据库中有一个id为1的用户 您有两个线程在运行,A和B。它们执行以下操作: A获取用户1并关闭其会话 B获取用户1并删除它 A更改用户1上的字段 A获取新的会话和合并s用户1 我所有的测试都表明,merge试图在数据库中找到用户1(显然它不能),因此它插入了一个id为2的新用户 另一方面,我的期望是Hibernate会看到被合并的用户不是新用户(因为它有一个ID)。它将尝试在数据库中查找用户,

这似乎会经常出现,但我在谷歌上搜索了一下,却一无所获

假设您有一个Hibernate实体
用户
。您的数据库中有一个id为1的
用户

您有两个线程在运行,A和B。它们执行以下操作:

  • A获取用户1并关闭其会话
  • B获取用户1并
    删除它
  • A更改用户1上的字段
  • A获取新的
    会话
    合并
    s用户1
我所有的测试都表明,
merge
试图在数据库中找到用户1(显然它不能),因此它插入了一个id为2的新用户

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

请注意,我使用的是通过
@Version
进行的乐观锁定,这对解决问题没有帮助

因此,问题是:

  • 我观察到的冬眠行为是否为预期行为
  • 如果是这样,那么在JPA
    EntityManager
    上调用
    merge
    而不是在Hibernate
    会话上调用
    merge
    时是否会出现相同的行为
  • 如果答案是2。是的,为什么没有人抱怨呢

  • 1.我观察到的冬眠行为是否为预期行为

    这种行为是正确的。如果必须将操作拆分为两个会话,您只需尝试执行不受并发数据修改保护的操作:)。只需再次找到要更新的对象并检查它是否仍然存在,如果不存在则抛出异常。如果有,则使用em(类、主键、LockModeType)将其锁定;或者使用@Version或@Entity(optimisticLock=OptimisticLockType.ALL/DIRTY/Version)保护对象直到事务结束

    2.如果是这样,在JPA EntityManager上而不是在Hibernate会话上调用merge时是否有相同的行为

    可能:是的

    3.如果答案是2。是的,为什么没有人抱怨呢

    因为如果使用悲观或乐观锁定来保护操作,问题将消失:)


    您试图解决的问题称为:不可重复读取请参阅下面hibernate文档中的文本

    将给定对象的状态复制到具有相同标识符的持久对象上。如果当前没有与会话关联的持久实例,则将加载该实例。返回持久实例。如果给定实例未保存,请保存其副本并将其作为新的持久实例返回。

    它清楚地说明了复制数据库中对象的状态(数据)。如果对象不存在,则保存该数据的副本。当我们说保存副本时,hibernate总是创建一个带有新标识符的记录

    Hibernate合并函数的工作原理如下

  • 它检查实体的状态(连接到会话或已分离到会话),并发现它已分离
  • 然后它尝试加载标识符为但在数据库中找不到的实体
  • 因为找不到实体,所以它将该实体视为暂时的
  • 临时实体始终使用新标识符创建新的数据库记录 锁定始终应用于附着的图元。若实体已分离,则hibernate将始终加载该实体并更新版本值


    锁定用于控制并发问题。这不是并发性问题。

    我一直在研究JSR-220,从中可以看到
    Session#merge
    。遗憾的是,我发现JSR模棱两可

    它确实说:

    乐观锁定是一种用于确保更新 与实体状态相对应的数据库数据 仅当自 实体状态被读取

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

    鉴于对我的问题的评论以及随后发现的问题,许多人都同意

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


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

    您是否尝试过使用乐观锁定(
    @Version
    )?这可能会捕获插入具有版本集的行。然而,我不确定。事实上,我已经知道了。它不会影响结果。但是,是的,你也希望这会有所帮助。我也对这种行为感到惊讶。对我来说就像一只巨大的虫子。你用的是哪个版本?看起来像是1661年我选出来的一个bug,因为它似乎得到了更多的关注。我还将972标记为副本。我们还能做些什么来引起更多的注意吗?不,事实上。正如我在问题中所说,乐观锁定无助于这种情况。悲观锁定可能会起作用(我没有使用过),但对于一个可以很容易解决的问题来说,这是一个相当苛刻的解决方法。嗯,为什么乐观锁定没有帮助?如果再次找到该对象,则hibernate将执行“更新X,其中版本=?”而不是insert语句。我