Spring boot , Spring data JPA 并发访问

Posted

技术标签:

【中文标题】Spring boot , Spring data JPA 并发访问【英文标题】:Spring boot , Spring data JPA concurrent access 【发布时间】:2020-03-16 05:25:51 【问题描述】:

我正在尝试使用 Spring boot 和 Spring data JPA 创建一个 Restful API 来执行 CRUD 操作。数据库将是 Oracle 关系数据库。现在用于并发访问,如果我们只使用 @Transactional 使用 Spring 事务,那将达到我们并发 CRUD 操作的目的。

我看到有 JPA 乐观和悲观锁定策略版本列。我的具体问题是,对于并发 CRUD 操作,我们是否需要 Spring 事务和 JPA 锁定策略?或者仅相应地配置 Spring 事务就足够了?

【问题讨论】:

这取决于您希望应用程序在存在并发 CRUD 操作时的行为方式。 谢谢@JensSchauder。我们想要的是多个用户可以来做并发的 CRUD 操作。我们可以使用 Spring 事务或 JPA 乐观锁定来实现相同的目标吗?或者我们是否需要同时配置 Spring 事务和 JPA 锁定。 到目前为止我明白你的问题。但是,如果两个用户/进程尝试同时更改同一个实体,您实际上希望发生什么?抛出异常?应用一个改变另一个,覆盖第一个?阻止一个直到另一个完成?更不用说如果有人编辑 Order 而其他人将 OrderItem 添加到同一实体会发生什么? @JensSchauder 如果两个进程试图同时更改同一个实体,我希望一个被阻止,直到另一个完成。这只能通过春季交易来实现吗? 【参考方案1】:

尝试从以下简单的方法开始,IMO 在许多情况下都适用:Optimistic locking 和 Spring Retry。

1) 将带有@Version 注释的version 属性添加到您的实体中(例如,您可以在基础抽象实体类中这样做,以简化流程):

@Entity
public class MyEntity 

    @Id
    @GeneratedValue
    private Long id;

    @Version
    private Long version;

    // other stuff

在这种情况下,例如,当您将更新您的实体时,Hibernate 将在更新查询的条件子句中使用 version 属性的当前值,并增加该值以存储实体。例如某些服务的这段代码:

@Transactional
public Optional<MyEntity> update(Long id, MyEntity source) 
    return myEntityRepository
           .findById(id)
           .map(target -> mapper.updateEntity(source, target));

将生成以下 SQL 查询:

1. select * from my_entities where id = ?; 
2. update my_entities set ..., version = <version value from query #1> + 1 where id = ? and version = <version value from query #1>;

因此,如果另一个并发进程首先设法更新此实体,那么您的方法将失败并出现异常 (OptimisticLockException)。

2) 要管理该方法中的异常,请为其添加 @Retryable 注释(并在您的配置或应用程序类上添加 @EnableRetry 注释):

@Retryable(maxAttempts = 2)
@Transactional
public Optional<MyEntity> update(Long id, MyEntity source) 
    // ...

在这种情况下,如果该方法中出现异常,它将在新事务中再次调用以重复操作。

附加信息:

Optimistic Locking in JPA Guide to Spring Retry My Spring Retry demo

【讨论】:

【参考方案2】:

乐观锁是 JPA 的默认策略。乐观锁定可用于大多数应用程序。乐观锁更加容易和高效。悲观锁需要用于在提交事务之前需要知道碰撞的情况。

因此您不需要配置锁定策略。

【讨论】:

感谢@Ahmet Amasyali。所以这意味着只有正确配置 Spring 事务才能帮助我们实现所需的并发行为,即并发 CRUD 操作。 @adiCool 是的。大多数情况下,乐观锁就足够了。 但是我遇到了一些说 LockModeType.NONE 是默认值的参考资料。我猜当您使用 EntityManager.find(Class, Object) 时,会使用默认的 LockModeType。

以上是关于Spring boot , Spring data JPA 并发访问的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + spring-data-redis

Spring Boot 整合Spring Data JPA

Spring Boot 在使用 solrj 而不是 spring-boot-starter-data-solr 时会爆炸

spring boot系列spring boot 配置spring data jpa (查询方法)

Spring boot集成spring-boot-starter-data-jpa环境搭建

spring boot -spring data-redis