Spring事務管理

spring是支持事務管理的,這樣就不用直接在sql中使用begin transaction這樣開始事務的命令了,spring幫你集成了
spring有兩種事務,一種是編程式事務,即通過java代碼來進行事務管理,一種是聲明式事務,使用xml配置或者註解即可。
spring的事務接口是PlatFormTransationManager,實現類DataSourceTransactionManager

編程式事務。

(一)、使用jdbcTemplate進行事務管理
jdbcTemplate是spring中個一個數據庫操做類,你可以理解爲jdbc那種,只是好一點,稍微封裝了一下,類似ibatis的sqlmapclient

1.首先引入事務處理類,可以配置在spring的配置xml,也可以配置在數據庫相關的xml中

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
    <property name="dataSource" ref="dataSource"/> 
</bean>

其中datasource就是數據庫的一些連接屬性,如下:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 
  <property name="driverClassName"> 
    <value>com.mysql.jdbc.Driver</value> 
  </property> 
  <property name="username"> 
    <value>root</value> 
  </property> 
  <property name="password"> 
    <value>root</value> 
  </property> 
  <property name="url"> 
    <value>jdbc:mysql://localhost:3306/test</value> 
  </property> 
</bean> 

2.java類中操作,測試事務回滾,這裏故意設置teacher表的name不能爲空,讓其報錯,從而測試是否回滾,執行事務了

ApplicationContext ctx=new ClassPathXmlApplicationContext("helloworld.xml");
          DefaultTransactionDefinition dtd=new DefaultTransactionDefinition();
          dtd.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); //設置事務隔離級別
          dtd.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);         
          //獲取spring事務處理類
          PlatformTransactionManager tx=(PlatformTransactionManager) ctx.getBean("transactionManager");
          TransactionStatus status=tx.getTransaction(dtd);
          //獲取數據源
          DataSource  dataSource = (DataSource) ctx.getBean("dataSource"); 
          JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);                   
          try{
              //執行sql,sql的執行可以換成使用ibatis,mybatis等框架
              jdbcTemplate.execute(sql);
              //如果執行sql,因爲name不能爲空,會報錯,然後就回滾。
              jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
              tx.commit(status);
              System.out.println("提交數據成功,狀態--"+status);
          }catch(Exception e){
              tx.rollback(status);
              System.out.println("數據回滾,狀態--"+status);
          }

這裏要注意,由於是使用單元測試類執行的,故需要用ClassPathXmlApplicationContext來獲取需要的這些bean,其實如果是啓動項目了,可以直接使用注入的方式。
其中真正負責回滾和提交的是spring自帶的transactionManager類,jdbcTemplate只不過負責執行sql,可以替換爲ibatis或者其他的ORM框架。

3.需要測試的sql,以下沒有說明,那麼sql,sql2也是指這兩個

private String sql="insert into student (name,sex,age,address,mobile) "
              + "values ('lisi','F',22,'AAAABCCC',158)";
     private String sql2="insert into teacher (name,sex,age,address,mobile,classid) "
              + "values (?,?,?,?,?,?)";

(二)、tranactionTemplate事務管理
在spring中,還可以使用tranactionTemplate進行事務管理,上面那個太麻煩了,commit,rollback要自己來寫,自己控制。tranactionTemplate就簡單多了,你只需要負責業務操作,就是執行sql那部分,其他框架幫你
1、配置差不多,都是一個datasource,一個transactionManager,這裏不重複了
2、java中調用,同樣是在單元測試類中

//獲取數據源
TransactionTemplate template=new TransactionTemplate(tx);     
//事務隔離級別       
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
//事務傳播行爲
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
//帶返回對象方法
template.execute(new TransactionCallback() {
       public Object doInTransaction(TransactionStatus arg0) {
          //此處使用jdbcTemplate來執行sql,可以替換爲其他方法,與事務無關
          jdbcTemplate.execute(sql);
          //如果執行sql,因爲name不能爲空,會報錯,然後就回滾。
          jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
          return null;
       }
 });

其中tx就是上面配置的spring的transactionManager,你只需要負責在doInTransaction中執行你的業務邏輯

3、還有一個不帶反回參數的execute方法,如下:

template.execute(new TransactionCallbackWithoutResult() {              
//無返回對象事務方法,出錯自動回滾,無需手動使用commit,rollback等
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
          //此處使用jdbcTemplate來執行sql,可以替換爲其他方法,與事務無關
          jdbcTemplate.execute(sql);
          //如果執行sql,因爲name不能爲空,會報錯,然後就回滾。
          jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
       }
  });

一般如果使用編程式事務,都是使用tranactionTemplate

