当前位置: 首页 > 知识库问答 >
问题:

同时从两个不同的线程插入/更新实体

华甫
2023-03-14

我有以下方法将播放器添加到数据库或更新它,如果它已经存在:

@Transactional(isolation = Isolation.SERIALIZABLE)
public Player addOrUpdatePlayer(String playerId, String playerName) {
    Optional<Player> playerOptional = playerRepository.findById(playerId);
    if (playerOptional.isPresent()) {
        Player player = playerOptional.get();
        player.setName(playerName);
        return playerRepository.save(player);
    } else {
        Player newPlayer = Player.builder()
            .id(playerId)
            .name(playerName)
            .build();
        return playerRepository.save(newPlayer);
    }
}

这个方法有时由两个不同的线程调用,因此几乎同时执行两次。有时我会得到以下异常:

org . H2 . JDBC . jdbcsqlintegrityconstraintviolationexception:唯一索引或主键冲突:" PUBLIC。PUBLIC上的PRIMARY_KEY_B42。玩家(ID)值3”;

这对我来说是有意义的,因为如果播放器在数据库中,这两种方法都会在一开始检查,而不是在这两种情况下。然后两者都尝试插入,速度较慢的一个会运行到约束中。

我想在执行此方法期间锁定玩家表,因此如果多次调用它,它们必须相互等待。

我的播放器存储库如下所示:

public interface PlayerRepository extends JpaRepository<Player, String> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Optional<Player> findById(String var1);

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    <S extends Player> S save(S var1);
}

我还尝试提高事务的隔离级别:

@Transactional(isolation = Isolation.SERIALIZABLE)

然而,我仍然得到了这个异常,因此显然插入仍在并发运行。我做错了什么?或者有没有其他方法来实现我在这里尝试的目标?

经过更多的研究,我想我明白了为什么@Lock@Transactional(隔离=Isolation.SERIALIZABLE)都没有做任何事情。它们基本上是通过在事务期间锁定数据库表中的记录来工作的,但是由于在我的情况下没有要锁定的记录,所以它没有做任何事情。

因此,我想我必须为该事务锁定整个表。Spring JPA有可能做到吗?既然这看起来不太明智,有没有其他方法来解决这个问题呢?

共有1个答案

须原
2023-03-14

为此,您可以使用< code>@SQLInsert来更改用于插入操作的SQL。根据您的数据库,可能会切换到一些数据库本地冲突/合并处理。请参见Hibernate事务和使用attachDirty的并发性(saveOrUpdate)

 类似资料:
  • 在一次采访中,有人问我,关于多线程,假设在同一个对象上有两个线程(线程1和线程2)。Thread-1在synchronized method1()中,Thread-2能以任何方式在java中同时进入synchronized method2()吗? 我回答“不”。这里,当Thread-1处于synchronizedmethod1()时,它必须持有对象监视器上的锁,并且只有当它退出synchroniz

  • 我对Android很陌生,我试图弄清楚片段和活动应该如何协同工作。我有一个非常丑陋的布局。1个活性和1个“根”片段。当用户单击左侧菜单时,片段被片段管理器替换。 我假设上面的代码应该用新的片段替换当前片段。片段实际上总是空的。我不知道为什么。 在onCreateView的RootFrament Fragment1是默认创建的。 在rootFragment的onCreateView中,rootFra

  • 我有一个情况,我需要启动两个线程一个接一个。我尝试了以下代码片段,在这里我可以启动Thread12,但不能启动Thread2。我怎样才能开始两个......?如何启动两个线程一个接一个...? 代码片段

  • 问题内容: 我只是对某事感到好奇。让我说我有一个表,我将更新该值,然后将其删除,然后插入新的1。如果我以这种方式编写代码,这将非常容易: 但是,如果使用“ update”语句,它将更加容易。但我的问题是,有可能同时完成这3个步骤吗? 问题答案: 引用Oracle事务处理语句文档: 事务是一个逻辑的 原子工作单元 ,包含一个或多个SQL语句。事务对SQL语句进行分组,以使它们要么全部提交(这意味着它

  • 假设有一个多线程服务器将数据写入同一端口上的两个不同套接字,其中一个专用线程处理每个套接字。两个线程是否可以同时写入各自的套接字?(所谓“同时”,我指的是真正的同时性,而不仅仅是并发交错。)或者,套接字共享同一端口的事实是否意味着强制执行互斥? 一般来说,我不清楚如何在两个任意I/O流之间共享资源。我知道两个线程不能同时写入磁盘,因为磁盘本身是共享资源。然而,在套接字和端口的情况下,我没有类似的物