如何使用Spring Data JPA优雅地实现乐观锁和悲观锁

在并发数据库操作领域,处理数据完整性至关重要。 Spring Data 与 JPA(Java Persistence API)集成,提供乐观和悲观锁定机制。

乐观锁: 乐观锁的基本思想是,认为在大多数情况下,数据访问不会导致冲突。因此,乐观锁允许多个事务同时读取和修改相同的数据,而不进行显式的锁定。在提交事务之前,会检查是

否有其他事务对该数据进行了修改。如果没有冲突,则提交成功;如果发现冲突,就需要回滚并重新尝试。

乐观锁通常使用版本号或时间戳来实现。每个数据项都会包含一个表示当前版本的标识符。在读取数据时,会将版本标识符保存下来。在提交更新时,会检查数据的当前版本是否与保存的版本匹配。如果匹配,则更新成功;否则,表示数据已被其他事务修改,需要处理冲突。

乐观锁适用于读操作频率较高、写操作冲突较少的场景。它减少了锁的使用,提高了并发性能,但需要处理冲突和重试的情况。

悲观锁: 悲观锁的基本思想是,在数据访问期间假设会发生冲突,因此在访问数据之前就会对其进行锁定,阻止其他事务对该数据进行修改。

悲观锁使用排他锁(Exclusive Lock)来实现。当一个事务对数据进行修改时,它会请求排他锁,并且其他事务无法获取相同的锁直到该事务释放锁。这样可以确保在任何时候只有一个事务能够修改数据,避免了冲突。

悲观锁适用于写操作频率较高、写操作冲突较多的场景。它确保了数据的一致性和完整性,但可能降低并发性能,因为其他事务需要等待锁的释放。

选择乐观锁还是悲观锁取决于具体的应用场景和并发控制需求。乐观锁适合读多写少、冲突较少的情况,而悲观锁适合写多读少、冲突较多的情况。

Spring Data JPA 乐观锁

@Data
@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    private int version;

    private String name;

    private double price;
}

public interface ProductRepository extends JpaRepository<Product, Long> {}

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;

    public void updatePrice(Long id, double newPrice) {
        Product product = productRepository.findById(id).orElseThrow();
        product.setPrice(newPrice);
        productRepository.save(product);
    }
}

在上面的示例中,当两个线程同时尝试更新同一产品的价格时,第一个线程将成功更新该产品。但第二个线程将失败,因为版本不匹配,抛出ObjectOptimisticLockingFailureException。

updatePrice方法生成的 SQL如下:

SELECT id, name, price, version FROM product WHERE id = ?
UPDATE product SET name = ?, price = ?, version = ? WHERE id = ? AND version = ?

原理如下:

  • 在Product实体的version字段添加@Version注解。
  • 读取操作(如findById)是非阻塞的,可以由多个线程并行完成。他们不检查也不关心版本列。
  • 写入操作(如save)将检查版本列,以确保数据自读取以来未发生更改。如果另一个线程同时更新了数据(因此增加了版本号),则保存操作将失败并显示 ObjectOptimisticLockingFailureException。

如果你想确保读取操作是最新的或在读取时阻止其他操作,则需要采用悲观锁定策略,例如 PESSIMISTIC_READ 或 PESSIMISTIC_WRITE。

Spring Data JPA 悲观锁

@Data
@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private double price;
}

public interface ProductRepository extends JpaRepository<Product, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Optional<Product> findByIdLocked(Long id);
}

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public void updatePrice(Long id, double newPrice) {
        Product product = productRepository
            .findByIdLocked(id)
            .orElseThrow(EntityNotFoundException::new);
        product.setPrice(newPrice);
    }
}

@Lock(LockModeType.PESSIMISTIC_WRITE) 注解确保在调用 findByIdLocked 时获得写锁。

此处,@Transactional 注释在调用 updatePrice 时启动新事务。如果该方法成功完成,则事务提交,如果抛出异常,则回滚。

生成的SQL:

用锁获取:

SELECT id, name, price FROM product WHERE id = ? FOR UPDATE

updatePrice SQL如下:

UPDATE product SET name = ?, price = ? WHERE id = ?

