SPRing事務管理
@(Spring)[spring, 事務管理, 事務, Spring]
Spring事務管理事務的基本概念什么是事務事務的特性事務的隔離性不完善引發的問題設置隔離級別解決讀問題Spring的事務管理Spring事務管理的APISpring的事務傳播行為Spring的事務管理案例環境搭建Spring的事務管理的分類創建相關接口和類并配置注解配置Spring在DAO中使用JDBC的模板編寫測試類Spring的聲明式事務管理xml方式的聲明式事務配置事務管理器配置一個事務通知配置AOPtxmethod的配置注解方式的聲明式事務配置事務管理器開啟注解事務在業務層添加一個事務的注解Transactional的配置Spring的編程式事務管理配置事務管理器配置事務管理的模板簡化事務開發在業務層注入事務管理的模板進行事務管理TransactionTemplate的使用標準用法沒返回值用法異常處理案例
事務的基本概念
什么是事務
是邏輯上一組操作,組成這組操作各個單元要么一起成功要么一起失敗。
事務的特性
原子性 :不可分割性一致性 :事務執行前后,數據完整性保持一致。隔離性 :一個事務執行的時候,不應該受到其他事務的干擾。持久性 :事務一旦結束,數據就持久化到數據庫。事務的隔離性不完善引發的問題
讀問題 臟讀 :一個事務讀到另一個事務未提交的數據。不可重復讀 :一個事務讀到另一個事務已經提交的update的數據,導致多次查詢結果內容不一致。幻影讀 :一個事務讀到另一個事務已經提交的insert、delete的數據,導致多次查詢結果集合不一致。寫問題 丟失更新設置隔離級別解決讀問題
read uncommitted
:讀未提交,三個讀問題都有。read committed
:讀已提交,解決臟讀,但是不可重復讀和幻影讀有可能發生。repeatable read
:可重復讀,解決臟讀和不可重復讀,但是幻影讀有可能發生。serializable
:可串行化,解決所有讀問題。Spring的事務管理
在Spring中,由事務定義類TransactionDefinition
完成事務信息的定義,由事務管理器PlatformTransactionManager
根據事務定義信息進行事務的管理,在管理過程中生成事務的各種狀態,將狀態記錄到事務狀態對象TransactionStatus
中。
Spring事務管理的API

PlatformTransactionManager :平臺事務管理器,真正進行事務管理的對象。 DataSourceTransactionManager :底層使用JDBC的模板HibernateTransactionManager :底層使用HibernateJpaTransactionManager平臺事務管理器接口有三個方法:
方法名 | 方法定義 | 注釋 |
commit | void commit(TransactionStatus status) | 根據事務狀態提交事務 |
rollback | void rollback(TransactionStatus status) | 根據事務狀態回滾事務 |
getTransaction | TransactionStatus getTransaction(TransactionDefinition definition) | 通過事務定義獲取事務狀態 |
TransactionDefinition :事務的定義信息
定義事務隔離級別 ISOLATION_DEFAULT :根據數據庫默認隔離級別設置數據庫隔離級別。(默認)ISOLATION_READ_UNCOMMITTED :讀未提交ISOLATION_READ_COMMITTED :讀已提交ISOLATION_REPEATABLE_READ :可重復讀ISOLATION_SERIALIZABLE :可串行化定義事務傳播行為 PROPAGATION_REQUIREDPROPAGATION_SUPPORTSPROPAGATION_MANDATORYPROPAGATION_REQUIRES_NEWPROPAGATION_NOT_SUPPORTEDPROPAGATION_NEVERPROPAGATION_NESTED定義事務超時信息
TIMEOUT_DEFAULT:使用事務默認超時時間。通過以下方法可以獲取事務定義信息
getIsolationLevel():獲取事務隔離級別getName():獲取事務名稱getPropagationBehavior():獲取事務傳播行為getTimeout():獲取事務超時時間isReadOnly():事務是否只讀TransactionStatus :事務的狀態
isNewTransaction():是否是新事務hasSavepoint():事務是否有保存點isCompleted():事務是否已經完成isRollbackOnly():事務是否是只能回滾setRollbackOnly():設置事務為只能回滾Spring的事務傳播行為
事務的傳播行為主要解決在復雜業務下的事務嵌套問題,比如說業務層之樂方法的相互調用。

