日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JPA事务示例分析

發布時間:2024/7/23 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JPA事务示例分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在這個工程中,定義一個名為User的實體:

@Entity @Data @NoArgsConstructor public class User {@Id@GeneratedValueprivate Long id;@Size(max = 5)private String name;@Max(50)private Integer age;public User(String name, Integer age) {this.name = name;this.age = age;}}

這里name設置了長度為5,這樣可以通過insert語句中的name超長,讓其拋出異常,從而可以測試事務的觸發。

另外工程中還包含了Spring Data Jpa的數據訪問對象UserRepository,用來實現對User實體的數據操作,這里就不放具體代碼了。

問題來了

這里數據庫采用MySQL 5.7,存儲引擎為InnoDB,使用默認事務級別。

下面來調整下這四個問題吧:

問題一:test1會不會回滾?-- 回滾

@Transactional public void test1() {userRepository.save(new User("AAA", 10));throw new RuntimeException(); }

問題二:test2會不會回滾?-- 不回滾

@Transactional public void test2() {userRepository.save(new User("AAA", 10));try {throw new RuntimeException();} catch (Exception e) {log.error("異常捕獲:", e);} }

問題三:test3會不會回滾?(第二句插入name超長)-- 回滾

@Transactional public void test3() {userRepository.save(new User("AAA", 10));userRepository.save(new User("1234567890", 20)); }

問題四:test4會不會回滾?(第二句插入name超長)-- 回滾

@Transactional public void test4() {userRepository.save(new User("AAA", 10));try {userRepository.save(new User("1234567890", 20));} catch (Exception e) {log.error("異常捕獲:", e);} }

為什么寫了catch,還會回滾

先來看看執行時候報的異常:

javax.validation.ConstraintViolationException: Validation failed for classes [com.didispace.chapter310.User] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ConstraintViolationImpl{interpolatedMessage='個數必須在0和5之間', propertyPath=name, rootBeanClass=class com.didispace.chapter310.User, messageTemplate='{javax.validation.constraints.Size.message}'} ]at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:209) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:83) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]

這個異常是這個回滾的關鍵。這個異常javax.validation.ConstraintViolationException是哪里的呢?還記得以前說的JSR 303不?對的,是Bean Validation中的異常。

有的讀者說這個不是RuntimeException,所以不會回滾。很顯然,這類判斷的都沒有實際嘗試一下,只要點開源碼可以馬上發現,這個異常就是屬于RunTimeException的。

實際上,之所以會回滾,與這里使用Spring Data JPA以及Hibernate Validator有直接關系。從JPA 2.0開始,就默認支持了這些Bean Validation的實現,它提供了實體生命周期中pre-persist,?pre-update,pre-remove三個事件發生時來執行校驗的功能。而在校驗的時候,當校驗失敗,拋出javax.validation.ConstraintViolationException時,當前事務就會被標記為rollback。

源碼解析

要想了解,這其中到底發生了什么,跟蹤源碼是最好的方式。那么源碼從哪里開始看呢?從異常日志中找線索吧。

從異常棧中找到最近的一個錯誤,點開看看。

錯誤行數在532行tx.commit(),習慣性的加上斷點,這樣下一次進來的時候可以看看當前情況下的各種參數情況。

同時看到下面還有個catch,既然532行出錯了,那這里肯定會進,所以也加個端點,到時候可以進去看看。

執行程序,調用一下test4,執行到532行,然后進入下一步,看看會到哪里?

這個時候,會進入到org.hibernate.engine.transaction.internal.TransactionImpl,具體位置如下:

還是習慣性的,在下面兩行重要位置加上斷點,以便下次可以快速到這里。

繼續按上看的步驟嘗試下去,可以來到下圖的位置:

可以看到校驗異常是從271行出來的,結合278行和280行,是不是清楚這里回滾的原因了呢?

為什么加了@Transactional注解,事務沒有回滾?

@Transactional注解不生效,是Spring使用者非常常見的一類問題,上面我們講了一種,其他還有一些可能的原因,這里作為擴展閱讀一并列出。

如果你當前碰到的原因不是上面的情況,那就看看下面這幾種情況是否存在:

1.@Transactional注解修飾的函數中catch了異常,并沒有往方法外拋。不過,也有一寫復雜場景可能不一樣,比如我這里出的四個題中的test4:我來出個題:這個事務會不會回滾?

2.@Transactional注解修飾的函數不是public類型

3.異常類型錯誤,如果有通過rollbackFor指定回滾的異常類型,那么拋出的異常與指定的是否一致。

4.數據源沒有配置事務管理器

5.在一個類中調用自己的方法。建議分開寫,互相調用。

6.對應數據庫使用的存儲引擎不支持事務,比如:MyISAM。

總結

以上是生活随笔為你收集整理的JPA事务示例分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。