MySQL事務及Spring事務管理
事務,是在數據庫中用於保證數據正確性的一種機制,涉及到很多概念以及不同的情況,這裏做一個總結
相關概念
事務四特性(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&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事務管理