A用戶向B用戶轉賬100,第一步要從A賬戶扣出100,第二步要將B賬戶加上100。其中不管是第一步失敗,仍是第二步失敗。都應該將A、B賬戶的餘額保持和轉賬操做以前一致。
事務就是一系列相關聯操做的集合,一個事務能夠是多個步驟組成,若是一個步驟失敗,那麼整個流程都應該回滾到初始狀態。java
rollback 回滾事務spring
只讀狀態sql
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManagerName" value="java:/TransactionManager" /> </bean>
在上面TransactionDefinition接口中能夠看到,一共有7個級別數據庫
代碼展現瞭如何經過將Connection設置成conn.setAutoCommit(false),而後在代碼執行成功以後再經過conn.commit()方法來提交,或者是conn.rollback()來回滾操做。express
public class Demo { public static void main(String[] args) { test1(); } public static Connection getConn() { String driver = "oracle.jdbc.OracleDriver"; String url = "jdbc:oracle:thin:@xxxxx"; String username = "username"; String password = "password"; Connection conn = null; try{ Class.forName(driver); conn = (Connection) DriverManager.getConnection(url, username, password); }catch (Exception e){ e.printStackTrace(); } return conn; } public static void test1() { Connection conn = getConn(); Random random = new Random(); int id = random.nextInt(100); System.out.println("--------id is "+id+"--------"); String sql = "insert into test (ID,NAME) values(?,?)"; PreparedStatement pstmt; try { conn.setAutoCommit(false); pstmt = (PreparedStatement) conn.prepareStatement(sql); pstmt.setString(1, id+""); pstmt.setString(2, "haha"); pstmt.executeUpdate(); if(id%2==0){ System.out.println("--------even num--------"); int error = 9/0; }else{ System.out.println("--------odd num--------"); } conn.commit(); pstmt.close(); conn.close(); System.out.println("----------commit-------------"); } catch (Exception e) { try { conn.rollback(); System.out.println("----------rollback-------------"); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); } } }
class Demo{ @Transactional(propagation = Propagation.REQUIRED) methodB(); }
若是此時單獨調用methodB(),剛進入方法的時候,是沒有事務的;而後因爲註解上的事務傳播級別是REQUIRED;因此此時Spring會默認建立一個事務,將methodB()內的全部操做放在一個事務內。編程
class Demo{ @Transactional(propagation = Propagation.REQUIRED) methodA(){ methodB(); } @Transactional(propagation = Propagation.REQUIRED) methodB(); }
此時若是調用methodA(),Spring一樣會爲methodA()上新建一個事務,而後再調用methodB()時,因爲已經存在事務,此時methodB()將在已有的事務中運行。Spring會保證methodB()內全部操做數據庫使用的鏈接是和methodA()中使用到的數據庫鏈接是同一個。後端
class Demo{ @Transactional(propagation = Propagation.SUPPORTS) methodB(); }
單獨調用methodB(),因爲默認沒有事務,因此方法就不會有事務的存在。session
class Demo{ @Transactional(propagation = Propagation.REQUIRED) methodA(){ methodB(); } @Transactional(propagation = Propagation.SUPPORTS) methodB(); }
此時若是調用methodA(),Spring一樣會爲methodA()上新建一個事務,而後再調用methodB()時,因爲已經存在事務,此時methodB()將在已有的事務中運行。併發
class Demo{ @Transactional(propagation = Propagation.MANDATORY) methodB(); }
單獨調用methodB(),因爲默認沒有事務,此時會報錯。oracle
class Demo{ @Transactional(propagation = Propagation.REQUIRED) methodA(){ methodB(); } @Transactional(propagation = Propagation.MANDATORY) methodB(); }
經過methodA()來調用methodB(),因爲methodA()中有事務,methodB()將運行在已有的事務中。
class Demo{ @Transactional(propagation = Propagation.REQUIRES_NEW) methodB(); }
單獨調用methodB(),因爲默認沒有事務,會新建立一個事務。
class Demo{ @Transactional(propagation = Propagation.REQUIRED) methodA(){ methodB(); } @Transactional(propagation = Propagation.REQUIRES_NEW) methodB(); }
經過methodA()來調用methodB(),因爲methodA()中有事務,methodB()將會把已有的事務掛起,新建立一個事務,methodB()將運行在新建立的事務中。methodB()和methodA()相互的運行結果不會由於對方失敗而回滾,由於他們是在兩個相互獨立的事務中。若是要使用PROPAGATION_REQUIRES_NEW,須要使用 JtaTransactionManager做爲事務管理器。
方法決不能運行在事務中,若是有事務存在,則會拋出異常。
class Demo{ @Transactional(propagation = Propagation.NESTED) methodB(); }
單獨調用methodB(),行爲和Propagation.REQUIRED同樣,因爲默認沒有事務,會新建立一個事務。
class Demo{ @Transactional(propagation = Propagation.REQUIRED) methodA(){ funBefore(); methodB(); funAfter(); } @Transactional(propagation = Propagation.NESTED) methodB(); }
嵌套事務一個很是重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所作的動做。而內層事務操做失敗並不會引發外層事務的回滾。
若是不存在事務,二者的行爲和PROPAGATION_REQUIRES一致,都會建立一個新的事務。
若是已存在事務,二者也都會新建立一個事務。
因而可知, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 徹底是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 若是外部事務 commit, 嵌套事務也會被 commit, 這個規則一樣適用於 roll back.
髒讀
發生在一個事務讀取了另外一個事務改寫但還沒有提交的數據時。若是改寫在稍後被回滾了,那麼第一個事務獲取的數據就是無效的。
不可重複讀
不可重複讀發生在一個事務執行相同的查詢兩次或兩次以上,可是每次都獲得不一樣的數據時。這一般是由於另外一個併發事務在兩次查詢期間進行了更新。
幻讀
幻讀與不可重複讀相似。它發生在一個事務(T1)讀取了幾行數據,接着另外一個併發事務(T2)插入了一些數據時。在隨後的查詢中,第一個事務(T1)就會發現多了一些本來不存在的記錄。
隔離級別 | 含義 |
---|---|
ISOLATION_DEFAULT | 使用數據庫的默認隔離級別 |
ISOLATION_READ_UNCOMMITTED | 最低的隔離級別,容許讀取到還沒有COMMIT的數據,會致使髒讀,幻讀和不可重複讀 |
ISOLATION_READ_COMMITTED | 容許讀取併發事務已經COMMIT的事務,能夠阻止髒讀,可是幻讀和不可重複讀還會發生 |
ISOLATION_REPEATABLE_READ | 對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改,能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生 |
ISOLATION_SERIALIZABLE | 最高的隔離級別,徹底服從ACID的隔離級別,確保阻止髒讀、不可重複讀以及幻讀,也是最慢的事務隔離級別,由於它一般是經過徹底鎖定事務相關的數據庫表來實現的 |
爲了使應用程序很好地運行,事務不能運行太長的時間。由於事務可能涉及對後端數據庫的鎖定,因此長時間的事務會沒必要要的佔用數據庫資源。事務超時就是事務的一個定時器,在特定時間內事務若是沒有執行完畢,那麼就會自動回滾,而不是一直等待其結束。
這些規則定義了哪些異常會致使事務回滾而哪些不會。默認狀況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾,可是你能夠聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。一樣,你還能夠聲明事務遇到特定的異常不回滾,即便這些異常是運行期異常。
這個接口描述的是一些處理事務提供簡單的控制事務執行和查詢事務狀態的方法,在回滾或提交的時候須要應用對應的事務狀態。
public interface TransactionStatus{ boolean isNewTransaction(); // 是不是新的事物 boolean hasSavepoint(); // 是否有恢復點 void setRollbackOnly(); // 設置爲只回滾 boolean isRollbackOnly(); // 是否爲只回滾 boolean isCompleted; // 是否已完成 }
TransactionTemplate tt = new TransactionTemplate(); // 新建一個TransactionTemplate Object result = tt.execute( new TransactionCallback(){ public Object doTransaction(TransactionStatus status){ updateOperation(); return resultOfUpdateOperation(); } }); // 執行execute方法進行事務管理
或者是
TransactionTemplate tt = new TransactionTemplate(); // 新建一個TransactionTemplate Object result = tt.execute( new TransactionCallbackWithoutResult(){ @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { // call dao } }); // 執行execute方法進行事務管理
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定義一個某個框架平臺的TransactionManager,如JDBC、Hibernate dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 設置數據源 DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定義事務屬性 transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 設置傳播行爲屬性 TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 得到事務狀態 try { // 數據庫操做 dataSourceTransactionManager.commit(status);// 提交 } catch (Exception e) { dataSourceTransactionManager.rollback(status);// 回滾 }
Spring配置文件中關於事務配置老是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,不管哪一種配置方式,通常變化的只是代理機制這部分。
DataSource、TransactionManager這兩部分只是會根據數據訪問方式有所變化,好比使用Hibernate進行數據訪問時,DataSource實際爲SessionFactory,TransactionManager的實現爲HibernateTransactionManager。
具體以下圖:
<?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>
<?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="transactionBase" 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="transactionBase" > <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>
<?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>
//todo