程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 深入分析@Transactional的用法,@transactional

深入分析@Transactional的用法,@transactional

編輯:JAVA綜合教程

深入分析@Transactional的用法,@transactional


 

在分析深入分析@Transactional的使用之前,我們先回顧一下事務的一些基本內容。

事務的基本概念

先來回顧一下事務的基本概念和特性。數據庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。事務,就必須具備ACID特性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)。

編程式事務與聲明式事務

Spring與Hibernate的整合實現的事務管理是常用的一種功能。Hibernate 建議所有的數據庫訪問都應放在事務內進行,即使只進行只讀操作。事務又應該盡可能短,因為長事務會導致長時間無法釋放表內行級鎖,從而降低系統並發的性能。 Spring 同時支持編程式事務和聲明式事務。

 

編程式事務需要在代碼中顯式調用beginTransaction()、commit()、rollback()等事務管理相關的方法。Spring 的聲明式事務管理在底層是建立在 AOP 的基礎之上的,其本質是對方法前後進行攔截,然後在目標方法開始之前創建或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。事務增強也是AOP的一大用武之處。

使用聲明式事務

聲明式事務最大的優點就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需在配置文件中做相關的事務規則聲明,很好的分離了業務邏輯和事務管理邏輯。以spring+jpa整合增加聲明式事務為例,下面是使用聲明式事務的配置方式以及使用。

applicationContext.xml配置

<!-- 配置entityManagerFactory 配置 -->

<bean id="entityManagerFactory"

class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="dataSource" ref="dataSource" />

<property name="persistenceXmlLocation"

value="/WEB-INF/classes/persistence.xml" />

<property name="loadTimeWeaver">

<beanclass="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

</property>

</bean>

 

<!-- 配置聲明式事務管理,使用JpaTransactionManager作為事務管理器的實現類 -->

<bean id="transactionManager"

class="org.springframework.orm.jpa.JpaTransactionManager">

<property name="entityManagerFactory" ref="entityManagerFactory" />

</bean>

 

<!--聲明業務組件使用注解生成事務代理 -->

<tx:annotation-driven transaction-manager="transactionManager" />

使用@Transactional 配置聲明式事務管理

在spirng 的配置文件中配置了事務管理和注解驅動之後,我們就可以在業務層使用@Transactional 配置聲明式事務管理了。如下

 

@Service

@Transactional

public class BaseService<T> implements IBaseService<T> {

 

@Autowired

private IBaseDao<T> baseDao;

......

}

@Transactional 深入使用

上面我們已經知道了聲明式事務管理的用法了,下面我們將進一步分析@Transactional在各種場景下用法。

事務的傳播行為

@Transactional注解支持9個屬性的設置,其中Propagation屬性用來枚舉事務的傳播行為。所謂事務傳播行為就是多個事務方法相互調用時,事務如何在這些方法間傳播。Spring 支持 7 種事務傳播行為:

 

 

 

REQUIRED 是常用的事務傳播行為,如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。其它傳播行為大家可另查閱。

多個事務方法的嵌套調用時的事務傳播

有三個事務方法調用嵌套關系如下:

 

PayHisInfoService.update()->PayHisInfoService.save()->PayInfoService.update()

 

@Transactional(propagation=Propagation.REQUIRED)

public abstract class BaseService<E>{

public abstract List<E> findAll();

}

@Component

public class PayHisInfoService extends BaseService<PayHisInfo>{

@Resource

private PayHisInfoDao payHisInfoDao;

@Resource

private PayInfoService payInfoService;

 

public  void update(PayHisInfo payHisInfo,PayInfo payInfo){

     save(payHisInfo);

payInfoService.update(payInfo);

     }

    public int save(PayHisInfo payHisInfo){

         return payHisInfoDao.insert(payHisInfo);

    }

}

 

@Component

public class PayInfoService extends BaseService<PayInfo>{

@Resource

private transient PayInfoDao payInfoDao;

@Override

@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)

public List<PayInfo> findAll() {

return payInfoDao.findAll();

}

public int update(PayInfo payInfo){

return payInfoDao.update(payInfo);

}

}

 

 

當我們按照上面的調用嵌套關系執行時,結果如下

 

 

 

從日志中我們可以看出,PayHisInfoService.update()執行時開啟了一個事務,而PayHisInfoService.save()方法和PayHisInfoService.update()方法處於同一個類中,調用時沒有發生事務傳播,就像已經處於PayHisInfoService.update()方法的事務當中一樣。而當執行PayInfoService.update()時,提示“Participating in existing transaction”,這說明發生了事務傳播,即PayInfoService.update()並沒有新建一個事務,而是加入到已有的PayHisInfoService.update()事務中。

