spring事務管理

本節要點:java

  •    瞭解事務的概念
  •   瞭解jdbc事務管理
  •   掌握spring事務管理的實現方式
    • 編程式事務管理
    • 聲明式事務管理
  •  瞭解事務的隔離級別和傳播方式

事務的定義:程序員

數據庫系統爲了保證數據操做的完整性和一致性,引入了事務這個重要的概念,所謂事務,就是將一系列的數據庫操做做爲一個總體來執行。如對數據庫存取,就是一組SQL指令,這一組SQL指令必須所有執行成功;若是由於某個緣由(例如其中一行SQL有錯誤),則先前所執行過的SQL指令撤銷。spring

 

Jdbc事務管理sql

在JDBC中,能夠用Connection的setAutoCommit()方法,給定它false參數。在一連串的SQL語句後面,調用Connection的commit()來送出變動。若是中間發生錯誤,則調用rollback()來撤銷全部的執行,例如:數據庫

try{express

    connection.setAutoCommit(false);編程

    …//一連串SQL操做併發

    connection.commit();    //執行成功,提交全部變動oracle

}catch(SQLException e){app

    connection.rollback();   //發生錯誤,撤銷全部變動

}

案例:操做用戶信息

在數據庫新建一個表t_user 幷包含id和name兩個屬性。

User

public class User {
     private int id;
     private String name;
    get/set……
}

 

UserDao

public interface UserDao {
     public String getUser(int id);
     public void updUser(int id);
     public void addUser(User user);
     public void delUser(int id);
}

 

UserDaoImpl

/**
 *    JDBC Connection類的事務控制方法:
      setAutoCommit(boolean autoCommit) 設置是否自動提交事務,默認自動
      commit() 提交事務
      rollback() 撤銷事務  
 * @author Administrator
 *
 */
public class UserDaoImpl implements UserDao {

     public DataSource dataSource;// 數據源
     private Connection conn;// 數據庫鏈接
     /**
      * 設置數據源並根據數據源獲取數據庫的鏈接
      */
     public void setDataSource (DataSource dataSource){
         this.dataSource = dataSource;
         try{
              this.conn = dataSource.getConnection();
         }catch(SQLException e){
              e.printStackTrace();
         }
     }

     @Override
     public String getUser(int id) {
         String name = null;
         try{
              PreparedStatement ps = conn.prepareStatement("select name from " + "t_user where id=?");
              ps.setInt(1, id);
              ResultSet rs = ps.executeQuery();
              if(rs.next()){
                   name = rs.getString(1);
              }
         }catch(SQLException e){
              e.printStackTrace();
         }
         return name;
     }

     @Override
     public void updUser(int id) {
         try{
              PreparedStatement ps = conn.prepareStatement("update t_user set name=? where id=? ");
              ps.setString(1, "25342");
              ps.setInt(2, id);
              ps.executeUpdate();
         }catch(SQLException e){
              e.printStackTrace();
         }
     }

     @Override
     public void addUser(User user) {
         try{
              PreparedStatement ps = conn.prepareStatement("insert into t_user(id,name) values("
                       +user.getId()+","+user.getName()+")");
              ps.executeUpdate();
         }catch(SQLException e){
              e.printStackTrace();
         }
     }

     @Override
     public void delUser(int id) {
         try{
              PreparedStatement ps = conn.prepareStatement("delete from t_user where id=? ");
              ps.setInt(1, id);
              ps.executeUpdate();
         }catch(SQLException e){
              e.printStackTrace();
         }
     }
}

 UserService

public interface UserService {
    public String getUser(int id);
    public void updUser(int id);
    public void addUser(User user);
    public void delUser(int id);
}

 

UserServiceImpl

public class UserServiceImpl implements UserService{

     private UserDao userDao;

     public UserDao getUserDao() {
         return userDao;
     }

     public void setUserDao(UserDao userDao) {
         this.userDao = userDao;
     }

     @Override
     public String getUser(int id) {
         return userDao.getUser(id);
     }
     @Override
     public void updUser(int id) {
         userDao.updUser(id);       
     }
     @Override
     public void addUser(User user) {
         userDao.addUser(user);     
     }
     @Override
     public void delUser(int id) {
         userDao.delUser(id);
     }
}

 Bean.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--數據源配置
    DriverManagerDataSource是spring提供的一個簡單的數據源實現類
    這個類實現了javax.sql.DataSource接口,可是它沒有提供池化鏈接的機制,
    每次調用getConnection()獲取新鏈接時,只是簡單地建立一個新的鏈接。
    所以,這個數據源簡單應用或測試,由於它不須要額外的依賴類
     -->
     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
         <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"></property>
     <property name="username" value="zhou"></property>
     <property name="password" value="123456"></property>
     </bean>
     <bean id="userService" class="com.silvan.service.UserServiceImpl">
     <property name="userDao" ref="userDao"></property>
     </bean>
     <bean id="userDao" class="com.silvan.dao.UserDaoImpl">
     <property name="dataSource" ref="dataSource"></property>
     </bean>