事務傳播行為取值
使用當前事務
PROPAGATION_REQUIRED
:默認值。如果A中有事務,使用A中的事務。如果A中沒有事務,創建一個新的事務將A的內容包含進來。PROPAGATION_SUPPORTS
:如果A中有事務,使用A中的事務,如果A中沒有事務,不使用事務。PROPAGATION_MANDATORY
:如果A中有事務,使用A中的事務,如果A中沒有事務,拋異常。不使用當前事務
PROPAGATION_REQUIRES_NEW
:如果A中有事務,將A中的掛起,創建一個新的事務執行自身部分。如果A中沒有事務,創建一個新事務,執行自身部分。PROPAGATION_NOT_SUPPORTED
:如果A中有事務,將A中事務掛起。以非事務的方式運行。PROPAGATION_NEVER
:如果A中有事務,就拋異常。使用嵌套事務
PROPAGATION_NESTED
:當A執行結束后,會設置一個保存點。如果B的部分沒有錯誤,執行通過,如果B的部分出錯,允許回滾到最初始也可以回滾到保存點。Spring的事務管理案例環境搭建
Spring的事務管理的分類
編程式事務管理:手動編寫代碼完成事務管理聲明式事務管理:通過配置方式完成事務管理 XML方式注解方式創建相關接口和類并配置注解
package com.pc.service;/** * 賬號服務接口 * * @author Switch * @data 2016年11月25日 * @version V1.0 */public interface AccountService { /** * 轉賬 * @param from 轉賬者 * @param to 收賬者 * @param money 錢數 */ public void transfer(String from, String to, Double money);}package com.pc.service.impl;import javax.annotation.Resource;import org.springframework.stereotype.Service;import com.pc.dao.AccountDao;import com.pc.service.AccountService;/** * 賬號服務接口實現類 * @author Switch * @data 2016年11月25日 * @version V1.0 */// 配置服務層注解@Service("accountService")public class AccountServiceImpl implements AccountService { /** * 注入賬號持久層對象依賴 */ @Resource(name = "accountDao") private AccountDao accountDao; @Override public void transfer(String from, String to, Double money) { accountDao.transferFrom(from, money); // int i = 1 / 0; accountDao.transferTo(to, money); }}package com.pc.dao;/** * 賬號持久層接口 * * @author Switch * @data 2016年11月25日 * @version V1.0 */public interface AccountDao { /** * 從哪個賬戶轉賬 * @param from 轉賬者 * @param money 錢數 */ void transferFrom(String from, Double money); /** * 轉賬到哪個賬戶 * @param to 收賬者 * @param money 錢數 */ void transferTo(String to, Double money);}package com.pc.dao.impl;import org.springframework.jdbc.core.support.JdbcDaoSupport;import com.pc.dao.AccountDao;/** * 賬號持久層接口實行類 * * @author Switch * @data 2016年11月25日 * @version V1.0 */// 配置持久層組件注解public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void transferFrom(String from, Double money) { } @Override public void transferTo(String to, Double money) { }}配置Spring
<?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: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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置組件掃描 --> <context:component-scan base-package="com.pc.service.impl"/> <!-- 配置賬號dao,并注入數據源 --> <bean id="accountDao" class="com.pc.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- spring配置c3p0連接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="passWord" value="${jdbc.password}" /> </bean> <!-- 引入外部屬性文件 --> <context:property-placeholder location="classpath:db.properties"/></beans>在DAO中使用JDBC的模板
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void transferFrom(String from, Double money) { super.getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from); } @Override public void transferTo(String to, Double money) { super.getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to); }}編寫測試類
package com.pc.test;import javax.annotation.Resource;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.pc.service.AccountService;/** * 測試事務 * * @author Switch * @data 2016年11月25日 * @version V1.0 */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class TestTX { @Resource(name = "accountService") AccountService accountService; @Test public void test1() { accountService.transfer("Switch", "Kity", 1000d); }}Spring的聲明式事務管理
聲明式的事務底層都是基于AOP的。
一般開發種常用的是XML方式的聲明式事務,但有時候為了簡單也會使用注解方式的事務。
引入jar包 
XML方式的聲明式事務
配置事務管理器
<!-- 配置事務管理器 --><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /></bean>配置一個事務通知
<!-- 基于XML的事務配置 --><!-- 配置事務通知 --><tx:advice id="txAdvice"> <tx:attributes> <!-- 配置事務定義 --> <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED"/> </tx:attributes></tx:advice>配置AOP
<!-- 事務aop配置 --><aop:config> <aop:pointcut expression="execution(* com.pc.service.impl.AccountServiceImpl.transfer(..))" id="pointcut1"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/></aop:config>tx:method的配置
name:配置的方法名稱,支持*通配符匹配propagation:事務傳播行為isolation:事務隔離級別timeout:超時時間read-only:是否只讀事務rollback-for:觸發回滾的異常,逗號分隔no-rollback-for:不觸發回滾的異常,逗號分隔注解方式的聲明式事務
配置事務管理器
<!-- 配置事務管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /></bean>開啟注解事務
<!-- 開啟注解事務 --><tx:annotation-driven transaction-manager="transactionManager"/>在業務層添加一個事務的注解
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)public class AccountServiceImpl implements AccountService { ......}PS:事務的注解加在類上表示該類所有方法都被事務管理器管理,加在方法上,則只有該方法被事務管理器管理。
@Transactional的配置
value:使用的TransactionManagerpropagation:事務傳播行為isolation:事務隔離級別timeout:超時readOnly:是否只讀事務rollbackFor:觸發回滾的異常類對象數組rollbackForClassName:出發回滾的異常類名稱數組noRollbackFor:不觸發回滾的異常類對象數組noRollbackForClassName:不觸發回滾的異常類名稱數組Spring的編程式事務管理
編程式事務基本不用,這里只是基本介紹
配置事務管理器
<!-- 配置事務管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /></bean>配置事務管理的模板(簡化事務開發)
<!-- 配置事務管理模板 --><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/></bean>在業務層注入事務管理的模板
@Resource(name = "transactionTemplate")private TransactionTemplate transactionTemplate;進行事務管理
package com.pc.service.impl;import javax.annotation.Resource;import org.springframework.stereotype.Service;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.annotation.Isolation;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import org.springframework.transaction.support.TransactionCallbackWithoutResult;import org.springframework.transaction.support.TransactionTemplate;import com.pc.dao.AccountDao;import com.pc.service.AccountService;/** * 賬號服務接口實現類 * @author Switch * @data 2016年11月25日 * @version V1.0 */// 配置服務層注解@Service("accountService")// @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)public class AccountServiceImpl implements AccountService { /** * 注入賬號持久層對象依賴 */ @Resource(name = "accountDao") private AccountDao accountDao; @Resource(name = "transactionTemplate") private TransactionTemplate transactionTemplate; @Override public void transfer(final String from, final String to, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { accountDao.transferFrom(from, money); int i = 1 / 0; accountDao.transferTo(to, money); } }); }}TransactionTemplate的使用
標準用法
public Object transfer(final String from, final String to, final Double money) { return transactionTemplate.execute(new TransactionCallback() { @Override public Object doInTransaction(TransactionStatus arg0) { accountDao.transferFrom(from, money); int i = 1 / 0; accountDao.transferTo(to, money); return accountDao.toString(); } });}沒返回值用法
@Overridepublic void transfer(final String from, final String to, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { accountDao.transferFrom(from, money); // int i = 1 / 0; accountDao.transferTo(to, money); } });}異常處理
public void transfer(final String from, final String to, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { try { accountDao.transferFrom(from, money); // int i = 1 / 0; accountDao.transferTo(to, money); } catch (Exception e) { e.printStackTrace(); } } });}案例
GitHub:SpringDataTest GitHub:MyStore-netease