JPA 加鎖機制及@Version版本控制方式
JPA的加鎖機制有兩種,樂觀鎖和悲觀鎖。
樂觀鎖:
樂觀鎖的特點在于認為數(shù)據(jù)沖突或者更新丟失等情況是很少發(fā)生的.當(dāng)發(fā)生的時候,拋出異常和回滾就足夠解決問題.
悲觀鎖:
悲觀鎖的邏輯在于認為每次數(shù)據(jù)操作都很有可能發(fā)生沖突,所以一開始就獲得記錄的鎖,再進行記錄的操作是解決問題的優(yōu)先選擇.
一 簡述悲觀鎖的用法
悲觀鎖通常是SQL級別的,通過讀寫時先拿到鎖實現(xiàn),在SQL語句中就會有體現(xiàn).
1.1 EntityManager 用法
return em.createQuery(sql 語句).setLockMode(LockModeType.NONE).getResultList(); //分解寫法大概是: Query query = getSession().createQuery(hql); query.setLockMode(LockModeType.NONE);
EntityManager 是一個輔助類,createQuery后返回的就是一個Query對象,然后通過
setLockMode設(shè)置鎖的級別即可.
LockModeType 類型 | 解釋 |
---|---|
LockMode.READ | 事務(wù)的隔離級別是Repeatable Read或Serializable時,請求讀取數(shù)據(jù)庫記錄時自動獲得 |
LockMode.WRITE | 請求插入或更新數(shù)據(jù)庫記錄時自動獲得 |
LockMode.OPTIMISTIC | 樂觀鎖 |
LockMode.OPTIMISTIC_FORCE_INCREMENT | 樂觀鎖,通過version控制 |
LockMode.PESSIMISTIC_READ | 與LockMode.PESSIMISTIC_WRITE相同 |
LockMode.PESSIMISTIC_WRITE | 事務(wù)開始即獲得數(shù)據(jù)庫的鎖 |
LockMode.PESSIMISTIC_FORCE_INCREMENT | 事務(wù)開始即設(shè)置version |
LockMode.NONE | 取消任何鎖,如事務(wù)結(jié)束后的所有對象,或執(zhí)行了Session的update()、 |
二 樂觀鎖的詳細用法
樂觀鎖本篇的主要內(nèi)容
實體類是關(guān)鍵 , 樂觀鎖常用方法是通過version來控制 ,
- 數(shù)據(jù)庫對應(yīng)的表中需要有一個字段(名字隨意),字段類型設(shè)置成BigInt即可
- 業(yè)務(wù)不對該字段進行控制,字段的控制交由系統(tǒng)處理
- 每一次修改都會導(dǎo)致version遞增
- 當(dāng)出現(xiàn)同時獲得該記錄的對象且均需要修改時,當(dāng)?shù)谝粋€已經(jīng)提交事務(wù),version字段發(fā)生改變,后面提交的事務(wù)發(fā)現(xiàn)version版本不對,則無法提交,拋出異常
實體類(注意其中的@Version注解)
@Entity public class User { @Id @GeneratedValue private Long id; private String username; private String userdesc; @Version private Long version; public User() { } public User(String username, String userdesc) { this.username = username; this.userdesc = userdesc; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserDesc() { return userdesc; } public void setUserDesc(String userdesc) { this.userdesc = userdesc; } public Long getVersion() { return version; } public void setVersion(Long version) { this.version = version; } }
controller中通過sleep將線程沉睡,測試事務(wù)的提交性
@RestController public class UserController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired UserService userService; @PostMapping("/changeone") @Transactional public String changeone() { User user = userService.findUser("gang"); try { logger.info("修改1 before:user--{}--Versdion:{}", user.getUserDesc(), user.getVersion()); Thread.sleep(25000); user.setUserDesc("修改1"); logger.info("修改1 :user--{}--version:{}", user.getUserDesc(), user.getVersion()); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { logger.info("eeeeeeeeeeeeee"); e.printStackTrace(); } return "true"; } @PostMapping("/changetwo") @Transactional public String changetwo() { User user = userService.findUser("gang"); try { logger.info("修改2 before:user--{}--version:{}", user.getUserDesc(), user.getVersion()); Thread.sleep(30000); user.setUserDesc("修改2"); logger.info("修改2:user--{}--version:{}", user.getUserDesc(), user.getVersion()); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { logger.info("eeeeeeeeeeeeee"); e.printStackTrace(); } return "true"; } @PostMapping("/changethree") @Transactional public String changethree() { User user = userService.findUser("gang"); logger.info("修改3 before:user--{}--version:{}", user.getUserDesc(), user.getVersion()); user.setUserDesc("修改3"); logger.info("修改3 :user--{}--version:{}", user.getUserDesc(), user.getVersion()); return "true"; } @PostMapping("/newuser") @Transactional public String newuser() { logger.info("save user"); User user = new User(); user.setUserDesc("第一次創(chuàng)建"); user.setUsername("gang"); userService.saveUser(user); return "true"; } }
以及service及repository
@Service public class UserService { @Autowired UserRepository userRepository; public User findUser(String username){ return userRepository.findByUsername(username); } public void saveUser(User user){ userRepository.save(user); } } UserRepository public interface UserRepository extends JpaRepository<User,Long> { User findByUsername(String username); }
總結(jié)
使用很簡單,version是自動增長的,唯一的缺點是拋出的異常不易捕獲,捕獲的方法:
@Resource private UserTransaction rtc; try { rtc.begin(); User user = userService.findUser("gang"); user .setDesc("異常捕獲"); rtc.commit(); } catch (OptimisticLockException e) { throw new OptimisticLockException (); } catch (Exception e) { throw new Exception (); }
注意其中的 rtc.begin(); 以及 rtc.commit();
不同于@Transaction,這種是手動的提交方法
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
FuncGPT慧函數(shù)保護數(shù)據(jù)安全提高代碼質(zhì)量減少軟件故障(java示例)
這篇文章主要為大家介紹了FuncGPT慧函數(shù)保護數(shù)據(jù)安全提高代碼質(zhì)量減少軟件故障(java示例),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10java實現(xiàn)簡單網(wǎng)絡(luò)象棋游戲
這篇文章主要為大家詳細介紹了java實現(xiàn)簡單網(wǎng)絡(luò)象棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12IDEA中實體類(POJO)與JSON快速互轉(zhuǎn)問題
這篇文章主要介紹了IDEA中實體類(POJO)與JSON快速互轉(zhuǎn),本文通過圖文實例代碼相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下2022-08-08Java中Comparable和Comparator兩種比較器的區(qū)別詳解
這篇文章主要介紹了Java中Comparable和Comparator兩種比較器的區(qū)別詳解,Comparable接口將比較代碼嵌入自身類中,像Integer、String等這些基本類型的JAVA封裝類都已經(jīng)實現(xiàn)了Comparable接口,這些類對象本身就支持和自己比較,需要的朋友可以參考下2023-09-09Spring?Boot:Idea從零開始初始化后臺項目的教程
這篇文章主要介紹了Spring?Boot:Idea從零開始初始化后臺項目的教程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12