Spring的事務管理

一、事務的基礎知識

  1. 數據庫事務:複雜的事務要分步執行,要麼整體生效、要麼整體失效。

  2. 必須滿足:原子性、一致性、隔離性、持久性。

  3. 數據併發問題:髒讀:A讀取了B未提交的更改數據。

             不可重複讀:A兩次讀,第二次讀到了B已經提交的數據。(行級鎖)

             幻讀(虛讀):A讀取B新提交的新增數據。(需添加表級鎖)

             第一類丟失更新:A撤銷時恢復原數據把B提交的數據覆蓋了。

             第二類丟失更新:A提交時覆蓋了B已經提交的數據。

  4. 數據庫鎖機制:一般分爲表鎖和行鎖,按併發來分有共享鎖和獨佔鎖。數據庫必須在更改的行上施加獨佔鎖;行共享鎖、行獨佔鎖、表共享鎖、表共享行獨佔鎖、表獨佔鎖。

  5. 事務隔離級別:

787876-20160313202333460-377269897.png


6.JDBC對事務的支持


二、ThreadLocal

  1. 概念:ThreadLocal是保存線程本地化的容器,爲每個使用該變量的線程分配一個獨立的變量副本。

  2. 原理:通過Map來保存每個線程的變量副本,key爲線程對象,值爲線程的副本。

public class TestNum {  
    // ①通過匿名內部類覆蓋ThreadLocal的initialValue()方法,指定初始值  
    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {  
        public Integer initialValue() {  
            return 0;  
        }  
    };  
  
    // ②獲取下一個序列值  
    public int getNextNum() {  
        seqNum.set(seqNum.get() + 1);  
        return seqNum.get();  
    }  
  
    public static void main(String[] args) {  
        TestNum sn = new TestNum();  
        // ③ 3個線程共享sn,各自產生序列號  
        TestClient t1 = new TestClient(sn);  
        TestClient t2 = new TestClient(sn);  
        TestClient t3 = new TestClient(sn);  
        t1.start();  
        t2.start();  
        t3.start();  
    }  
  
    private static class TestClient extends Thread {  
        private TestNum sn;  
  
        public TestClient(TestNum sn) {  
            this.sn = sn;  
        }  
  
        public void run() {  
            for (int i = 0; i < 3; i++) {  
                // ④每個線程打出3個序列值  
                System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn["  
                         + sn.getNextNum() + "]");  
            }  
        }  
    }  
}

在spring中大部分Bean都可以聲明爲singleton,故而spring要對這些非線程安全bean採用Threadlocal進行封裝,這樣有狀態的bean就能夠以sigleton的方式在多線程中正常工作。spring通過Threadlocal來實現事務管理


三、spring對事務的支持

  1. spring封裝了事務模版類TranscationTemplate.


  2. 事務管理主要有3個接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus.

TransactionDefinition:用於描述事務的隔離級別、超時時間、等事務屬性;

PlatformTransactionManager根據TransactionDefinition提供的事務屬性來創建事務;就三個方法getTransaction、commit、rollback;

TransactionStatus描述激活事務的狀態;


3.spring把事務管理委託給底層具體的持久化實現框架來完成,爲不同的持久化框架提供了不同的PlatformTransactionManager接口的實現類。

<bean id="dataSource"    
      class="org.apache.commons.dbcp.BasicDataSource"
      destroy-method="close"
      p:driverClassName="${jdbc.driverClassName}"
      p:url="${jdbc.url}"
      p:username="${jdbc.username}"
      p:password="${jdbc.password}"/>
<bean id="transactionManager"              2)基於數據源的事務管理器
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      p:dataSource-ref="dataSource"/>      3)引用數據源

Hibernate

1)Hibernate配置

<hibernate-configuration>
    <session-factory>
        <!-- 指定連接數據庫所用的驅動 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <!-- 指定連接數據庫的url,其中hibernate是本應用連接的數據庫名 -->
        <property name="connection.url">jdbc:mysql://localhost/hibernate_test</property>
        <!-- 指定連接數據庫的用戶名 -->
        <property name="connection.username">root</property>
        <!-- 指定連接數據庫的密碼 -->
        <property name="connection.password">cheng</property>
        <!-- 指定連接池裏最大連接數 -->
        <property name="hibernate.c3p0.max_size">20</property>
        <!-- 指定連接池裏最小連接數 -->
        <property name="hibernate.c3p0.min_size">1</property>
        <!-- 指定連接池裏連接的超時時長 -->
        <property name="hibernate.c3p0.timeout">5000</property>
        <!-- 指定連接池裏最大緩存多少個Statement對象 -->
        <property name="hibernate.c3p0.max_statements">100</property>
        <property name="hibernate.c3p0.idle_test_period">3000</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>
        <property name="hibernate.c3p0.validate">true</property>
        <!-- 指定數據庫方言 -->
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
        <!-- 根據需要自動創建數據表 -->
        <property name="hbm2ddl.auto">update</property><!--①-->
        <!-- 顯示Hibernate持久化操作所生成的SQL -->
        <property name="show_sql">true</property>
        <!-- 將SQL腳本進行格式化後再輸出 -->
        <property name="hibernate.format_sql">true</property>
        <!-- 避免這個錯誤信息Disabling contextual LOB creation as createClob() method threw error :java.lang.reflect.InvocationTargetException -->
        <property name="hibernate.temp.use_jdbc_metadata_defaults">false</property>
        <!-- 羅列所有持久化類的類名 -->
        <mapping class="com.wechat.entity.po.User"/>
        <mapping class="com.wechat.entity.po.Person"/>
    </session-factory></hibernate-configuration>