聲明式(配置式)事務

編程式事務還是對代碼要修改,有侵入性,spring有更高級的,就是聲明式事務,無需代碼,只要配置
1.xml中配置如下,使用事務標籤

<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
    <tx:attributes> 
    <!-- 這個name是指要切入的方法,以insert開頭的都切入事務 -->
        <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED"/> 
    </tx:attributes> 
</tx:advice>

2.aop配置,因爲這個就是用到了面向切面編程,故需要aop

<aop:config> 
    <aop:pointcut id="serviceMethod" expression="execution(* com.xxxx.dao.TransactionDao.insert(..))"/> 
    <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/> 
</aop:config>

pointcut表示切入點,這裏表示TransactionDao類,以insert開頭的方法都會加入事務,而不是insert的sql語句,不要混淆。
3.這個是被切入的那個類,負責數據庫操作(業務操作)

<bean id="transaction" class="com.luohw.dao.TransactionDao">
  <property name="dataSource" ref="dataSource"></property>
</bean>

4.業務邏輯:

public void insert(){
          //在沒有聲明式事務前,sql執行正常,數據庫有數據,sql2失敗,沒有插入
          jdbcTemplate = new JdbcTemplate(dataSource);
          jdbcTemplate.execute(sql);
          jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
     }

其中,由於用到tx事務標籤,aop標籤,配置文件開頭需要引入以下東西:

<?xml version="1.0" encoding="UTF-8"?> 
<beans   
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop
                    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                    http://www.springframework.org/schema/tx
                    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> 

注意,這是指多的部分,之前還有spring必須的一個dtd和shema要引入。
這樣對於業務代碼,沒有一點的侵入,完全只要配置就可以了,使用aop編程,spirng會自動動態代理,類似組合成一個新的方法,先開啓事務,再執行你的sql。

使用註解注入事務,也是聲明式的一種,更簡潔好用。因爲用aop,有可能返回太大,某個類,或者某個包所有的方法都加入了事務,有好處,一次操作多個,也有壞處,有些不需要事務的被你誤殺。

註解的比較好控制範圍,可以在方法,或者類上註解,那麼就表示對應範圍的都加了事務控制了。
1.在配置文件中,加入這個標籤,表示支持事務註解

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

2.在需要加入事務控制的代碼處,加入這個,此處以方法爲例

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
     public void insert2(){
          jdbcTemplate = new JdbcTemplate(dataSource);
          jdbcTemplate.execute(sql);
          jdbcTemplate.update(sql2,new Object[]{null,'M',1,"abc",151,111});
     }

看,是否非常簡單。。。。

xml聲明式事務,有傳播行爲(propagation),有以下值:
1.required——表示方法必須運行在事務中,如果沒有,會創建一個新的事務,如果有,加入到這個事務中
2.mandatory——表示方法必須在事務中,如果沒有,拋出異常
3.nested——如果存在事務,那麼方法會在嵌套事務中運行?其他與required一樣
4.never——表示方法不在事務中運行,如果有事務,那麼拋出異常
5.not_supported——表示方法不在事務中運行,如果有事務,那麼將被掛起(暫停),直到方法完成
6.required_new——表示方法必須在新的事務中運行, 如果當前存在事務,把當前事務掛起
7.supports——表示支持事務,如果有,也可以在事務中運行,也可以沒有

傳播行爲用於確定方法嵌套方法時的事務調用情況。比如方法A調用方法B,兩個方法都使用了註解事務。如果爲required(一般都是這個),那麼會把A,B合併到一起作爲一個事務。如果是其他,看上面配置。

事務隔離級別(isolation)
1.read_uncommited——不提交讀,未提交的數據,其他事務可以讀,會出現髒讀。比如B改了未提交,恰好A在事務中讀,那麼這個就是髒數據(A讀了B修改前的數據)
2.read_commited——提交讀,提交後的數據,其他事務才能讀。會發生幻讀和不可重複讀
不可重複讀, 一 個事務對同一行數據重複讀取兩次但是卻得到了不同結果(有其他事務數據修改了)
幻讀,事務在操作過程中進行兩次查詢,第二次查詢結果包含了第一次查詢中未出現的數據(有其他事務數據插入了)
3.repeatable_read——可重複讀,對於同一字段多次讀取結果是一致的,除非數據被本身事務修改,會發生幻讀。
4.serializable——序列化,最高級別(性能最差),什麼問題讀都不會發生

隔離級別 更新丟失 髒讀取 不可重複讀取 幻讀
未提交讀取 N Y Y Y
提交讀取 N N Y Y
可重複讀取 N N N Y
串行 N N N N

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章