Spring 事務配置管理,簡單易懂,詳細 [聲明式]

Spring 事務配置說明java

Spring 若是沒有特殊說明,通常指是跟數據存儲有關的數據操做事務操做;對於數據持久操做的事務配置,通常有三個對象,數據源,事務管理器,以及事務代理機制;mysql

Spring 提供了多種的底層數據源實現,以及多種類型的事務管理器;全部的管理器都基於 PlatformTransactionManager 接口實現各自的事務策略;正則表達式

Spring 事務管理採用 AOP 切面代理技術實現,AOP 用於分隔關注點,保證事務的原子性,採用必定的技術 把該關注點 (weaving) 織入到 待完善的關注點上,實現單獨組件沒法實現的功能,以解決面向對象編程在某些方式下難於實現的操做,更好的支持面向對象的開關原則(擴展開放,修改關閉)。spring

底層數據源配置sql

首選加載 數據源配置 .properties 文件;數據庫

<bean id="loadProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:META-INF/mybatis/mysql.properties</value>
                <value>classpath:META-INF/spring/hibernate.properties</value>
            </list>
        </property>
</bean>

mysql.properties:express

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/springdb
username=root
password=xxxxx
filters=stat
initialSize=2
maxActive=300
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200

hibernate.properties:apache

# hibernate.X
hibernate.connection.driverClass=org.gjt.mm.mysql.Driver
hibernate.connection.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&amp;characterEncoding=utf-8
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.connection.username=root
hibernate.connection.password=xxxxx
hibernate.show_sql=true
hibernate.hbm2ddl.auto=create-drop

 

1. JDBC 方式:編程

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
</bean>

2. c3p0 方式:服務器

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定鏈接數據庫的驅動 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定鏈接數據庫的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/springdb"/>
        <!-- 指定鏈接數據庫的用戶名 -->
        <property name="user" value="root"/>
        <!-- 指定鏈接數據庫的密碼 -->
        <property name="password" value="xxxxx"/>
        <!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
        <property name="maxIdleTime" value="20"/>
</bean>

3. dbcp 方式:

<beans>
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="${driver}"></property>
   <property name="url" value="${url}"></property>
   <property name="username" value="${username}"></property>
   <property name="password" value="${password}"></property>
</bean>

4. Alibaba Druid 方式:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init" >
        <property name="url" value="${url}?useUnicode=true&amp;characterEncoding=utf-8"></property>
        <property name="driverClassName" value="${driver}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
        <property name="filters" value="${filters}"></property>
        <property name="maxActive" value="${maxActive}"></property>
        <property name="maxWait" value="${maxWait}"></property>
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"></property>
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"></property>
        <property name="validationQuery" value="${validationQuery}"></property>
        <property name="testWhileIdle" value="${testWhileIdle}"></property>
        <property name="testOnBorrow" value="${testOnBorrow}"></property>
        <property name="testOnReturn" value="${testOnReturn}"></property>
        <property name="poolPreparedStatements" value="${poolPreparedStatements}"></property>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}"></property>
</bean>

5. JNDI 全局配置方式:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">      
     <property name="jndiName" value="${jndiName}"></property> 
</bean>

6. 自定義 DataSource:

<bean id="dataSource" class="me.study.hnmapper.utils.CustomDataSource">
   <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property>
   <property name="driverUrl" value="jdbc:oracle:thin:@10.30.2.204:1527:sec"></property>
   <property name="username" value="apps"></property>
   <property name="password" value="secapp29"></property>
</bean>

CustomDataSource:

package me.study.hnmapper.utils;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

import javax.sql.DataSource;

public class CustomDataSource implements DataSource {

    private String driverClass;
    private String driverUrl;
    private String username;
    private String password;

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Connection getConnection() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        // TODO Auto-generated method stub
        // TODO Auto-generated method stub
        Connection conn = null;
        try {
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        conn = DriverManager.getConnection(driverUrl, username, password);

        return conn;
    }

    public String getDriverClass() {
        return driverClass;
    }

