【Spring】2七、JPA 實現樂觀鎖@Version註解的使用

持久層使用jpa時,默認提供了一個註解@Version來實現樂觀鎖html

簡單來講就是用一個version字段來充當樂觀鎖的做用。
先來設計實體類java

/** * Created by xujingfeng on 2017/1/30. */ @Entity @Table(name = "t_student") public class Student { @Id @GenericGenerator(name = "PKUUID", strategy = "uuid2") @GeneratedValue(generator = "PKUUID") @Column(length = 36) private String id; @Version private int version; private String name; //getter()... //setter()... }

Dao層程序員

/** * Created by xujingfeng on 2017/1/30. */ public interface StudentDao extends JpaRepository<Student,String>{ @Query("update Student set name=?1 where id=?2") @Modifying @Transactional int updateNameById(String name,String id); }

Controller層充當單元測試的做用,經過訪問一個requestMapping來觸發咱們想要測試的方法。spring

/** * Created by xujingfeng on 2017/1/30. */ @Controller public class StudentController { @Autowired StudentDao studentDao; @RequestMapping("student.html") @ResponseBody public String student(){ Student student = new Student(); student.setName("xujingfeng"); studentDao.save(student); return "student"; } @RequestMapping("testVersion.html") @ResponseBody public String testVersion() throws InterruptedException { Student student = studentDao.findOne("6ed16acc-61df-4a66-add9-d17c88b69755"); student.setName("xuxuan"); new Thread(new Runnable() { @Override public void run() { studentDao.findOne("6ed16acc-61df-4a66-add9-d17c88b69755"); student.setName("xuxuanInThread"); studentDao.save(student); } }).start(); Thread.sleep(1000); studentDao.save(student); return "testVersion"; } @RequestMapping("updateNameById.html") @ResponseBody public String updateNameById(){ studentDao.updateNameById("xuxuan2","6ed16acc-61df-4a66-add9-d17c88b69755"); return "updateNameById"; } }

這裏面三個方法,主要是咱們想用來測試的三個注意點。
第一個方法student.html咱們想看看springdata如何對version字段進行增加的。就不貼圖了,直接給結論,對於添加了@Version的註解,咱們不須要手動去控制,每一次save操做會在原來的基礎上+1,若是初始爲null,則springdata自動設置其爲0。
第二個方法testVersion.html是樂觀鎖的核心,當多個線程併發訪問同一行記錄時,添加了@Version樂觀鎖以後,程序會進行怎麼樣的控制呢?sql

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.example.jpa.Student#6ed16acc-61df-4a66-add9-d17c88b69755]

異常信息如上,主線程和新線程獲取了同一行記錄,而且新線程優先提交了事務,版本號一致,修改爲功。等到了主線程再想save提交事務時,便獲得一個版本號不一致的異常,那麼在項目開發中就應該本身捕獲這個異常根據業務內容作對應處理,是重試仍是放棄etc…數據庫

第三個方法,updateNameById.html是想強調一下,@Query中的update,delete操做是不會觸發springdata的相關代理操做的,而是轉化爲原生sql的方式,因此在項目中使用時也要注意這點。併發

總結

樂觀鎖,用在一些敏感業務數據上,而其自己的修飾:樂觀,表明的含義即是相信大多數場景下version是一致的。可是從業務角度出發又要保證數據的嚴格一致性,避免髒讀等問題,使用的場景須要斟酌。記得前面一片博文簡單介紹了一下行級鎖的概念,其實本質上和樂觀鎖都是想要再數據庫層面加鎖控制併發,那麼何時該用樂觀鎖,行級鎖,何時得在程序級別加同步鎖,又要根據具體的業務場景去判斷。找到可以知足本身項目需求的方案,找到性能和可靠性的平衡點,纔是一個程序員的價值所在。app

出處:http://www.importnew.com/26099.htmlide

相關文章
相關標籤/搜索