Spring——事務管理
事務
(1)事務是資料庫操作最基本單元,邏輯上一組操作,要麼都成功,如果有一個失敗所有操作都失敗
(2)典型場景:銀行轉賬
事務四個特性(ACID
原子性(atomicity) 一個事務是一個不可分割的工作單位,事務中包括的操作要麼都做,要麼都不做。
一致性(consistency) 事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation) 一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
永續性(durability) 永續性也稱永久性(permanence),指一個事務一旦提交,它對資料庫中資料的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。
環境搭建
模擬銀行轉賬
資料庫表
Dao層
public interface AccountDao { /** * 增加金額 * @param username 轉賬人 * @param money 金額 */ void addMoney(String username, BigDecimal money); /** * 減少金額 * @param username 收賬人 * @param money 金額 */ void reduceMoney(String username, BigDecimal money); }
@Repository public class AccountDaoImpl implements AccountDao { @Autowired JdbcTemplate jdbcTemplate; @Override public void addMoney(String username, BigDecimal money) { String sql = "update t_account set money=money+? where username=?"; jdbcTemplate.update(sql, money, username); } @Override public void reduceMoney(String username, BigDecimal money) { String sql = "update t_account set money=money-? where username=?"; jdbcTemplate.update(sql, money, username); } }
service層
@Service
public class AccountService {
@Autowired
AccountDao accountDao;
/**
* 轉賬
* @param fromName 轉賬人
* @param toName 收賬人
* @param money 金額
*/
public void transfer(String fromName, String toName, BigDecimal money){
accountDao.reduceMoney(fromName, money);
accountDao.addMoney(toName, money);
}
}
配置檔案
資料庫配置
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/wcy
jdbc.driver=com.mysql.cj.jdbc.Driver
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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.wcy"/>
<!--配置檔案-->
<context:property-placeholder location="classpath:db.properties"/>
<!--引用外部配置檔案 配置資料來源-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
測試
@Test
public void transfer() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
AccountService accountService = context.getBean("accountService", AccountService.class);
accountService.transfer("wcy", "wp", new BigDecimal(100));
}
在不發生異常的情況下,執行轉賬的方法沒有問題
但是如果在轉賬圖中發生了異常,就會造成不對應的情況,所以需要用到事務管理
Spring事務管理
事務新增到 JavaEE 三層結構裡面 Service 層(業務邏輯層)
Spring 進行事務管理操作有兩種方式:程式設計式事務管理和宣告式事務管理(使用)
宣告式事務管理
(1)基於註解方式(使用)
(2)基於 xml 配置檔案方式
Spring 進行宣告式事務管理,底層使用 AOP 原理
Spring 事務管理 API
提供一個介面,代表事務管理器,這個介面針對不同的框架提供不同的實現類
基於註解的事務管理
在配置檔案中配置事務管理器,並開啟註解事務管理
需要匯入tx名稱空間
<!--開啟基於註解的事務管理-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
在service層添加註解
@Service
@Transactional
public class AccountService {
}
@Transactional
可以新增到類上面,也可以新增方法上面- 如果把這個註解新增類上面,這個類裡面所有的方法都新增事務
- 如果把這個註解新增方法上面,為這個方法新增事務
這樣就完成了基本的事務管理
事務的操作
在@Transactional
中可以配置許多的引數
// 事務的傳播行為
Propagation propagation() default Propagation.REQUIRED;
// 事務的隔離級別
Isolation isolation() default Isolation.DEFAULT;
// 超時時間
int timeout() default -1;
//是否只讀
boolean readOnly() default false;
// 回滾配置
Class<? extends Throwable>[] rollbackFor() default {};
// 回滾配置
String[] rollbackForClassName() default {};
// 不回滾
Class<? extends Throwable>[] noRollbackFor() default {};
// 不回滾
String[] noRollbackForClassName() default {};
propagation
作用:配置事務的傳播特性
實際就是多事務方法直接進行呼叫,在這個過程中事務是如何進行管理的
spring中定義了7種傳播行為
ioslation
作用:配置事務隔離級別
事務有特性成為隔離性,多事務操作之間不會產生影響。不考慮隔離性產生很多問題
有三個讀問題:髒讀、不可重複讀、虛(幻)讀
-
髒讀:即為事務1第二次讀取時,讀到了事務2未提交的資料。若事務2回滾,則事務1第二次讀取時,讀到了髒資料。
舉例:學生表中張三的成績是59,事務一修改了學生表張三的成績69,但是事務還沒有提交,這個時候事務二來讀取張三的成績,讀到了69,但是事務一出問題回滾了,資料庫學生表中張三還是59,明顯事務二讀的資料不對,“髒讀”了。 -
不可重複讀:事務一第二次查詢某條資料時,讀到了事務二提交的修改的該資料
舉例:事務一第一次讀取張三的成績是59(事務沒結束),這個時候,事務二改了張三的成績為69並且commit,這時候事務一又去讀取張三的成績,哎?變成69了,就是重複讀資料不一致,不可重複讀。 -
事務2在事務1第二次讀取時,提交了資料。導致事務1前後兩次讀取的資料量不一致
舉例:事務一讀取學生表總共有45人(事務沒結束),這個時候事務二突然插入一條學生資料,事務一再次讀取總數,發現變成了46條資料,就是兩次讀取的資料數量不一致。
通過配置ioslation
來解決
timeout:超時時間
(1)事務需要在一定時間內進行提交,如果不提交進行回滾
(2)預設值是 -1 ,設定時間以秒單位進行計算
readOnly:是否只讀
(1)讀:查詢操作,寫:新增修改刪除操作
(2)readOnly 預設值 false,表示可以查詢,可以新增修改刪除操作
(3)設定 readOnly 值是 true,設定成 true 之後,只能查詢
rollbackFor:回滾
(1)設定出現哪些異常進行事務回滾,通過Class物件來設定
noRollbackFor:不回滾
(1)設定出現哪些異常不進行事務回滾,通過Class物件來設定
rollbackForClassName
(1)設定出現哪些異常進行事務回滾,通過類名來設定
純註解方式的事務管理
使用配置類來替代配置檔案
@Configuration
@ComponentScan(basePackages = {"com.wcy"})
@EnableTransactionManagement //開啟事務管理
public class TransactionConfig {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/wcy");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
/**
* 配置jdbcTemplate 和配置檔案中的bean標籤作用一樣
* DataSource引數通過byType的方式自動注入 也可以使用byName
* Autowired可以寫可以不寫
*/
@Bean
public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public TransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
@EnableTransactionManagement
的作用和<tx:annotation-driven transaction-manager="transactionManager"/>
的作用一樣@Bean
和bean標籤的作用一樣
新增@Transactional註解
@Service
@Transactional(
propagation = Propagation.REQUIRED,
rollbackFor = {Exception.class}
)
public class AccountService {
}
基於xml配置的事務管理
不需要開啟註解事務,使用aop來切入
<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事務通知-->
<tx:advice transaction-manager="transactionManager" id="transactionAdvice">
<tx:attributes>
<!--配置那些方法需要新增事務 可以使用萬用字元*來配置-->
<!--可以配置事務的引數 和註解中的一樣-->
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--aop事務切入-->
<aop:config>
<aop:pointcut id="point" expression="execution(* com.wcy.service.AccountService.*(..))"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="point"/>
</aop:config>
Service中不需要@Transactionl
註解
事務的基本配置~