1. 程式人生 > >MySQL事務及Spring事務管理

MySQL事務及Spring事務管理

dao層 映射文件 cit .sql classpath cat 不能 插入 nested

事務,是在數據庫中用於保證數據正確性的一種機制,涉及到很多概念以及不同的情況,這裏做一個總結

相關概念

事務四特性(ACID)

原子性(Atomicity,或稱不可分割性):要麽全部完成或者全部不完成,錯誤是會發生回滾,這個要求兩條相關的操作,就像情侶一樣,要麽一起海誓山盟,一個發生意外,另外一個也掛了

一致性(Consistency):操作完成後和原本想的結果一樣,不能對數據完整性造成破壞

隔離性(Isolation,又稱獨立性):可以同時開啟多個事務,為避免出現錯誤,針對這個問題制定了事務在處理時對數據進行不同隔離

持久性(Durability):事務操作後數據永久存儲數據庫。

事務並發問題

臟讀:事務A讀取了事務B更新的數據,然後B回滾操作,那麽A讀取到的數據是臟數據,數據是錯誤的就是臟數據

不可重復讀:事務A多次讀取同一數據,事務B在事務A多次讀取的過程中,對數據作了更新並提交,導致事務A多次讀取同一數據時,結果不一致。

幻讀:一個事務進行數據處理,正在處理時插入一條記錄,原本事務處理後發現一條記錄沒有更改,如同發生幻覺一般。

spring事務機制

spring針對事務進行一系列的處理

事務隔離級別

在多個事務進行處理的時候鎖住數據,保證統一時間只有一個事務對數據進行處理,針對不同的情況有不同的鎖

DEFAULT(默認值):使用底層數據庫事務隔離級別,MySql中使用select @@tx_isolation可以查詢當前隔離級別

READ_COMMITTED:讀已提交,讀到已經提交的數據,可以防止臟讀,但是對不可重復讀和幻讀

READ_UNCOMMITTED:讀未提交,可以讀取沒有提交的數據,較少用

REPEATABLE_READ:重復讀取,讀出去後自動加鎖,其他事務不能修改,解決臟讀,不可重復讀

SERIALIZABLE:串行化,事務一個排一個執行,一個事務執行完成後執行下一個

事務傳播機制

在服務中進行數據庫操作,兩個操作中事務如何管理

REQUIRED(默認值):如果當前沒有事務開啟一個事務,有的話自動加入到這個事務中

REQUIRES_NEW :針對被調用者,不管調用者是否存在事務,被調用者創建一個新事務。

MANDATORY:使用當前的事務,如果當前沒有事務,就拋出異常。

NESTED:如果存在事務,嵌套事務中執行,如果沒有則新建

SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行。

NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。

代碼實現

1),這裏是基於註解的方式使用事務,使用之前引入aop,tx,context約束
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.2.xsd

2),配置事務管理器

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

3),開啟事務註解

<tx:annotation-driven/>

 <!-- 設置基於基於接口或者基於類的代理被創建 -->
<aop:aspectj-autoproxy proxy-target-class="true"/><!-- 不配置的話可能造成類型轉換錯誤-->

整體配置如下

<?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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <!-- 配置mybatis數據源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms?
                        useUnicode=true&amp;characterEncoding=utf-8" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>
    
    <!-- 配置SQLSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 引用數據源組件 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 引用MyBatis配置文件中的配置 -->
        <property name="configLocation" value="classpath:resource/mybatis-config.xml" />
        <!-- 配置SQL映射文件信息 -->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/bdqn/dao/*.xml</value>
            </list>
        </property>
    </bean>
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.bdqn.dao" />
    </bean>
    
    <!-- 聲明DaoBean,添加註解,開啟自動掃描 -->
    <context:component-scan base-package="com.bdqn.service"></context:component-scan>
    
    <!-- 定義事務管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:annotation-driven/>
    
    <!-- 設置基於基於接口或者基於類的代理被創建 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    
</beans>

4),處理服務類

@Service("userService")
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao; // 一個普通數據庫Dao層,進行修改數據操作
    
    // 註意@Transactional配置propagation指定隔離範圍,rollbackFor指定發生什麽情況回滾
    @Transactional(propagation=Propagation.REQUIRED,rollbackFor= {NullPointerException.class})
    public int updateNames() {
        userDao.updateName("李1","19");
        /*if(true) { // 測試回滾的時候取消註釋
            throw new NullPointerException();
        }
        userDao.updateName("王1","20");*/
        return 0;
    }
}

5),測試類

public static void main(String[] args){
    ApplicationContext ac = new ClassPathXmlApplicationContext("resource/spring.xml");
    UserServiceImpl userService = (UserServiceImpl) ac.getBean("userService");
    
    userService.updateNames();
}

MySQL事務及Spring事務管理