看了慕課的spring事務管理,然後整理了一下,以便以後自己學習
1. 原子性.
事務是一個不可分割的整體,要麼都失敗,要麼都成功
2. 一致性:
事務前後性必須資料的完整性必須保持一致
3. 隔離性:
多個使用者併發訪問資料庫的時候,一個使用者的事務不能被其他使用者的事務給干擾,
多個併發事務之間資料要相互隔離
4. 永續性:
永續性就是事務一旦提交,那麼對於資料庫中就是永久性的,不會因為資料庫傳送事故而改變
事務管理的三個主要介面
platformTransactionManager
事務管理器
spring為不同的持久層框架提供了不同的介面
對於 mybatis,jdbc 使用的是 org.springframework.jdbc.datasource.DataSourceTransaction
對於 hibernate org.springframework.orm.hibernate3.HibernateTransactionManager
對於 jpa org.springframework.orm.jpa.JpaTransactionManager
對於 jdo org.springframework.orm.jdo.JdoTransactionManager
對於 jta(是一個分散式事務) org.springframework.orm.jta.JtaTransactionManager
TransactionDefinition
事務定義資訊 (隔離,傳播,超時,只讀)
TransactionStatus
事務具體執行狀態
事務中出現的問題:
1. 髒讀:
一個事務讀到了另一個事務改寫但還未提交的資料,如果這些資料回滾,則讀到的資料是無效的
2. 不可重複讀:
在同一事務中,多次讀取同一資料返回的結果有所不同
3. 幻讀:
在一個事務中讀取了幾行資料,當另一個事務插入了幾條資料後,在後來的查詢後,發現一些沒有的資料.
事務的隔離級別:
DEFAULT 使用後端資料庫預設的隔離級別
對於mysql 預設的隔離級別(REPEATABLE_READ)
oracle (READ_COMMITED)
READ_UNCOMMITED 允許讀取修改還未提交的資料,可能會導致,髒,不可重複,幻讀的情況
READ_COMMITED 允許在事務提交以後讀取,但不可避免不可重複,幻讀的情況
REPEATABLE_READ 對於相同欄位多次讀取時一致的,除非資料被事務本身改變,可防止髒讀,不可重複..但幻讀不行
SERIALIZABLE 完全服從ACID的隔離級別,確保不發生髒,幻,不可重複讀,這是所有隔離級別中最慢的,它是完全
鎖在事務中涉及的資料表來完成的.
事務的傳播行為:
當一個功能 需要呼叫 2個業務層的方法 才能完成, 每個service層的方法都有事務.\
PROPAGATION_REQUIRED * 支援當前事務,如果不存在就新建一個
PROPAGATION_SUPPORTS 支援當前事務,如果不存在就不適用事務,
PROPAGATION_MANDATORY 支援當前事務,如果不存在,丟擲異常
PROPAGATION_REQUIRED_NEW * 如果有事務存在,掛起當前事務,新建一個
PROPAGATION_NOT_SUPPORTS 以非事務方式執行,如果事務存在,掛起當前事務
PROPAGATION_NEVER 以非事務方式執行,丟擲異常
PROPAGATION_NESTED * 如果當前事務存在咋巢狀事務執行
spring 支援兩種事務管理
1. 程式設計式的事務管理
1. 在實際應用中很少使用
2. 通過TransactionTemplate手動管理事務
使用XML配置宣告式事務
1. 開發中推薦使用(程式碼侵入最小)
2. Spring的宣告式事務式通過AOP實現的
1. 基於 TransactionProxyFactoryBean (很少使用)
2. 基於 AspectJ的XML方式 (經常使用)
一旦配置好了,不需要在新增任何東西
3. 基於 註解 (經常使用)
配置簡單,需要在每個業務層類上面加 @Transcational 註解
做的一個例項 是 轉賬問題 a 轉 b 兩個人的賬戶相應增加,減少,當發生異常,則事務不進行提交,a,b的金額沒有發生改變
在 SpringTest 測試類中 需要加入
@RunWith(SpringJUnit4ClassRunner.class) // //使用junit4進行測試
@ContextConfiguration("classpath:applicationContext.xml") // 載入配置檔案
這兩個註解
搭建測試環境
userDao,userDaoImpl
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class UserDaoImpl extends JdbcDaoSupport implements UserDao{
@Override
public void outPrice(String name, double price) {
// TODO Auto-generated method stub
String sql = "update user set price = price - ? where username = ?";
this.getJdbcTemplate().update(sql,price,name);
}
@Override
public void intPrice(String name, double price) {
// TODO Auto-generated method stub
String sql = "update user set price = price + ? where username = ?";
this.getJdbcTemplate().update(sql,price,name);
}
}
public interface UserDao {
/**
* 轉賬
*/
public void outPrice(String name,double price);
/**
* 收賬
*/
public void intPrice(String name,double price);
}
userService,userServiceImpl
public interface UserService {
public void out(String name,String name2,double price);
}
import javax.annotation.Resource;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class UserServiceImpl implements UserService{
@Resource(name="userDao")
public UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 事務模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void out(String name, String name2, double price) {
// TODO Auto-generated method stub
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus tr) {
// TODO Auto-generated method stub
userDao.intPrice(name, price);
int a = 1 / 0;
userDao.outPrice(name2, price);
}
}
);
}
}
1. 程式設計式的事務管理
在 service 中使用TransactionTemplate
TransactionTemplate 依賴 DataSourceTransactionManager
DataSourceTransactionManager依賴DataSource構造
1.1 在 applicationContext 中配置事務管理器
<?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: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.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>
<!-- 資料庫配置檔案 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 建立資料來源 -->
<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="${name}"></property>
<property name="password" value="${password}"></property>
<property name="maxActive" value="10"></property>
<property name="maxIdle" value="5"></property>
</bean>
<!-- 配置資料層 -->
<bean id="userDao" class="com.zyh.UserDaoImpl" scope="prototype">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置業務層 -->
<bean id="userService" class="com.zyh.UserServiceImpl" scope="prototype">
<property name="userDao" ref="userDao"></property>
<!-- 注入事務管理的模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 程式設計式的事務管理 -->
<!-- 建立事務管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 將資料連線注入過來 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 建立一個 事務管理模板 簡化 底層程式碼 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!-- 將真正進行事務管理的 注入進來 -->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
</beans>
1.2 在 service 實現類中注入 transactionTemplate 模板來實現事務管理
// 當在service 方法中注入了transactionTemplate 那麼在 applicationContext 中相應的 service
// 也注入transactionTemplate
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate){
this.transactionTemplate = transactionTemplate;
}
然後呼叫 transactionTemplate 中的方法 execute
此方法呢需要一個匿名內部類 new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus tr) {
// TODO Auto-generated method stub
userDao.intPrice(name, price);
int a = 1 / 0;
userDao.outPrice(name2, price);
}
}
1.3 然後進行測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
@Resource(name="userService")
public UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Test
public void springDemo() {
userService.out("aaa", "bbb", 200);
}
}
然後檢視資料庫,發現中間發生異常,事務是不會提交的
2. spring宣告式的事務管理
宣告式的事務管理是基於spring aop思想的
將環境恢復到 一開始狀態,(將所有的關於 transactionTemplate 的都刪除)
<!-- 建立事務管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 將資料連線注入過來 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
2. 配置代理
<!-- 配置 代理 -->
<bean id="userServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置目標物件 -->
<property name="target" ref="userService"></property>
<!-- 注入事務管理 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事務屬性 -->
<property name="transactionAttributes">
<props>
<!--
prop格式:
PROPAGATION : 事務的傳播行為
ISOLATION : 事務的隔離級別
readOnly : 只讀
-Exception : 發生哪些異常回滾事務
+Exception : 發生那修異常事務不會滾
-->
<prop key="out">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
然後service層不需要再進行修改了,只需要修改SpringTest
將userService的 注入換為 userServiceProxy
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest {
/**
* 注入代理類
*/
// @Resource(name="userService")
@Resource(name="userServiceProxy")
public UserService userService;
@Test
public void springDemo() {
userService.out("aaa", "bbb", 200);
}
}
然後進行測試.成功, 當有異常的時候是不會進行 事務提交,沒有異常正常提交事務.
然後在 配置檔案 bean userServiceProxy 的 事務屬性中
<!-- 注入事務屬性 -->
<property name="transactionAttributes">
<props>
<!--
prop格式:
PROPAGATION : 事務的傳播行為
ISOLATION : 事務的隔離級別
readOnly : 只讀
-Exception : 發生哪些異常回滾事務
+Exception : 發生那修異常事務不會滾
-->
<prop key="out">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
多配置一個readonly 然後進行測試會 報異常
因為進行的是修改操作.所有當設定為只讀的話 就會報異常
上面那個是第一種方式 基於 TransactionProxyFactoryBean的原始方式
缺點 只能對一個業務層類 管理,當有多個模組的話就需要配置多個Proxy來實現事務管理
宣告式事務管理,第二種方式As
在 applicationConext 中
<!-- 配置事務的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation : 事務傳播行為
isolation : 事務隔離級別
readOnly : 只讀
rollback-for : 發生哪些異常回滾
no-rollback-for: 發生哪些異常不回滾
timeout : 過期資訊
-->
<tx:method name="out" propagation="REQUIRED" isolation="DEFAULT" />
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切入點 -->
<aop:pointcut expression="execution(* zyh.UserService+.*(..))" id="pointcut1"/>
<aop:advisor advice-ref="pointcut1" pointcut="pointcut1"/>
</aop:config>
這種方式的只需要在配置檔案配置後就可以 實現對事務管理, 代理物件在對應類生成時生成
第三種 基於註解的 宣告式事務管理
<!-- 開啟事務註解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
然後只需要在 指定的類上面新增Transactional
/**
* propagation : 事務傳播行為
isolation : 事務隔離級別
readOnly : 只讀
rollback-for : 發生哪些異常回滾
no-rollback-for: 發生哪些異常不回滾
timeout : 過期資訊
*/
@Transactional
public class UserServiceImpl implements UserService