</beans>

 Test

public class Test {
     public static void main(String[] args) throws Exception{
         ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");
         UserService userService=(UserService)applicationContext.getBean("userService");
         User user = new User();
//       user.setId(1);
//       user.setName("1231");
//       userService.addUser(user);
         userService.delUser(1);
     }
}

 

UserDaoImpl中的新增方法修改爲以下,體驗事務的功能:若是事務過程當中出現異常,捕獲異常後對數據進行回滾,能夠保證數據的完整性。

public void addUser(User user) {
         try{
              conn.setAutoCommit(false);
              PreparedStatement ps = conn.prepareStatement("insert into t_user(id,name) values(" +user.getId()+","+user.getName()+")");
              ps.executeUpdate();
              Integer.parseInt("asfdas");
              conn.commit();
         }catch(Exception e){
              e.printStackTrace();
              try {
                   conn.rollback();
              } catch (SQLException e1) {
                   e1.printStackTrace();
              }
         }
     }

 Spring事務概述

Spring框架提供了極其強大而簡便的事務處理功能,其核心即是PlatformTransactionManager抽象接口。Spring將全部的事務管理都抽象爲PlatformTransactionManager、TransactionStatus和TransactionDefinition這3個接口,而不管其底層關聯的具體的事務到底是JDBC事務、JTA事務,仍是ORM框架自定義的事務。

  • PlatformTransactionManager:定義了事務管理器,全部與事務相關的操做都有它管理;
  • TransactionStatus:表明事務自己,它提供了簡單的控制事務執行和查詢事務狀態的方法;
  • TransactionDefinition :定義了事務的規則(事務的隔離級別、傳播行爲、事務超時、只讀狀態), 在啓動事務時,事務管理器會根據TransactionDefinition來啓動合適的事務;

在Spring中實現事務管理有兩種方式,一種是傳統的編程式事務管理,也就是程序員在編寫程序代碼實現事務的管理,具體包括定義事務的開始、在程序異常時進行事務的回滾及程序正常執行後的事務提交。

另外一種則是基於AOP技術實現的聲明式事務管理,事務管理自己是一項共有的系統級服務功能,徹底能夠將事務管理抽象成一個事務切面,程序員再也不關心事務管理的問題,把主要精力放在覈心業務邏輯代碼的編寫上,而後在須要進行事務管理的方法上切入事務切面,使之具備事務管理的功能,達到事務管理的目的。

 

Spring編程式事務

PlatformTransactionManager:

  • 事務管理器接口, 只定義了3個方法:getTransaction()獲取事務的狀態; commit();rollback();
  • 事務管理器的實現類有多種,根據具體的持久層框架的不一樣而不一樣;
    • 實現類: DataSourceTransactionManager,HibernateTransactionManager,JdoTransactionManager等
  • 可使用PlatformTransactionManager直接管理事務。簡單地經過一個bean引用給你的bean傳遞一個你使用的 PlatformTransaction對象。而後,使用TransactionDefinition和TransactionStatus對象就能夠發起、回滾、提交事務。
  •  流程通常以下:
    • 1 .聲明數據源
    •   2 .聲明一個事務管理類,例如 DataSourceTransactionManager, HibernateTransactionManger, JTATransactionManager等
    •   3 .在咱們的代碼中加入事務處理代碼

userDaoImpl

package com.silvan.dao;

import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.silvan.pojo.User;
/**
 *spring編程式事務
 * @author Administrator
 */
public class UserDaoImpl implements UserDao {
     public DataSourceTransactionManager transactionManager;// 事務管理器的實現類,做用如建立事務,管理事務等
     public JdbcTemplate jdbcTemplate;//spring中要使用Jdbctemplate對象來完成jdbc 操做

