Spring的事務配置

火星人 @ 2014-03-09 , reply:0
←手機掃碼閱讀

  Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分.

  DataSource、TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hibernate進行數據訪問時,DataSource實際為SessionFactory,TransactionManager的實現為HibernateTransactionManager.

  具體如下圖:

  根據代理機制的不同,總結了五種Spring事務的配置方式,配置文件如下:

  第一種方式:每個Bean都有一個代理

  <?xml version="1.0" encoding="UTF-8"?>

  <beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:aop="http://www.springframework.org/schema/aop"

  xsi:schemaLocation="http://www.springframework.org/schema/beans

  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

  http://www.springframework.org/schema/context

  http://www.springframework.org/schema/context/spring-context-2.5.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

  <bean id="sessionFactory"

  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  </bean>

  <!-- 定義事務管理器(聲明式的事務) -->

  <bean id="transactionManager"

  class="org.springframework.orm.hibernate3.HibernateTransactionManager">

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

  </bean>

  <!-- 配置DAO -->

  <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">

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

  </bean>

  <bean id="userDao"

  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

  <!-- 配置事務管理器 -->

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

  <property name="target" ref="userDaoTarget" />

  <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />

  <!-- 配置事務屬性 -->

  <property name="transactionAttributes">

  <props>

  <prop key="*">PROPAGATION_REQUIRED</prop>

  </props>

  </property>

  </bean>

  </beans>

  第二種方式:所有Bean共享一個代理基類

  <?xml version="1.0" encoding="UTF-8"?>

  <beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:aop="http://www.springframework.org/schema/aop"

  xsi:schemaLocation="http://www.springframework.org/schema/beans

  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

  http://www.springframework.org/schema/context

  http://www.springframework.org/schema/context/spring-context-2.5.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

  <bean id="sessionFactory"

  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  </bean>

  <!-- 定義事務管理器(聲明式的事務) -->

  <bean id="transactionManager"

  class="org.springframework.orm.hibernate3.HibernateTransactionManager">

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

  </bean>

  <bean id="transactionBaseProxy"

  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"

  lazy-init="true" abstract="true">

  <!-- 配置事務管理器 -->

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

  <!-- 配置事務屬性 -->

  <property name="transactionAttributes">

  <props>

  <prop key="*">PROPAGATION_REQUIRED</prop>

  </props>

  </property>

  </bean>

  <!-- 配置DAO -->

  <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">

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

  </bean>

  <bean id="userDao" parent="transactionBaseProxy" >

  <property name="target" ref="userDaoTarget" />

  </bean>

  </beans>

  第三種方式:使用攔截器

  <?xml version="1.0" encoding="UTF-8"?>

  <beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:aop="http://www.springframework.org/schema/aop"

  xsi:schemaLocation="http://www.springframework.org/schema/beans

  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

  http://www.springframework.org/schema/context

  http://www.springframework.org/schema/context/spring-context-2.5.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

  <bean id="sessionFactory"

  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  </bean>

  <!-- 定義事務管理器(聲明式的事務) -->

  <bean id="transactionManager"

  class="org.springframework.orm.hibernate3.HibernateTransactionManager">

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

  </bean>

  <bean id="transactionInterceptor"

  class="org.springframework.transaction.interceptor.TransactionInterceptor">

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

  <!-- 配置事務屬性 -->

  <property name="transactionAttributes">

  <props>

  <prop key="*">PROPAGATION_REQUIRED</prop>

  </props>

  </property>

  </bean>

  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

  <property name="beanNames">

  <list>

  <value>*Dao</value>

  </list>

  </property>

  <property name="interceptorNames">

  <list>

  <value>transactionInterceptor</value>

  </list>

  </property>

  </bean>

  <!-- 配置DAO -->

  <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">

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

  </bean>

  </beans>

  第四種方式:使用tx標籤配置的攔截器

  <?xml version="1.0" encoding="UTF-8"?>

  <beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:aop="http://www.springframework.org/schema/aop"

  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.5.xsd

  http://www.springframework.org/schema/context

  http://www.springframework.org/schema/context/spring-context-2.5.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

  <context:annotation-config />

  <context:component-scan base-package="com.bluesky" />

  <bean id="sessionFactory"

  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  </bean>

  <!-- 定義事務管理器(聲明式的事務) -->

  <bean id="transactionManager"

  class="org.springframework.orm.hibernate3.HibernateTransactionManager">

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

  </bean>

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

  <tx:attributes>

  <tx:method name="*" propagation="REQUIRED" />

  </tx:attributes>

  </tx:advice>

  <aop:config>

  <aop:pointcut id="interceptorPointCuts"

  expression="execution(* com.bluesky.spring.dao.*.*(..))" />

  <aop:advisor advice-ref="txAdvice"

  pointcut-ref="interceptorPointCuts" />

  </aop:config>

  </beans>

  第五種方式:全註解

  <?xml version="1.0" encoding="UTF-8"?>

  <beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:aop="http://www.springframework.org/schema/aop"

  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.5.xsd

  http://www.springframework.org/schema/context

  http://www.springframework.org/schema/context/spring-context-2.5.xsd

  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

  <context:annotation-config />

  <context:component-scan base-package="com.bluesky" />

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

  <bean id="sessionFactory"

  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  </bean>

  <!-- 定義事務管理器(聲明式的事務) -->

  <bean id="transactionManager"

  class="org.springframework.orm.hibernate3.HibernateTransactionManager">

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

  </bean>

  </beans>

  此時在DAO上需加上@Transactional註解,如下:

  package com.bluesky.spring.dao;

  import java.util.List;

  import org.hibernate.SessionFactory;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

  import org.springframework.stereotype.Component;

  import com.bluesky.spring.domain.User;

  @Transactional

  @Component("userDao")

  public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

  public List<User> listUsers() {

  return this.getSession().createQuery("from User").list();

  }

  }

  注意的幾點:

  1 @Transactional 只能被應用到public方法上, 對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.

  2 默認情況下,一個有事務方法, 遇到RuntiomeException 時會回滾 . 遇到 受檢查的異常 是不會回滾 的. 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .

  @Transactional 的所有可選屬性如下:

