在數(shù)據(jù)庫并發(fā)操作時(shí)斋竞,為了保證數(shù)據(jù)的正確性,我們會(huì)做一些并發(fā)處理秃殉,主要就是加鎖坝初。在加鎖的選擇上,有幾種方式钾军,悲觀鎖鳄袍,樂觀鎖。
悲觀鎖巧颈,簡單的理解就是把需要的數(shù)據(jù)全部加鎖畦木,在事務(wù)提交之前,這些數(shù)據(jù)全部不可讀取和修改砸泛。
樂觀鎖十籍,使用對(duì)數(shù)據(jù)進(jìn)行版本校驗(yàn)和比較,來對(duì)保證本次的更新時(shí)最新的唇礁,否則就失敗勾栗。
悲觀鎖的做法:
select * from user where uid=1 for update;
update user set name='bac' where uid=1;
這樣,uid為1的這行記錄盏筐,就被鎖住围俘,在事務(wù)提交之前,他不可被其他事務(wù)讀取和修改琢融。
樂觀鎖的做法:
select uid,name,version from user where uid=1;
假設(shè)本次查詢version=1界牡,在更新操作時(shí),
update user set name='abc', version=version+1 where uid=1 and version=1
這樣漾抬,當(dāng)其他事務(wù)在本次事務(wù)提交之前更新了宿亡,version就會(huì)+1,就不是剛才查詢到的1纳令,本次update 就失敗挽荠。
在JPA中克胳,我們可以使用@Version在某個(gè)字段上進(jìn)行樂觀鎖控制。
@Entity
@Table(name = "data_version")
public class DataVersion extends BaseEntity {
/**
*
*/
private static final long serialVersionUID = 1830796806342370927L;
private String name;
private int age;
@Version
private Long version;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
@Override
public String toString() {
return "DataVersion [name=" + name + ", age=" + age + ", version=" + version + "]";
}
}
@DataJpaTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Core.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JpaTest {
@Autowired
DataVersionJpaRepository jpa;
@Test
public void test() {
DataVersion d = new DataVersion();
d.setName("hello");
d.setAge(27);
d = jpa.saveAndFlush(d);
Long id = d.getId();
System.out.println(id);
DataVersion d2 = jpa.findOne(id);
d2.setAge(26);
d2 = jpa.saveAndFlush(d2);
System.out.println(d2);
}
}
Hibernate: insert into data_version (enable, age, name, version) values (?, ?, ?, ?)
21
Hibernate: update data_version set enable=?, age=?, name=?, version=? where id=? and version=?
DataVersion [name=hello, age=26, version=1]
當(dāng)樂觀鎖更新失敗的時(shí)候圈匆,會(huì)拋出異常org.springframework.orm.ObjectOptimisticLockingFailureException
@RunWith(SpringRunner.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@SpringBootTest(classes = AccountsApplication.class)
public class JpaTest {
@Autowired
DataVersionJpaRepository jpa;
@Test
public void test() throws InterruptedException {
DataVersion d = new DataVersion();
d.setName("hello");
d.setAge(27);
d = jpa.saveAndFlush(d);
Long id = d.getId();
System.out.println(id);
DataVersion d2 = jpa.findOne(id);
d2.setAge(26);
new Thread(new Runnable() {
@Override
public void run() {
DataVersion d2 = jpa.findOne(id);
d2.setAge(99);
d2 = jpa.saveAndFlush(d2);
System.out.println("th:" + d2);
}
}).start();
Thread.sleep(5000);
d2 = jpa.saveAndFlush(d2);
System.out.println(d2);
Thread.sleep(20000);
}
}