spring transaction
建議
Spring
團隊的建議是你在具體的類(或類的方法)上使用 @Transactional
注解,
而不要使用在類所要實現的任何接口上。你當然可以在接口上使用 @Transactional
注解,
但是這將只能當你設置了基于接口的代理時它才生效。
因為注解是不能繼承的,
這就意味著如果你正在使用基于類的代理時,那么事務的設置將不能被基于類的代理所識別,
而且對象也將不會被事務代理所包裝(將被確認為嚴重的)。
因此請接受Spring
團隊的建議并且在具體的類上使用 @Transactional
注解。
事務無法使用的可能原因
導入spring的事務注解
應該是
1
|
org.springframework.transaction.annotation.Transactional |
而不是
1
|
javax.transaction.Transactional |
是否開啟了對注解的解析:
xml
文件配置
1
|
< tx:annotation-driven transaction-manager = "transactionManager" proxy-target-class = "true" /> |
springboot
注解開啟自動掃描
1
|
@EnableTransactionManagement |
spring
是否掃描到你使用注解事務的這個類所在的包
配置xml
1
|
< context:component-scan base-package = "com.xxx.xxx" ></ context:component-scan > |
springboot
開啟事務
1
|
@EnableTransactionManagement |
數據庫引擎要支持事務
如果是mysql
,注意表要使用支持事務的引擎,比如InnoDB
,如果是myisam
,事務是不起作用的
springboot
的配置
1
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect |
檢查方法是不是public的
@Transactional
僅僅在 public
方法,才能進行事務管理。
這是因為在使用 Spring AOP
代理時,
Spring
在調用在圖中的 TransactionInterceptor
在目標方法執行前后進行攔截之前(圖中是cglib
代理)
DynamicAdvisedInterceptor(CglibAopProxy 的內部類)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法會間接調用 AbstractFallbackTransactionAttributeSource ,而會去調用computeTransactionAttribute 方法。
1
2
3
4
5
6
7
|
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null ; } } |
這個方法會判斷如果不是 public
則會返回 null
異常類型是不是unchecked異常
默認,只有unchecked
異常時才回滾該事務
spring
只有在拋出的異常為運行時unchecked
異常時才回滾該事務,
也就是拋出的異常為RuntimeException
的子類(Errors
也會導致事務回滾).
而拋出checked
異常則不會導致事務回滾。可以明確的配置在拋出那些異常時回滾事務,
包括checked
異常。也可以明確定義那些異常拋出時不回滾事務。
如果想讓checked
異常也回滾,在注解上面寫明異常類型即可:
1
|
@Transactional (rollbackFor=Exception. class ) |
noRollbackFor
自定義不回滾的異常
異常是不是被catch住了
在Service
層捕捉異常后,發現事務不生效。
在Service
層手工捕捉并處理了異常(try…catch
)等于把異常吃掉了,
Spring
自然不知道這里有錯,更不會主動去回滾數據。推薦做法是在Service
層統一拋出異常,
然后在Controll
層統一處理。
下面代碼事務是無法生效的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//在類上@Transactional 說明,所以public都是有事務的 @Service @Transactional public class StudentService { @Autowired private GroupRepository groupRepository; @Autowired private InstituteRepository instituteRepository; public void initStudent() { Institute institute = Institute.builder().build(); institute.setCode( "TEST4" ); instituteRepository.save(institute); // 這里自己處理異常,spring不會知道存在異常,無法進行事務回滾 try { throw new RuntimeException( "運行時異常----------看事務是否起作用" ); } catch (Exception e) { e.printStackTrace(); } } } |
修改成如下代碼
1
2
3
4
5
6
7
8
9
|
public void initStudent() throws Exception{ Institute institute = Institute.builder().build(); institute.setCode( "TEST4" ); instituteRepository.save(institute); groupRepository.save(group); //不進行異常處理,而是把異常拋出 throw new RuntimeException( "運行時異常----------看事務是否起作用" ); } |
避免 Spring 的 AOP 的自調用問題
檢查是不是同一個類中的方法調用(如a方法調用同一個類中的b方法),從而避免 Spring
的 AOP
的自調用問題
這是因為在 Spring
的 AOP
代理下,只有目標方法由外部調用,
目標方法才由 Spring
生成的代理對象來管理,這會造成自調用問題。
若同一類中的其他沒有@Transactional
注解的方法內部調用有@Transactional
注解的方法,
有@Transactional
注解的方法的事務被忽略,不會發生回滾。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@Service public class StudentService { @Autowired private GroupRepository groupRepository; @Autowired private InstituteRepository instituteRepository; //initStudent() 加上@Transactional(),則會回滾 public void initStudent() throws Exception{ Institute institute = Institute.builder().build(); institute.setCode( "TEST4" ); instituteRepository.save(institute); //雖然 initGroup() 有 @Transactional() 但是事務還是沒起作用 initGroup(); throw new RuntimeException( "運行時異常----------看事務是否起作用" ); } @Transactional () public void initGroup() { Group group = Group.builder().academic_year( 2015 ).build(); group.setCode( "ELSE1" ); groupRepository.save(group); } } |
AspectJ
取代 Spring AOP
代理
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/qq_34120430/article/details/88913679