屬性 類型 默認值 說明
propagation Propagation枚舉 REQUIRED 事務傳播屬性 (下有說明)
isolation isolation枚舉 DEFAULT 事務隔離級別 (另有說明)
readOnly boolean false 是否只讀
timeout int -1 超時(秒)
rollbackFor Class[] {} 需要回滾的異常類
rollbackForClassName String[] {} 需要回滾的異常類名
noRollbackFor Class[] {} 不需要回滾的異常類
noRollbackForClassName String[] {} 不需要回滾的異常類名

  事務的傳播屬性 ,有如下可選

  可以去看spring源碼 : org.springframework.transaction.annotation.Propagation

REQUIRED 業務方法需要在一個事務中運行,如果方法運行時,已處在一個事務中,那麼就加入該事務,否則自己創建一個新的事務.這是spring默認的傳播行為.
SUPPORTS 如果業務方法在某個事務範圍內被調用,則方法成為該事務的一部分,如果業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行.
MANDATORY 只能在一個已存在事務中執行,業務方法不能發起自己的事務,如果業務方法在沒有事務的環境下調用,就拋異常
REQUIRES_NEW 業務方法總是會為自己發起一個新的事務,如果方法已運行在一個事務中,則原有事務被掛起,新的事務被創建,直到方法結束,新事務才結束,原先的事務才會恢復執行.
NOT_SUPPORTED 聲明方法需要事務,如果方法沒有關聯到一個事務,容器不會為它開啟事務.如果方法在一個事務中被調用,該事務會被掛起,在方法調用結束后,原先的事務便會恢復執行.
NEVER 聲明方法絕對不能在事務範圍內執行,如果方法在某個事務範圍內執行,容器就拋異常.只有沒關聯到事務,才正常執行.
NESTED 如果一個活動的事務存在,則運行在一個嵌套的事務中.如果沒有活動的事務,則按REQUIRED屬性執行.它使用了一個單獨的事務, 這個事務擁有多個可以回滾的保證點.內部事務回滾不會對外部事務造成影響, 它只對DataSourceTransactionManager 事務管理器起效.

  隔離級別:

  資料庫提供了四種事務隔離級別, 不同的隔離級別採用不同的鎖類開來實現.

  在四種隔離級別中, Serializable的級別最高, Read Uncommited級別最低.

  大多數資料庫的默認隔離級別為: Read Commited,如Sql Server , Oracle.

  少數資料庫默認的隔離級別為Repeatable Read, 如MySQL InnoDB存儲引擎

  即使是最低的級別,也不會出現 第一類 丟失 更新問題 .

  Read Uncommited :讀未提交數據( 會出現臟讀,不可重複讀,幻讀 ,避免了 第一類丟失 更新 )

  Read Commited :讀已提交的數據(會出現不可重複讀,幻讀)

  Repeatable Read :可重複讀(會出現幻讀)

  Serializable :串列化

  丟失 更新 :

  當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,會發生丟失更新問題.每個事務都不知道其它事務的存在.的更新將重寫由其它事務所做的更新,這將導致數據丟失.

  例:

  事務A和事務B同時修改某行的值,

  1.事務A將數值改為1並提交

  2.事務B將數值改為2並提交.

  這時數據的值為2,事務A所做的更新將會丟失.

  解決辦法:對行加鎖,只允許併發一個更新事務.

  臟讀: 一個事務讀到另一個事務未提交的更新數據

  例:

  1.Mary的原工資為1000, 財務人員將Mary的工資改為了8000(但未提交事務)

  2.Mary讀取自己的工資 ,發現自己的工資變為了8000,歡天喜地!

  3.而財務發現操作有誤,回滾了事務,Mary的工資又變為了1000, 像這樣,Mary記取的工資數8000是一個臟數據.

  不可重複讀: 在同一個事務中,多次讀取同一數據,返回的結果有所不同. 換句話說就是,後續讀取可以讀到另一個事務已提交的更新數據. 相反"可重複讀"在同一事務多次讀取數據時,能夠保證所讀數據一樣,也就是後續讀取不能讀到另一事務已提交的更新數據.

  例:

  1.在事務1中,Mary 讀取了自己的工資為1000,操作並沒有完成

  2.在事務2中,這時財務人員修改了Mary的工資為2000,並提交了事務.

  3.在事務1中,Mary 再次讀取自己的工資時,工資變為了2000

  解決辦法:如果只有在修改事務完全提交之後才可以讀取數據,則可以避免該問題.

  幻讀: 一個事務讀取到另一個事務已提交的insert數據.

  例:

  第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行.同時 (此時第一事務還未提交) ,第二個事務向表中插入一行新數據.這時第一個事務再去讀取表時,發現表中還有沒有修改的數據行,就好象發生了幻覺一樣.





[火星人 via ] Spring的事務配置已經有361次圍觀

http://www.coctec.com/docs/java/show-post-60235.html