     public void setTransactionManager(
              DataSourceTransactionManager transactionManager) {
         this.transactionManager = transactionManager;
     }

     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
         this.jdbcTemplate = jdbcTemplate;
     }

     @Override
     public String getUser(int id) {
         String name = null;
         try{
              List<Map<String, Object>> result = jdbcTemplate.queryForList("select name from  t_user where id=?", id);
              if(result.iterator() != null){
                   name = (String) result.get(0).get("name");
              }
         }catch(Exception e){
              e.printStackTrace();
         }
         return name;
     }

     @Override
     public void updUser(int id) {
         try{
              jdbcTemplate.update("update t_user set name=? where id=? ","2544",id);
         }catch(Exception e){
              e.printStackTrace();
         }
     }
     @Override
     public void addUser(User user) {
         TransactionStatus ts=null;
         try{
             //定義事務規則
            TransactionDefinition td=new DefaultTransactionDefinition();
            //根據事務規則,建立一個新的事務或者獲取以前已經建立的事務
            ts=transactionManager.getTransaction(td);
            jdbcTemplate.update("insert into t_user(id,name) values(?,?)",user.getId(),user.getName());
//            Integer.parseInt("asfdas");
              transactionManager.commit(ts);
         }catch(Exception e){
              e.printStackTrace();
              try {
                   transactionManager.rollback(ts);
              }catch (Exception e1) {
                   e1.printStackTrace();
              }
         }
     }
     @Override
     public void delUser(int id) {
         try{
              jdbcTemplate.update("delete from t_user where id=?",id);
         }catch(Exception e){
              e.printStackTrace();
         }
     }
}

 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
     <!-- 建立數據源對象 -->
     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
    <property name="url" value="jdbc:oracle:thin:@localhost:1521:zhouyq"></property>
     <property name="username" value="zhou"></property>
     <property name="password" value="123456"></property>
     </bean>
     <!-- 建立事務管理對象,並注入數據源 -->
     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
     <property name="dataSource" ref="dataSource"></property>
     </bean>
     <!-- 建立jdbc操做對象 -->
     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
      </bean>
     <bean id="userService" class="com.silvan.service.UserServiceImpl">
     <property name="userDao" ref="userDao"></property>
     </bean>
     <!-- 注入jdbc事務操做對象和事務管理對象 -->
     <bean id="userDao" class="com.silvan.dao.UserDaoImpl">
     <property name="jdbcTemplate" ref="jdbcTemplate"></property>
     <property name="transactionManager" ref="transactionManager"></property>
     </bean>
</beans>

 Spring聲明式事務

Spring爲聲明式事務提供了簡單而強大的支持,所謂聲明式事務,是指在Spring的配置文件中使用相應的標籤對事務進行配置,這樣作的好處是Spring能夠幫助咱們管理事務,例如:何時提交事務、何時回滾事務等。

從開發效率與易維護的角度來看,Spring聲明式事務管理是實際開發中比較經常使用的。

基於 <tx> 命名空間的聲明式事務管理:

①   在xml中啓用tx和aop兩個命名空間

xmlns:tx=http://www.springframework.org/schema/tx

xsi:schemaLocation="http://www.springframework.org/schema/tx          http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"

②   在xml中配置通知、切入點以及Advisor

 

修改一下文件:

UserDaoImpl

package com.silvan.dao;

import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;
import com.silvan.pojo.User;
/**
 *spring聲明式事務
 * @author Administrator
 *
 */
public class UserDaoImpl implements UserDao {
     public JdbcTemplate jdbcTemplate;//spring中要使用Jdbctemplate對象來完成jdbc 操做
     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
         this.jdbcTemplate = jdbcTemplate;
     }
     @Override
     public String getUser(int id) {
         String name = null;
         try{
              List<Map<String, Object>> result = jdbcTemplate.queryForList("select name from  t_user where id=?", id);
              if(result.iterator() != null){
                   name = (String) result.get(0).get("name");
              }
         }catch(Exception e){
              e.printStackTrace();
         }
         return name;
     }
     @Override
     public void updUser(int id) {
         try{
              jdbcTemplate.update("update t_user set name=? where id=? ","2544",id);
         }catch(Exception e){
              e.printStackTrace();
         }
     }
     @Override
     public void addUser(User user) {
         try{
              jdbcTemplate.update("insert into t_user(id,name) values(?,?)",user.getId(),user.getName());
              Integer.parseInt("asfdas");
         }catch(Exception e){
              e.printStackTrace();
              throw new RuntimeException();
         }
     }
     @Override
     public void delUser(int id) {
         try{
              jdbcTemplate.update("delete from t_user where id=?",id);
         }catch(Exception e){
              e.printStackTrace();
         }
     }
}

 Bean.xml

<?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:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
     <!-- 建立數據源對象 -->
     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
    <property name="url" value="jdbc:oracle:thin:@localhost:1521:zhouyq"></property>
     <property name="username" value="zhou"></property>
     <property name="password" value="123456"></property>
     </bean>
      <!--      配置JdbcTemplate,若是不用spring的jdbc能夠省略  -->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
      </bean>
      <!--       建立事務管理對象 -->
     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
     <property name="dataSource" ref="dataSource"></property>
     </bean>
     <tx:advice id="testAdvice" transaction-manager="transactionManager">
     <tx:attributes>
          <tx:method name="*" propagation="REQUIRED"/>
     </tx:attributes>
     </tx:advice>
     <!-- 配置切入點和advisor -->
     <aop:config>
     <aop:pointcut expression="execution(* com.silvan.service.*.*(..))" id="pointcut"/>
     <aop:advisor advice-ref="testAdvice" pointcut-ref="pointcut"/>
     </aop:config>
     <bean id="userService" class="com.silvan.service.UserServiceImpl">
     <property name="userDao" ref="userDao"></property>
     </bean>
     <bean id="userDao" class="com.silvan.dao.UserDaoImpl">
     <property name="jdbcTemplate" ref="jdbcTemplate"/>
     </bean>