    public void setDriverClass(String driverClass) {
        this.driverClass = driverClass;
    }

    public String getDriverUrl() {
        return driverUrl;
    }

    public void setDriverUrl(String driverUrl) {
        this.driverUrl = driverUrl;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

}

6. Hibernate方式,利用的是SessionFactory做爲數據源操做;

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingLocations" >
            <list>
                <value>classpath*:model/*.hbm.xml</value>
            </list>
        </property>
        <!-- packagesToScan能夠自動搜索某個package的所有標記@Entity class -->
        <!-- 
        <property name="packagesToScan">
            <list>
                <value>hibernatelibs.model*</value>
            </list>
        </property>
         -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            </props>
        </property>
</bean>

TransactionManager 事務管理器配置

1. JDBC TransactionManager 配置;

<bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager時須要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
</bean>

2. hibernate TransactionManager 配置:

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
</bean>

3. Jta TransactionManager 配置: 

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

Jta 在應用服務器依賴於 JNDI 查找,也可基於 (ObjectWeb)JOTM 或 Atomikos 這樣的 userTransaction 來實現。

PlatformTransactionManager 是 Spring 事務策略核心的接口,Spring 並不支持事務的實現,而是負責包裝底層的事務,負責分離各類基於PlatformTransaction接口的具體實現。
應用底層支持什麼樣的事務策略,Spring 就支持什麼樣的事務策略。
Spring 聲明式事務管理是基本 AOP 切面代理技術實現,是基本基於XML 或 Annotation 配置的一種實現,PlatformTransaction 爲各類管理機制提供統一操做接口,各類類型的管理機制都得基於 PlatformTransaction 接口實現具體的事務處理,所以,Spring 事務配置能夠很容易的在各類類型的事務管理機制間切換。

PlatformTransaction 提供的接口以下:

public interface PlatformTransactionManager {  
       TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;  
       void commit(TransactionStatus status) throws TransactionException;  
       void rollback(TransactionStatus status) throws TransactionException;  
} 

 

比較經常使用的事務機制還有:(Jdo)JdoTransactionManager,(Jpa)JpaTransactionManager。此外還有:WebSphereUowTransactionManager、WebLogicJtaTransactionManager等類型;

事務代理機制

1. 使用 pointcut 通知方式 (最多見的方式):

 首先配置 tx 命名空間:

<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">

配置通知項 <tx:advice

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"  /> <!-- 匹配到類下方法的切入點, 好比 query* 表示匹配全部以 query開頭的方法 , 這裏能夠配置事務行爲 傳播行爲propagation, 隔離級別isolation, 超時(秒)timeout_xx, 自讀read-only 等 -->
        </tx:attributes>
</tx:advice>

<tx:method 配置項:

name: 用於匹配類中的方法,spring 只支持 類下的方法的形式,對於模式匹配,能夠支持 perl 的正則表達式,也支持 aspectj ,默認支持 AspectJ;

<tx:method name="*" ... /> <!-- 匹配全部方法 -->
<tx:method name="get*" ... /> <!-- 匹配全部以 get 開頭的方法名 -->
<tx:method name="save*" ... /> <!-- 匹配全部以 save 開頭的方法名 -->
...

isolation: 

一、Serializable:最嚴格的級別,事務串行執行,資源消耗最大;

二、REPEATABLE_READ:保證了一個事務不會修改已經由另外一個事務讀取但未提交(回滾)的數據。避免了「髒讀取」和「不可重複讀取」的狀況,可是帶來了更多的性能損失。

三、READ_COMMITTED:大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另外一個並行事務已修改但未提交的數據,避免了「髒讀取」。該級別適用於大多數系統。

四、Read_Uncommitted:保證了讀取過程當中不會讀取到非法數據。隔離級別在於處理多事務的併發問題。

五、Default 默認。

propagation:  Spring 支持七種傳播行爲,比 EJB 的 CMT 還多一種;

1. Required=>PROPAGATION_REQUIRED: 有邏輯事務就加入執行,沒有就建立一個新的;
2. RequiresNew=>PROPAGATION_REQUIRES_NEW: 表示每次都會建立一個新事務,事務間不互相影響;
3. Supports=>PROPAGATION_SUPPORTS: 有邏輯事務就加入,沒有就以非事務方式執行;
4. NotSupported=>PROPAGATION_NOT_SUPPORTED: 不支持事務,有事務時,暫停,以非事務方式執行;
5. Mandatory=>PROPAGATION_MANDATORY: 以事務方式執行,沒有事務時,拋出異常;
6. Never=>PROPAGATION_NEVER: 不支持事務,有事務時,拋出異常;
7. Nested=>PROPAGATION_NESTED: 有事務時,嵌入事務內執行,沒有時建立新的事務;(內部事務異常不影響外部事務,外部事務異常會影響到內部的事務)

timeout="5" 或 timeout_xx: 好比: timeout_5; 表示超時 5 秒;

完整的一個 tx:method:

<tx:method propagation="REQUIRED" isolation="REPEATABLE_READ" timeout="5" read-only="true" />

另一種配置方式,是配置在具體bean內的:

<bean id="xxxxDao">
    <!-- ... -->
    <property name="transactionAttributes">  
            <props>  
                <prop key="*">PROPAGATION_REQUIRED,REPEATABLE_READ,timeout_5,readOnly</prop>
            </props>  
    </property>
</bean>

配置 aop:config :

<aop:config>
        <!-- 表示在全部切入點加註事務管理 -->
        <aop:pointcut expression="execution(* springlibs.service.*.*(..))" id="puintCutid" /> <!-- 須要配置 component-scan -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="puintCutid" />
</aop:config>

expression表達式 : execution(* springlibs.service.*.*(..))

第一個 * : 表示全部的返回值類型;

第二個 * : 表示全部的類;

第三個 * : 表示全部的方法;

切入點表達式例子:

1>. execution(* springlibs.service.*.*(..)) 表示 springlibs.service 下全部的類跟全部的方法;

2>. execution(* springlibs.service.*.save*(..)) 表示 springlibs.service 下全部的以 save 開頭的方法;

3>. execution(public * springlibs.service.*.*(..)) 表示 springlibs.service 下全部的公共方法;

pointcut 表示的是一些鏈接點的集合,使用 expression 來表達這種集合;advisor 這裏就當作鏈接 pointcut切入點 與 advice通知 的做用;

2. 一個 bean 對應一個事務;

配置 dao 實現 bean:

<bean id="xxxDaotarget" class="springlibs.dao.xxxDaoImpl">
    <!-- 
    property name="sessionFactory" ref="sessionFactory" />
    -->
</bean>

在 Dao 上配置事務:

<bean id="xxxDao"  
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
           <!-- 配置事務管理器 -->  
           <property name="transactionManager" ref="transactionManager" />     
        <property name="target" ref="xxxDaotarget" />  
        <!-- 配置事務屬性 -->  
        <property name="transactionAttributes">  
            <props>  
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>  
        </property>  
</bean>  

3. 全部 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="xxxDao" parent="transactionBase">
        <property name="target">
            <bean id="xxxDaoImpl" class="springlibs.dao.xxxDaoImpl"></bean>
        </property>
</bean>

4. 使用攔截器:

<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 上配置事務;

<!-- 配置DAO實現 -->
<bean id="xxxDao" class="springlibs.dao.xxxDaoImpl">
        <!--
        <property name="sessionFactory" ref="sessionFactory" />
        -->
</bean>

5. 使用 Annotation 方式:

<tx:annotation-driven transaction-manager="transactionManager"  proxy-target-class="true"/>

proxy-target-class 爲 true: 使用 CGLib 建立增長的代理;爲 false 就是使用 標準 JDK 將會被建立;

能夠在 類級別或方法 上使用 @Transaction 注入;

加註 @Transaction 的類或方法,自己並無事務行爲,能被識別爲事務的,實際上是 tx:annotation-driven 的配置開啓了事務;

相關文章
相關標籤/搜索