Spring Data Jpa 乐观锁

Laughing
2021-07-22 / 0 评论 / 1,086 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年03月17日,已超过318天没有更新,若内容或图片失效,请留言反馈。

在数据库并发操作时,为了保证数据的正确性,我们会做一些并发处理,主要就是加锁。在加锁的选择上,常见有两种方式:悲观锁和乐观锁。
悲观锁:简单的理解就是把需要的数据全部加锁,在事务提交之前,这些数据全部不可读取和修改。
乐观锁:使用对单条数据进行版本校验和比较,来保证本次的更新是最新的,否则就失败,效率要高很多。实际工作中,乐观锁不止
在数据库层面,其实我们在做分布式系统的时候,为了实现分布式系统的数据一致性,分布式事物的一种做法就是乐观锁。

数据库操作举例说明

悲观锁的做法

select * from user where id = 1 for update;
update user set name = '张三' where id = 1;

通过使用for update给这条语句加锁,如果事务没有提交,其他任何读取和修改,都得排队等待。在代码中,我们加事务的java方法
就会自然地形成了一个锁。

乐观锁的做法

select id,version from user where id = 1;
update user set name = '张三',version=version+1 where id = 1 and version = 1;

假设本次查询version=1,在更新操作时,带上这次查出来的Version,这样只有和我们上次版本一样的时候才会更新,就不会出
现互相覆盖的问题,保证了数据的原子性。

Jpa中乐观锁解决方案

在没有@Version之前,我们都是自己手动维护这个version的,这样很有可能忘掉。或者是我们自己底层做框架,用AOP的思路做拦截底层维护这个Version的值。而Spring Data JPA的@Version就是通过AOP机制,帮我们动态维护这个Version,从而更优雅地实现乐观锁。
实体上的version字段加上@Version注解即可。

修改用户实体

@Entity
@Table(name = "sys_user")
@Data
@Slf4j

@EntityListeners({UserAuditListener.class})
public class SysUser extends AbstractAuditable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @JoinColumn(name = "dept_id", referencedColumnName = "dept_id")
    @ManyToOne(cascade = CascadeType.ALL)
    private SysDept sysDept;

    private String userName;

    private String nickName;

    private String userType;

    private String email;

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

    private String sex;

    private String avatar;

    @JsonIgnore
    private String password;

    @Enumerated(EnumType.STRING)
    private Status status;

    private String delFlag;

    private String loginIp;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private java.sql.Timestamp loginDate;

    private String remark;

    private Long recycleCompanyId;

    private Long medicalInstitutionId;

    private Long ethnicity;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private Set<SysRole> sysRoles;

    @Version
    private Long version;

}

测试更新

@GetMapping("/save")
    public SysUser save(){
        Optional<SysUser> sysUserOptional = sysUserRepository.findById(100L);
        sysUserOptional.get().setNickName("哈哈");
        return sysUserRepository.save(sysUserOptional.get());
    }

我们查看控制台输出信息,可以看到实际Sql中自动追加了version字段。
同时我们查看数据库的version字段,值自动+1了

0

评论 (0)

取消