在使用事務管理前,有必要先了解下應用場景。如實際過程當中一個購買操做包含多個執行過程:查詢庫存、下單、更新庫存,實際操做時,因爲高併發存在,可能到下單結束時,更新庫存出錯,那本次購買操做就是失敗的,其下單結果應該被回滾。這種狀況就須要引入事務控制,保證整個操做的有效性。html
(1)配置applicationContext.xmljava
<!-- 事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事務註解驅動,標註@Transactional的類和方法將具備事務性 --> <tx:annotation-driven transaction-manager="txManager"/>
(2)給具體業務方法添加註解程序員
public class TestService { @Transactional("txManager") public void buy(){ AuthenticationMapper authenticationMapper = MyApplicationContextUtil.getBean("authenticationMapper"); int item_id = 2; // 查詢指定書目狀態 Integer status = authenticationMapper.selectStatusOfItem(item_id); if (status == 1) { Integer count = authenticationMapper.insertOrder(item_id); if (count == 1) { authenticationMapper.updateStatus(item_id); throw new IllegalArgumentException("數據已存在,回滾"); } } } }
接下來,對buy方法作一個說明,該方法實現以下功能:spring
這個過程不加事務控制時,可能會出現一個問題,訂單建立後,數據更新失敗,致使這個過程執行失敗,就會給數據庫帶來髒數據,這些訂單數據也沒有意義。因此這裏經過事務控制來保證方法執行的一致性,過程當中失敗則回滾。數據庫
固然,Spring中事務回滾是有觸發機制的,其觸發機制就是拋出unchecked異常,即RuntimeException及其子類異常。即上述代碼中的實現:api
// 直接拋出便可回滾 throw new RuntimeException("數據已存在,回滾");
可是,若是是拋出checked異常,即須要在代碼中顯式地處理,好比try-catch塊處理,或者給所在的方法加上throws說明。對於這類異常,也要實現回滾。既然已知其觸發機制,人爲製造觸發點便可,好比在catch中拋出unchecked異常。網絡
try { throw new IOException("IO異常"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("數據已存在,回滾"); }
有關checked與unchecked異常區別後續補充,本文不展開。有關拋出RuntimeException,其實本身能夠定義一個異常類來處理。併發
public class MyException extends RuntimeException{ public MyException(String message) { super(message); } }
(3)業務類注入beanoracle
在beans.xml中添加testService的bean,交由Spring來統一管理業務對象,這樣該對象上面的事務纔會生效。app
<bean id="testService" class="com.loongshawn.service.TestService"/>
若是業務對象是經過new產生的,即testService沒有註冊bean,數據庫是不會執行回滾的,即以下:
TestService testService = new TestService(); testService.buy();
(4)業務測試
public void executeDeliveryTask() { TestService testService = MyApplicationContextUtil.getBean("testService"); testService.buy(); }
運行結果:
數據庫中沒有髒數據插入。
spring事務有7種傳播行爲,分別是:
一、PROPAGATION.REQUIRED:若是當前沒有事務,就建立一個新事務,若是當前存在事務,就加入該事務,該設置是最經常使用的設置。
二、PROPAGATION.SUPPORTS:支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就以非事務執行。
三、PROPAGATION.MANDATORY:支持當前事務,若是當前存在事務,就加入該事務,若是當前不存在事務,就拋出異常。
四、PROPAGATION.REQUIRES_NEW:建立新事務,不管當前存不存在事務,都建立新事務。
五、PROPAGATION.NOT_SUPPORTED:以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
六、PROPAGATION.NEVER:以非事務方式執行,若是當前存在事務,則拋出異常。
七、PROPAGATION.NESTED:若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。
註解@Transactional默認的傳播行爲是:PROPAGATION.REQUIRED
一、在須要事務管理的地方加@Transactional 註解。@Transactional 註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上。
二、@Transactional 註解只能應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。
三、注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據。必須在配置文件中使用配置元素,才真正開啓了事務行爲。
四、Spring團隊建議在具體的類(或類的方法)上使用 @Transactional 註解,而不要使用在類所要實現的任何接口上。在接口上使用 @Transactional 註解,只能當你設置了基於接口的代理時它才生效。由於註解是 不能繼承 的,這就意味着若是正在使用基於類的代理時,那麼事務的設置將不能被基於類的代理所識別,並且對象也將不會被事務代理所包裝。
以上內容摘自網絡,實際應用過程當中,儘可能只在具體業務類上添加 @Transactional 註解,作到程序員只關注具體業務便可。
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: txManager,transactionManager at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:313) at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:337) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:252) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644) at com.autonavi.service.TestService$$EnhancerBySpringCGLIB$$272f01cd.buy(<generated>) at com.autonavi.task.test.ScheduledTest.executeDeliveryTask(ScheduledTest.java:64) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
解決方法:
須要指定具體的事物管理器,即將@Transactional改成@Transactional(「txManager」),txManager爲定義好的事務管理器。
一、RuntimeException API文檔
二、Exception API文檔
三、Spring事務管理(詳解+實例)
四、Interface PlatformTransactionManager