前言
Mysql使用Innodb引擎,支持事务,行锁,
那么,高并发情况下,多个事务同时进行;如果多个事务对同一条数据进行操作,并且都成功了,肯定会出现脏读等问题,最终导致数据库的数据有问题。破坏了数据的原子性,唯一性,正确性。
简单举个栗子:
银行转账:
小李卡里有10块钱。
小李和小王同时来取钱,小李取2块钱,小王取2块钱。
取钱操作加了事务。
【预期结果,最后剩余: 10 - 2 - 2 = 6】
【实际结果,最后剩余:8】
原因:小李取钱,上下文开启事务1,读取的余额是10;
小王取钱,这个时候事务1还没有提交,又开启事务2,读取到的的余额也是10;
事务1不知道事务2取钱了,都提交成功了。数据库最终是8.
@Version
公司用的spring data jpa,通过@version注解添加表数据的乐观锁定的支持
添加@version注解,系统每次保存会自动给该属性值加1。配合事务,版本不对,事务则不能提交,数据回滚,保证数据没有脏写入
实体基类:
DomainObject
package com.test.domain;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@MappedSuperclass
public class DomainObject implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
Long id;
@Version
@Column(name = "version", length = 11)
private int version = 0;
@Column(name = "guid")
String guid = GuidGenerator.createGuid();
@Column(name = "archived", length = 1)
boolean archived = false;
@Column(name = "created_datetime")
protected LocalDateTime createdDateTime = LocalDateTime.now();
@Column(name = "last_modified_datetime")
LocalDateTime lastModifiedDateTime;
@PrePersist
@PreUpdate
public void updateLastModifiedDateTime() {
lastModifiedDateTime = LocalDateTime.now();
}
public void updateLastModifiedDateTime(LocalDateTime localDateTime) {
lastModifiedDateTime = localDateTime;
}
public void archive() {
archived = true;
}
public void unarchive() {
archived = false;
}
public boolean isArchived() {
return archived;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !(obj instanceof DomainObject)) {
return false;
}
DomainObject other = (DomainObject) obj;
// if the id is missing, return false
if (guid() == null) {
return false;
}
// equivalence by guid
return guid().equals(other.guid());
}
@Override
public int hashCode() {
return guid.hashCode();
}
public String guid() {
return guid;
}
public int version() {
return version;
}
public LocalDateTime createdDateTime() {
return createdDateTime;
}
public LocalDateTime lastModifiedDateTime() {
return lastModifiedDateTime;
}
public boolean isNew() {
return id == null;
}
public boolean isPersisted() {
return id != null;
}
public void copyFrom(DomainObject domainObject) {
this.guid = domainObject.guid;
this.archived = domainObject.archived;
this.version = domainObject.version;
this.createdDateTime = domainObject.createdDateTime;
this.lastModifiedDateTime = domainObject.lastModifiedDateTime;
}
}
其他实体继承至这个实体,就可以了。当事务提交时,通过版本来对数据进行校验,如果事务提交时,版本已经被修改了,那本次事务提交失败并回滚。从而保证数据一致性。