多線程環境下的事務傳播

現在我們將上面例子中的PayHisInfoService.update()用另一個線程來執行,如下

 

@Component

public class PayHisInfoService extends BaseService<PayHisInfo>{

@Resource

private PayHisInfoDao payHisInfoDao;

    @Resource

     private PayInfoService payInfoService;

 

public  void update(PayHisInfo payHisInfo,PayInfo payInfo){

          save(payHisInfo);

           //payInfoService.update(payInfo);

PayInfoThread pifth = new PayInfoThread(payInfoService,payInfo);

     pifth.start();

     }

     public int save(PayHisInfo payHisInfo){

          return payHisInfoDao.insert(payHisInfo);

     }

     private class PayInfoThread extends Thread{

        private PayInfoService payInfoService;

        private PayInfo payInfo;

       

        private PayInfoThread(PayInfoService payInfoService,PayInfo payInfo) {

            this.payInfoService = payInfoService;

            this.payInfo = payInfo;

        }

        public void run() {

        System.out.println("PayInfoThread updating payInfo...");

        payInfoService.update(payInfo);

        }

  }

}

 

當我們按照上面的調用嵌套關系執行時,結果如下

 

 

 

從日志結果可以看出,在執行PayHisInfoService.update()和PayInfoService.update()時分別創建了各自的事務。而PayHisInfoService.save()和PayHisInfoService.update()則在同一個線程中執行。所以spring 的事務管理是線程安全的。

在相同線程中進行相互嵌套調用的事務方法工作於相同的事務中。如果這些相互嵌套調用的方法工作在不同的線程中,不同線程下的事務方法工作在獨立的事務中。

@Transactional的繼承關系

或許你已經在上面的例子中看出了,我們只在父類BaseService中聲明了@Transactional,子類就自然得到事務增強。注解並沒有繼承這種說法,但此處用“繼承關系”來形容父類@Transactional和子類方法之間的關系最恰當不過了:父類Service聲明了@Transactional,子類繼承父類,父類的聲明的@Transactional會對子類的所有方法進行事務增強。這個還有個很實用的用法,例如測試基類繼承

AbstractTransactionalJUnit4SpringContextTests

聲明@Transactional可方便進行事務測試

 

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("/applicationContext.xml")

@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

@Transactional

public class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {

public Object getBean(String beanName){

return applicationContext.getBean(beanName);

}

}

 

給BaseTest 聲明了@Transactional了之後,就會自動給我們自己編寫的測試類中的所有測試方法進行事務增強。

@Transactional的優先級

如果子類的方法重寫了父類的方法並且聲明了@Transactional,那麼子類的事務聲明會優先於父類的事務聲明。

利用spring的AOP進行事務切面增強

還是上面的列子,把父類聲明的@Transactional去掉。在applicationContext.xml中配置事務增強切面,配置如下:

 

<!--②使用aop和tx命名空間語法為PayHisInfoService所有公用方法添加事務增強 -->

    <aop:config proxy-target-class="true">

        <aop:pointcut id="serviceJdbcMethod"

        expression="execution(public * com.xxx.service.PayHisInfoService.*(..))"/>

        <aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/>

    </aop:config>

    <tx:advice id="jdbcAdvice" transaction-manager="transactionManager">

        <tx:attributes>

            <tx:method name="*"/>

        </tx:attributes>

    </tx:advice>

 

運行測試用例,結果如下:

 

 

 

當有一條數據更新失敗時,事務會自動回滾,如下:

 

 

@Transactional方法的可見度

上面為了測試演示方便,我們把@Transactional都聲明在了類上。實際上@Transactional 可以作用於接口、接口方法、類以及類方法上。但是 Spring 小組建議不要在接口或者接口方法上使用該注解,因為這只有在使用基於接口的代理時它才會生效。另外, @Transactional 注解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的(從上面的Spring AOP 事務增強可以看出,就是針對方法的)。如果你在 protected、private 或者默認可見性的方法上使用 @Transactional 注解,這將被忽略,也不會拋出任何異常。

總結

(1)聲明式事務優於編程式事務

(2)事務方法的嵌套調用會產生事務傳播

(3)spring 的事務管理是線程安全的

(4)父類的聲明的@Transactional會對子類的所有方法進行事務增強

(5)從Spring AOP本質看,@Transactional 注解應該只被應用到 public 方法上

 

 

參考文檔

http://www.ibm.com/developerworks/cn/java/j-lo-spring-ts3/

http://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/

 

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved