主要介紹Spring聲明式事務中使用註解@Transactional的原理及注意事項。java
本文主要討論Spring聲明式事務中使用註解@Transactional的方式、原理及注意事項,主要包括如下內容:spring
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- 事務管理器配置,單數據源事務 --> <bean id="pkgouTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="pkGouDataSource" /> </bean> <!-- 使用annotation定義事務 --> <tx:annotation-driven transaction-manager="pkgouTransactionManager" />
1> 事務註解方式: @Transactional數據庫
2> 事務傳播行爲介紹:編程
事務傳播行爲 | 說明 |
---|---|
@Transactional(propagation=Propagation.REQUIRED) | 若是有事務, 那麼加入事務, 沒有的話新建一個(默認狀況) |
@Transactional(propagation=Propagation.NOT_SUPPORTED) | 容器不爲這個方法開啓事務 |
@Transactional(propagation=Propagation.REQUIRES_NEW) | 不論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務 |
@Transactional(propagation=Propagation.MANDATORY) | 必須在一個已有的事務中執行,不然拋出異常 |
@Transactional(propagation=Propagation.NEVER) | 必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反) |
@Transactional(propagation=Propagation.SUPPORTS) | 若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務。若是其餘bean沒有聲明事務,那就不用事務 |
3> 事務超時設置:
@Transactional(timeout=30) //默認是30秒網絡
4> 事務隔離級別:app
事務隔離級別 | 說明 |
---|---|
@Transactional(isolation = Isolation.READ_UNCOMMITTED) | 讀取未提交數據(會出現髒讀, 不可重複讀),基本不使用 |
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默認) | 讀取已提交數據(會出現不可重複讀和幻讀) |
@Transactional(isolation = Isolation.REPEATABLE_READ) | 可重複讀(會出現幻讀) |
@Transactional(isolation = Isolation.SERIALIZABLE) | 串行化 |
@Transactional的屬性:post
默認狀況下,數據庫處於自動提交模式。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,若是執行成功則隱式的提交事務,若是執行失敗則隱式的回滾事務。
事務管理,是一組相關的操做處於一個事務之中,所以必須關閉數據庫的自動提交模式。這點,Spring會在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中將底層鏈接的自動提交特性設置爲false。ui
// switch to manual commit if necessary。 this is very expensive in some jdbc drivers, // so we don't want to do it unnecessarily (for example if we've explicitly // configured the connection pool to set it already)。if (con。getautocommit()) { txobject.setmustrestoreautocommit(true); if (logger.isdebugenabled()) { logger.debug("switching jdbc connection [" + con + "] to manual commit"); } //首先將自動提交屬性改成false con.setautocommit(false); }
Spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內拋出異常。Spring事務管理器會捕捉任何未處理的異常,而後依據規則決定是否回滾拋出異常的事務。
默認配置下,Spring只有在拋出的異常爲運行時unchecked異常時纔回滾該事務,也就是拋出的異常爲RuntimeException的子類(Errors也會致使事務回滾)。而拋出checked異常則不會致使事務回滾。
Spring也支持明確的配置在拋出哪些異常時回滾事務,包括checked異常。也能夠明肯定義哪些異常拋出時不回滾事務。
還能夠編程性的經過setRollbackOnly()方法來指示一個事務必須回滾,在調用完setRollbackOnly()後你所能執行的惟一操做就是回滾。this
(1)對於基於接口動態代理的AOP事務加強來講,因爲接口的方法是public的,這就要求實現類的實現方法必須是public的(不能是protected,private等),同時不能使用static的修飾符。因此,能夠實施接口動態代理的方法只能是使用「public」或「public final」修飾符的方法,其它方法不可能被動態代理,相應的也就不能實施AOP加強,也即不能進行Spring事務加強。spa
(2)基於CGLib字節碼動態代理的方案是經過擴展被加強類,動態建立子類的方式進行AOP加強植入的。因爲使用final,static,private修飾符的方法都不能被子類覆蓋,相應的,這些方法將不能被實施的AOP加強。
因此,必須特別注意這些修飾符的使用,@Transactional 註解只被應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯,可是這個被註解的方法將不會展現已配置的事務設置。
@Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾 public void methodName() { throw new Exception("註釋"); }
@Transactional(noRollbackFor=Exception.class) //指定不回滾,遇到運行期異常(throw new RuntimeException("註釋");)會回滾 public ItimDaoImpl getItemDaoImpl() { throw new RuntimeException("註釋"); }
-僅僅 @Transactional註解的出現不足於開啓事務行爲,它僅僅是一種元數據,可以被能夠識別 @Transactional註解和上述的配置適當的具備事務行爲的beans所使用。其實,根本上是 元素的出現 開啓了事務行爲。
Spring事務使用AOP代理後的方法調用執行流程,如圖所示:
從圖中能夠看出,調用事務時首先調用的是AOP代理對象而不是目標對象,首先執行事務切面,事務切面內部經過TransactionInterceptor環繞加強進行事務的加強。即進入目標方法以前開啓事務,退出目標方法時提交/回滾事務。
這樣在自我調用時,則會出現沒法開啓事務的問題,好比:
public interface TargetService { public void a(); public void b(); } @Service public class TargetServiceImpl implements TargetService{ public void a() { this.b(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void b() { //執行數據庫操做 } }
此處的this指向目標對象,所以調用this.b()將不會執行b事務切面,即不會執行事務加強,所以b方法的事務定義「@Transactional(propagation = Propagation.REQUIRES_NEW)」將不會實施,即結果是b和a方法的事務是方法的事務定義是同樣的。
經過BeanPostProcessor 在目標對象中注入代理對象:
public interface BeanSelfAware{ public abstract void setSelf(Object obj); }
public class InjectBeanSelfProcessor implements BeanPostProcessor, ApplicationContextAware{ ApplicationContext context; private static Log log = LogFactory.getLog(com/netease/lottery/base/common/BeanSelf/InjectBeanSelfProcessor); public InjectBeanSelfProcessor(){ } public void setApplicationContext(ApplicationContext context) throws BeansException{ this.context = context; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{ if(bean instanceof BeanSelfAware){ //若是Bean實現了BeanSelfAware標識接口,就將代理對象注入 BeanSelfAware myBean = (BeanSelfAware)bean; Class cls = bean.getClass(); if(!AopUtils.isAopProxy(bean)){ Class c = bean.getClass(); Service serviceAnnotation = (Service)c.getAnnotation(org/springframework/stereotype/Service); if(serviceAnnotation != null) try{ bean = context.getBean(beanName); if(AopUtils.isAopProxy(bean)); } catch(BeanCurrentlyInCreationException beancurrentlyincreationexception) { } catch(Exception ex){ log.fatal((new StringBuilder()).append("No Proxy Bean for service ").append(bean.getClass()).append(" ").append(ex.getMessage()).toString(), ex); } } myBean.setSelf(bean); return myBean; } else{ return bean; } } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{ return bean; }
public interface TargetService { public void a(); public void b(); } @Service public class TargetServiceImpl implements TargetService,BeanSelfAware { private TargetService self; public void setSelf(Object proxyBean) { //經過InjectBeanSelfProcessor注入本身(目標對象)的AOP代理對象 this.self = (TargetService) proxyBean; } public void a() { self.b(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void b() { //執行數據庫操做 } }
postProcessAfterInitialization根據目標對象是否實現BeanSelfAware標識接口,經過setSelf(bean)將代理對象(bean)注入到目標對象中,從而能夠完成目標對象內部的自我調用。