【问题标题】:Requests in parallel generate inserts instead of upates in JPA并行请求在 JPA 中生成插入而不是更新
【发布时间】:2021-09-24 10:31:55
【问题描述】:

我有一项服务应该计算用户登录的次数。如果我并行发出 3 个请求,我最终会在数据库中使用:

Name Requests
Jane 1
Jane 1
Jane 1

代替

Name Requests
Jane 3

名称不是主键。 睡眠仅用于演示目的,用于缓慢连接到数据库。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public Long getRequestNumber(String name) {
       UserEntity userEntity = userRepository.findByName(name);

       if (userEntity == null) {
          Thread.sleep(10000);
          UserEntity userEntity = new UserEntity();
          userEntity.setName(name);
          userEntity.setReqNumber(1L);
          userRepository.saveAndFlush(userEntity);
          return 1L;
        } else {
          userEntity.setReqNumber(userEntity.getReqNumber() + 1);
          userRepository.saveAndFlush(userEntity);
          return userEntity.getReqNumber();
       }
  }
}


public interface UserRepository extends JpaRepository<UserRepository, Long> {
      UserEntity findByName(String name);
}



  @Data
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  @Entity
  @Table(name = "user")
  public class UserEntity implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Long id;

  @Column(name = "name")
  private String name;

  @Column(name = "req_number")
  private Long reqNumber;

}

稍后编辑:

这似乎可行,但如果有人有更好的主意,我很高兴?

public interface UserRepository extends JpaRepository<UserRepository, Long> {

  @Lock(LockModeType.PESSIMISTIC_READ) @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "30000")})
  UserEntity findByName(String name);
}

【问题讨论】:

  • 这种行为并不奇怪,因为您在插入新的 UserEntity 之前睡了 10 秒
  • 睡眠仅用于演示目的
  • 我明白这一点。即使没有睡眠,这种行为也可能偶尔发生
  • 您应该查看隔离级别的概念,并确定这些限制与您的要求之间的关系。
  • 我试过公共接口 UserRepository extends JpaRepository { @Lock(LockModeType.PESSIMISTIC_READ) @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = " 3000")}) UserEntity findByName(String name); }

标签: java jpa spring-data-jpa spring-data


【解决方案1】:

如果您不希望多行具有相同的用户名,则应对该列设置唯一约束。

使用唯一约束,只有一个用户的并行第一个请求会成功。另一个将因违反约束而失败。这是有道理的,它类似于同时创建具有相同用户名的新用户帐户 - 只有一个用户可以(并且应该)成功。

要增加计数器,最好让数据库以事务方式处理:

@Modifying
@Query("update UserEntity u set u.Requests = u.Requests + 1 where u.name = :name")
int updateUserSetStatusForName(@Param("name") String name);

【讨论】:

  • 对不起,它是 .setName(name)
  • 放置唯一约束会产生错误,并且该请求将被拒绝。
  • 正确。但是,如果您想消除为一个用户获取多个条目的风险,这是一种解决方案。
  • 我试过了:公共接口 UserRepository extends JpaRepository { @Lock(LockModeType.PESSIMISTIC_READ) @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")}) UserEntity findByName(String name); }
猜你喜欢
  • 2011-07-03
  • 1970-01-01
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多