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