悲观锁提供了一种通过在事务的整个持续时间内获取锁定来防止并发数据访问冲突的方法。此方法在高争用场景中特别有用。然而,必须意识到死锁的可能性以及对系统吞吐量的影响。正确的事务管理(如 @Transactional 所示)可确保操作的原子性。

结论:

在 Spring Data JPA 的事务管理和数据一致性方面,我们有两种主要的锁定策略可供使用:

  • @Transactional+@Lock(LockModeType.PESSIMISTIC_WRITE):这种组合实现了悲观锁定方法。当使用此配置执行读取操作时,应用程序将锁定数据库中的特定行,以防止其他事务修改它,直到当前事务完成。虽然这确保了严格的一致性并防止冲突,但在某些情况下,由于等待释放锁的时间可能会降低吞吐量。
  • @Version:该注解采用乐观锁定策略。这里,当读取数据时,不应用锁。相反,在尝试更新时,Spring Data JPA 会检查自上次读取以来数据的版本是否已被另一个事务修改。如果发生此类修改,则会抛出 ObjectOptimisticLockingFailureException 。该策略假设冲突很少,并且大多数交易将在不受干扰的情况下进行。

根据特定的用例和性能要求,开发人员可以在悲观锁定和乐观锁定之间进行选择。每种方法都有其独特的优点和挑战。该决定取决于并发数据访问的预期频率以及管理数据一致性所需的严格程度。

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/241756.html<

(0)
管理的头像管理
上一篇2025-04-24 11:51
下一篇 2025-04-24 11:52

相关推荐

  • 云服务器和云虚拟主机怎么选?云服务器和虚拟主机区别

    云服务器适合业务增长快、需弹性扩展的场景,而云虚拟主机适合预算有限、技术门槛低的小型静态网站或测试环境,二者核心区别在于资源独享性与运维复杂度,核心差异解析:从底层架构到使用体验很多人容易混淆这两者,觉得它们都是“买空间建站”,它们的底层逻辑完全不同,云服务器(ECS)就像是你租了一整栋别墅,水电网络独立,你想……

    2026-06-29
    0
  • 赣州智慧旅游招聘是真的吗?赣州旅游人才招聘信息

    中级岗位(3-5年经验)月薪范围通常在6000-10000元,这类岗位需要独立负责项目模块,如独立运营一个抖音账号,或维护一个景区小程序的功能迭代,具备成功案例的候选人议价能力较强,高级岗位(5年以上经验)月薪范围通常在10000-20000元,部分核心管理岗可达更高,这类人才需要具备战略规划能力,如制定整个景……

    2026-06-29
    0
  • 赣州智能物联网车位锁如何管理?智能车位锁管理系统多少钱

    赣州智能物联网车位锁管理的核心在于通过云端平台实现远程控锁、状态实时监控及自动计费,彻底解决传统车位“被占难管”与“找位难”的痛点,在赣州这样的城市,随着机动车保有量的持续增长,老旧小区、商业综合体以及私人固定车位的资源矛盾日益凸显,传统的机械地锁或简易遥控锁,不仅操作繁琐,更无法实现数据化管理,引入智能物联网……

    2026-06-29
    0
  • 赣州智能消防栓好用吗,智能消防栓多少钱一个

    赣州智能消防栓通过物联网技术实现实时监测与远程报警,能显著降低火灾响应时间并提升城市消防安全管理水平,是目前智慧城市建设中不可或缺的基础设施,赣州智能消防栓的核心价值与应用场景传统消防栓往往存在“看不见、摸不着、用不了”的痛点,在赣州这样地形复杂、老城区与新城区并存的区域,传统设施的管理难度极大,智能消防栓的出……

    2026-06-29
    0
  • 云服务器和物理机到底有啥区别?

    云服务器本质上是虚拟化资源池中的弹性实例,而传统物理服务器是独占的硬件实体,前者胜在弹性与运维便捷,后者强在物理隔离与性能稳定,具体选择取决于业务对成本、扩展性及安全合规的权衡,很多人初次接触服务器时,容易把“云服务器”和“传统物理服务器”混为一谈,觉得它们都是用来跑网站或存数据的盒子,这两者的底层逻辑完全不同……

    2026-06-29
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注