你的浏览器不支持canvas

做你害怕做的事情,然后你会发现,不过如此。

使用JPA的乐观锁@Version解决数据库并发问题

时间: 作者: 黄运鑫

本文章属原创文章,未经作者许可,禁止转载,复制,下载,以及用作商业用途。原作者保留所有解释权。


问题

  • 多个用户或多线程同时查询并更新同一条数据时,后更新的数据会把先更新的数据覆盖掉。
  • 比如:有两个用户A和B,同时操作“张三”这条数据,A用户把“张三”改成“李四”,B用户把“张三”改成“王五”;如果在A用户保存之后B用户也点击保存,那么A用户保存的“李四”就会被B用户保存的“王五”覆盖掉,而且A用户和B用户互不知情。

@Version的使用

简介

  • @Version是乐观锁,即不对数据库加锁,而是通过保存在数据的版本号来判断数据是否有其他更新,从而保证并发时数据不会被覆盖。

使用代码

  • 使用时在数据库表中增加version字段,并在实体类中增加@Version注解,部分代码如下:
    @Entity
    @Table(name = "bw_pet")
    public class Pet{
    
      //id
      @Column(name = "id")
      private Long id;
    
      @Column(name = "num")
      private Integer num;
    
      @Column(name = "version")
      @Version
      private Integer version;
    
      //其他代码略... ...
    }
    

执行结果

  • 增加了@Version注解后,在每次更新数据时,数据库中的version会自增+1,并且在更新时会判断version是否改变,如果version改变则说明数据已经有其他更新,不执行本次更新操作并报异常org.springframework.orm.ObjectOptimisticLockingFailureException

具体实现过程

  • 先看一下没有加@Version时,更新numsql语句如下:

    UPDATE pet SET num = 0 WHERE id = 1;

  • 再对比一下增加@Version后的sql语句:

    UPDATE pet SET num = 0 WHERE id = 1 AND version=2;

  • 会发现增加@Version后,sql语句增加了version=2来判断是否执行更新。
  • 如果在UPDATE pet SET num = 0 WHERE id = 1 AND version=2;执行前有其他用户先更新了这条数据,那么数据库中的version会自增为3;这时候如果再执行UPDATE pet SET num = 0 WHERE id = 1 AND version=2;时,因为语句中的version=2条件不成立,代码会抛出异常org.springframework.orm.ObjectOptimisticLockingFailureException,并且不执行sql语句。

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。