</beans>

事務回滾:  

默認spring事務只在發生未被捕獲的 runtimeexcetpion時纔回滾。  

 spring aop  異常捕獲原理:被攔截的方法需顯式拋出異常,並不能經任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,默認狀況下aop只捕獲runtimeexception的異常,但能夠經過配置來捕獲特定的異常並回滾,換句話說在service的方法中不使用try catch 或者在catch中最後加上throw new runtimeexcetpion(),這樣程序異常時才能被aop捕獲進而回滾

事務的傳播方式:

事務的傳播行爲用於指定在多個事務方法間調用時,事務是如何在這些方法間傳播的,spring共支持7種傳播行爲。

  • PROPAGATION_REQUIRED

     表示業務邏輯方法須要在一個事務中運行,若是該方法在運行時,已經處在一個事務中,則直接加入到該事務中,不然本身建立一個新的事務。即:若是存在一個事務,則支持當前事務。若是沒有事務則開啓。(在實際開發中經常使用該傳播方式。)

  • PROPAGATION_SUPPORTS

      表示業務邏輯方法若是在某個事務範圍內被調用,則該方法直接成爲當前事務的一部分。若是該方法在事務範圍外被調用,則該方法在無事務的環境下執行。即:若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行。

  • PROPAGATION_MANDATORY

      表示業務邏輯方法只能在一個已經存在的事務中執行,該方法不能建立本身的事務,若是該方法在沒有事務的環境下被調用,容器就會拋出事務不存在的異常。 即:若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。

  • PROPAGATION_REQUIRES_NEW

      表示無論當前是否有事務存在,該業務邏輯方法都會爲本身建立一個全新的事務。若是該方法已經運行在一個事務中,則原有事務會被掛起,新的事務會被建立,直到該方法執行結束後新事務纔算結束,原先的事務再恢復執行。即: 老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起,新事務運行完畢後,再接着運行被掛起的事務。

  • PROPAGATION_NOT_SUPPORTED

      表示業務邏輯方法不須要事務。若是該方法目前沒有關聯到某個事務,容器不會爲它建立事務。若是該方法在一個事務中被調用,則該事務會被掛起,在方法調用結束後原先的事務纔會恢復執行。即:老是非事務地執行,並掛起任何存在的事務,當前方法運行完畢後,被掛起的事務才恢復執行。

  • PROPAGATION_NEVER

      表示業務邏輯方法絕對不能在事務範圍內執行。若是該方法在某個事務中執行,容器會拋出異常,只有沒有關聯到任何事務時該方法才能正常執行。即:老是非事務地執行,若是存在一個活動事務,則拋出異常

  • PROPAGATION_NESTED

      表示若是一個活動的事務存在,業務邏輯方法則運行在一個嵌套的事務中,若是沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點,內部事務的回滾不會對外部事務形成影響,它只對DataSourceTransactionManager事務管理器生效。即:若是一個活動的事務存在,則運行在一個嵌套的事務中。若是沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。

事務傳播特性:

對基於方法級別的事務管理而言,方法開始執行時建立事務,方法運行過程當中若出現異常則進行事務回滾,方法若是正常執行完成則進行事務的提交。於是事務管理的主要任務就是事務的建立、事務的回滾與事務的提交,其中是否須要建立事務及如何建立事務時由事務傳播行爲控制的,一般數據的讀取時不須要事務管理的,或者也可爲其指定只讀事務,而對於插入、修改與刪除數據的方法來講,就有必要進行事務管理了,在未指定事務傳播行爲時,Spring2.x將啓用默認的REQUIRED。

Spring中事務隔離級別

  • ISOLATION_DEFAULT

    這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.

如下四個與JDBC的隔離級別相對應

  •  ISOLATION_READ_UNCOMMITTED

     這是事務最低的隔離級別,它充許令外一個事務能夠看到這個事務未提交的數據。這種隔離級別會產生髒讀、不可重複讀和幻像讀。

  •  ISOLATION_READ_COMMITTED

     保證一個事務修改的數據提交後才能被另一個事務讀取。另一個事務不能讀取該事務未提交的數據。

  • ISOLATION_REPEATABLE_READ

    這種事務隔離級別能夠防止髒讀,不可重複讀。可是可能出現幻像讀。它除了保證一個事務不能讀取另外一個事務未提交的數據外,還保證了避免下面的狀況產生,即:不可重複讀。

  •  ISOLATION_SERIALIZABLE

    這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀、不可重複讀外,還避免了幻像讀。可是併發性最差。

相關文章
相關標籤/搜索