hibernate.cfg.xml文件的主要作用就是配置了一個session-factory

  1. 在session-factory中主要通過property配置一些數據庫的連接信息,我們知道,spring通常會將這種數據庫連接用dataSource來表示,這樣一來,hibernate.cfg.xml文件中的所有跟數據庫連接的都可以幹掉了,直接用spring的dataSource,而dataSource也可以用c3p0、dbcp等。

  2. 在session-factory中通過property除了配置一些數據庫的連接信息之外,還有一些hibernate的配置,比如方言、自動創建表機制、格式化sql等,這些信息也需要配置起來。

  3. 還有最關鍵的一個持久化類所在路徑的配置

2)spring的sessionFactroy配置

<!-- 加載配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"
        file-encoding="utf-8" ignore-unresolvable="true" />

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan">
            <list>
                <!-- 可以加多個包 -->
                <value>com.wechat.entity.po</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>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
            </props>
        </property>
    </bean>


四、聲明事務

1.基於aop/tx命名空間的配置:spring在Schema的配置中添加了一個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:p="http://www.springframework.org/schema/p" 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-4.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <import resource="classpath:applicationContext-dao.xml"/>

    <!--配置事務管理器-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
    <!--使用切點表達式定義目標方法-->
    <aop:config>
        <!--定義切面-->
        <aop:pointcut id="serviceMethod"
                      expression="execution(* com.smart.service.*Forum.*(..))"/>
        <!--引用事務增強-->
        <aop:advisor pointcut-ref="serviceMethod"
                     advice-ref="txAdvice"/>
    </aop:config>
    <!--定義事務增強-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="false"/>
            <tx:method name="add*" rollback-for="PessimisticLockingFailureException"/>
            <tx:method name="update*"/>
        </tx:attributes>
    </tx:advice>
    <!--
        1.引入tx命名空間的聲明
        2.配置事務管理器
        3.定義事務增強:事務增強一定需要一個事務管理器的支持,<tx:advice>通過transaction-manager屬性引用之前定義的事務管理器,默認查找名爲transactionManager
                       所以如果事務管理器命名爲transactionManager,可以不指定transaction-manager屬性.
        4.使用AOP
    -->
    <!--tx:method的屬性
        name 就name是必須的屬性 與事務屬性關聯的方法名 get* handle*等自己起的名字
        isolation 事務隔離級別
        timeout 默認爲-1 超時時間
        read-only 默認false 事務是否只讀
        rollback-for 默認所有運行期間異常都滾回 
        no-rollback-for 默認所有檢查型異常都不滾回
    -->

2.使用註解配置聲明式事務

1)在xml中配置

<bean id="txManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      p:dataSource-ref="dataSource"/>

<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
<!--同上,如果名字是transactionManager 可以省略
    proxy-target-class="true"表示spring會通過創建子類來代理業務類
-->

2)在業務類上註解

@Service
@Transactional
public class BbtForum {
    public ForumDao forumDao;

    public TopicDao topicDao;

    public PostDao postDao;
    ...}

3)@Transactional的屬性

默認屬性:

事務傳播行爲:PROPAGATION_REQUIRED

事務隔離級別:IOSLATION_DEFAULT

讀寫事務屬性:讀/寫事務

超時時間:-1

回滾設置:熱河運行期異常引發回滾、任何檢查型異常不會引發回滾


4)spring要在具體的業務類上使用@Transactional註解


5)在方法處使用註解會覆蓋類定義的註解,如果方法需要使用特殊的事務屬性,可以在方法上使用註解

@Transactional(readOnly = true)
public Forum getForum(int forumId) {
    return forumDao.getForum(forumId);
}


五、事務的一些注意點

1.使用不同的事務管理器

@Transactional("name")使用名爲name的事務管理器


2.事務管理的目的是保證數據操作的事務性(原子性、一致性、隔離性、持久性)脫離了事務,DAO一樣可以進行數據操作。


3.事務的傳播性

PROPAGATION_REQUIERD:如果當前沒有事務就創建一個,有就加進去

PROPAGATION_SUPPORTS:支持當前事務,沒有就以非事務方式執行

PROPAGATION_MANDATORY:使用當前事務,沒有就拋出異常

PROPAGATION_REQURES_NEW:新建事務,如果當前存在事務,就將其掛起

PROPAGATION_NOT_SUPPORTED:以非事務方式執行,如果當前有就掛起

PROPAGATION_NEVER:以非事務方式執行,如果有就掛起

PROPAGATION_NESTED:嵌套事務

相同線程中進行互相嵌套調用的事務方法工作在相同的事務中,如果在不同線程,則不同線程下事務方法工作在獨立的事務中。






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