javascript
014_Spring事务
一. 事務
1. 什么事務?
1.1. 事務: 邏輯上的一組操作, 組成這組操作的各個單元, 要么全都成功, 要么全都失敗。
2. 事務的特性
2.1. 原子性: 事務不可分割。
2.2. 一致性: 事務執行前后數據完整性保持一致。
2.3. 隔離性: 一個事務的執行不應該受到其他事務的干擾。
2.4. 持久性: 一旦事務結束, 數據就持久化到數據庫。
3. 如果不考慮隔離性引發安全性問題
3.1. 讀問題
3.1.1. 臟讀: 一個事務讀到另一個事務未提交的數據。
3.1.2. 不可重復讀: 一個事務讀到另一個事務已經提交的update的數據, 導致一個事務中多次查詢結果不一致。
3.1.3. 虛讀、幻讀: 一個事務讀到另一個事務已經提交的insert的數據, 導致一個事務中多次查詢結果不一致。
3.2. 寫問題
3.2.1. 丟失更新。
4. 設置事務的隔離級別解決讀問題
4.1. Read uncommitted: 未提交讀, 任何讀問題解決不了。
4.2. Read committed: 已提交讀, 解決臟讀, 但是不可重復讀和虛讀有可能發生。
4.3. Repeatable read: 重復讀, 解決臟讀、不可重復讀和幻讀(MySQL8中)。
4.4. Serializable: 解決所有讀問題, 同時似乎給整張表添加了一個鎖, 客戶并發讀, 但不能并發寫。
二. Spring的事務管理的API
1. PlatformTransactionManager平臺事務管理器接口
1.1. DataSourceTransactionManager: 底層使用JDBC管理事務。
1.2. HibernateTransactionManager: 底層使用Hibernate管理事務。
2. TransactionDefinition事務定義信息
2.1. 事務定義: 用于定義事務的相關的信息, 隔離級別、超時信息、傳播行為、是否只讀(只讀的時候不能寫)。
3. TransactionStatus事務的狀態
3.1. 事務狀態: 用于記錄在事務管理過程中, 事務的狀態的對象。
4. 事務管理的API的關系
4.1. Spring進行事務管理的時候, 首先平臺事務管理器根據事務定義信息進行事務的管理, 在事務管理過程中, 產生各種狀態, 將這些狀態的信息記錄到事務狀態的對象中。
5.?Spring中提供了七種事務的傳播行為:
5.1. 保證多個操作在同一個事務中
5.1.1. PROPAGATION_REQUIRED: 默認值, 如果A中有事務, 使用A中的事務, 如果A沒有, 創建一個新的事務, 將操作包含進來。
5.1.2. PROPAGATION_SUPPORTS: 支持事務, 如果A中有事務, 使用A中的事務。如果A沒有事務, 不使用事務。
5.1.3. PROPAGATION_MANDATORY: 如果A中有事務, 使用A中的事務。如果A沒有事務, 拋出異常。
5.2. 保證多個操作不在同一個事務中
5.2.1. PROPAGATION_REQUIRES_NEW: 如果A中有事務, 將A的事務掛起(暫停), 創建新事務, 只包含自身操作。如果A中沒有事務, 創建一個新事務, 包含自身操作。
5.2.2. PROPAGATION_NOT_SUPPORTED: 如果A中有事務, 將A的事務掛起。不使用事務管理。
5.2.3. PROPAGATION_NEVER: 如果A中有事務, 報異常。
5.3. 嵌套式事務
5.3.1. PROPAGATION_NESTED: 嵌套事務, 如果A中有事務, 按照A的事務執行, 執行完成后,設置一個保存點, 執行B中的操作, 如果沒有異常, 執行通過, 如果有異常, 可以選擇回滾到最初始位置, 也可以回滾到保存點。
三. 編程式事務
1. 配置平臺事務管理器
2. Spring提供了事務管理的模板類
3. 在業務層注入事務管理的模板
4. 編寫事務管理的代碼
5. 例子
5.1. 新建一個名為SpringJdbcProgramTx的Java工程, 拷入相關jar包
5.2. 新建一個Account.java
package com.lywgames.tx;import java.io.Serializable;public class Account implements Serializable {private static final long serialVersionUID = 1L;private Integer id;private String name;private Float money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Float getMoney() {return money;}public void setMoney(Float money) {this.money = money;}@Overridepublic String toString() {return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";}}5.3. 新建一個AccountDao.java數據庫操作接口
package com.lywgames.tx;/*** 轉賬的Dao的接口*/ public interface AccountDao {public void outMoney(String from, Float money);public void inMoney(String to, Float money); }5.4. 新建一個AccountDaoImpl.java數據庫操作實現類
package com.lywgames.tx;import org.springframework.jdbc.core.support.JdbcDaoSupport;/*** 轉賬的Dao的實現類*/ public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {@Overridepublic void outMoney(String from, Float money) {getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from);}@Overridepublic void inMoney(String to, Float money) {getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);}}5.5. 新建一個AccountService.java業務接口
package com.lywgames.tx;/*** 轉賬的業務層的接口*/ public interface AccountService {public void transfer(String from, String to, Float money); }5.6. 新建一個AccountServiceImpl.java業務實現類
package com.lywgames.tx;import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate;/*** 轉賬的業務層的實現類*/ public class AccountServiceImpl implements AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}private TransactionTemplate transactionTemplate;public void setTransactionTemplate(TransactionTemplate transactionTemplate) {this.transactionTemplate = transactionTemplate;}/*** from: 轉出賬號* to: 轉入賬號* money: 轉賬金額*/@Overridepublic void transfer(String from, String to, Float money) {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {accountDao.outMoney(from, money);int d = 1/0;accountDao.inMoney(to, money);}});}}5.7. 新建一個Test.java測試類
package com.lywgames.tx;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = context.getBean(AccountService.class);accountService.transfer("王五", "李四", 100.00F);context.close();} }5.8. 在src目錄下創建jdbc.properties數據庫連接配置
5.9. 在src目錄下創建applicationContext.xml
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 通過context標簽引入屬性文件 --><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!-- 配置Spring內置連接池 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClassName}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置平臺事務管理器 --><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><!-- Dao繼承了JdbcDaoSupport, 可以不配置jdbcTemplate模板bean, 在注入dataSource屬性時, 會自動創建jdbcTemplate。 --><bean id="accountDao" class="com.lywgames.tx.AccountDaoImpl"><property name="dataSource" ref="dataSource"></property></bean><bean id="accountService" class="com.lywgames.tx.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property><!-- 注入事務管理的模板 --><property name="transactionTemplate" ref="transactionTemplate" /></bean> </beans>5.10. 查看account表
5.11. 運行項目, 拋出異常, 查看數據庫結果, 轉載失敗, 每個人的錢數不變
5.12. 注釋掉異常, 運行項目, 查看數據庫結果, 轉賬成功
四. 聲明式事務管理(XML方式的聲明式事務管理)
1. 配置平臺事務管理器
2. 配置增強
3. AOP的配置
4. 例子
4.1. 新建一個名為SpringJdbcXmlTx的Java工程, 拷入相關jar包
4.2. 新建一個Account.java
package com.lywgames.tx;import java.io.Serializable;public class Account implements Serializable {private static final long serialVersionUID = 1L;private Integer id;private String name;private Float money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Float getMoney() {return money;}public void setMoney(Float money) {this.money = money;}@Overridepublic String toString() {return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";}}4.3. 新建一個AccountDao.java數據庫操作接口
package com.lywgames.tx;/*** 轉賬的Dao的接口*/ public interface AccountDao {public void outMoney(String from, Float money);public void inMoney(String to, Float money); }4.4. 新建一個AccountDaoImpl.java數據庫操作實現類
package com.lywgames.tx;import org.springframework.jdbc.core.support.JdbcDaoSupport;/*** 轉賬的Dao的實現類*/ public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {@Overridepublic void outMoney(String from, Float money) {getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from);}@Overridepublic void inMoney(String to, Float money) {getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);}}4.5. 新建一個AccountService.java業務接口
package com.lywgames.tx;/*** 轉賬的業務層的接口*/ public interface AccountService {public void transfer(String from, String to, Float money); }4.6. 新建一個AccountServiceImpl.java業務實現類
package com.lywgames.tx;/*** 轉賬的業務層的實現類*/ public class AccountServiceImpl implements AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/*** from: 轉出賬號* to: 轉入賬號* money: 轉賬金額*/@Overridepublic void transfer(String from, String to, Float money) {accountDao.outMoney(from, money);int d = 1/0;accountDao.inMoney(to, money);}}4.7. 新建一個Test.java測試類
package com.lywgames.tx;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = context.getBean(AccountService.class);accountService.transfer("王五", "李四", 100.00F);context.close();} }4.8. 在src目錄下創建jdbc.properties數據庫連接配置
4.9. 在src目錄下創建applicationContext.xml
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 通過context標簽引入屬性文件 --><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!-- 配置Spring內置連接池 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClassName}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置事務管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- Dao繼承了JdbcDaoSupport, 可以不配置jdbcTemplate模板bean, 在注入dataSource屬性時, 會自動創建jdbcTemplate。 --><bean id="accountDao" class="com.lywgames.tx.AccountDaoImpl"><property name="dataSource" ref="dataSource"></property></bean><bean id="accountService" class="com.lywgames.tx.AccountServiceImpl"><property name="accountDao" ref="accountDao"></property></bean> <!-- 配置事務的增強/通知, 類似前置通知、后置通知等。 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!-- name="*" 表示所有方法 --><tx:method name="*" propagation="REQUIRED" read-only="false"/></tx:attributes></tx:advice><!-- aop的配置 --><aop:config><aop:pointcut expression="execution(* com.lywgames.tx.AccountServiceImpl.*(..))" id="pointcut"/><!-- aop:advisor和aop:aspect都是切面。aop:advisor多個切入點和多個通知的組合。aop:aspect一個切入點和一個通知的組合。 --><aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/></aop:config> </beans>4.10. 查看account表
4.11. 運行項目, 拋出異常, 查看數據庫結果, 轉載失敗, 每個人的錢數不變
4.12. 注釋掉異常, 運行項目, 查看數據庫結果, 轉賬成功
五. 聲明式事務管理(注解方式的聲明式事務管理)
1. 配置平臺事務管理器
2. 開啟注解事務
3. 在業務層添加注解
4. 例子
4.1. 新建一個名為SpringJdbcAnnotationTx的Java工程, 拷入相關jar包
4.2. 新建一個Account.java
package com.lywgames.tx;import java.io.Serializable;public class Account implements Serializable {private static final long serialVersionUID = 1L;private Integer id;private String name;private Float money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Float getMoney() {return money;}public void setMoney(Float money) {this.money = money;}@Overridepublic String toString() {return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";}}4.3. 新建一個AccountDao.java數據庫操作接口
package com.lywgames.tx;/*** 轉賬的Dao的接口*/ public interface AccountDao {public void outMoney(String from, Float money);public void inMoney(String to, Float money); }4.4. 新建一個AccountDaoImpl.java數據庫操作實現類
package com.lywgames.tx;import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component;/*** 轉賬的Dao的實現類*/ @Component(value="accountDao") public class AccountDaoImpl implements AccountDao {@Resource(name="jdbcTemplate")private JdbcTemplate jdbcTemplate;@Overridepublic void outMoney(String from, Float money) {jdbcTemplate.update("update account set money = money - ? where name = ?", money, from);}@Overridepublic void inMoney(String to, Float money) {jdbcTemplate.update("update account set money = money + ? where name = ?", money, to);}}4.5. 新建一個AccountService.java業務接口
package com.lywgames.tx;/*** 轉賬的業務層的接口*/ public interface AccountService {public void transfer(String from, String to, Float money); }4.6. 新建一個AccountServiceImpl.java業務實現類
package com.lywgames.tx;import javax.annotation.Resource; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional;/*** 轉賬的業務層的實現類*/ @Transactional @Component(value="accountService") public class AccountServiceImpl implements AccountService {@Resource(name="accountDao")private AccountDao accountDao;/*** from: 轉出賬號* to: 轉入賬號* money: 轉賬金額*/@Overridepublic void transfer(String from, String to, Float money) {accountDao.outMoney(from, money);int d = 1/0;accountDao.inMoney(to, money);}}4.7. 新建一個Test.java測試類
package com.lywgames.tx;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = context.getBean(AccountService.class);accountService.transfer("王五", "李四", 100.00F);context.close();} }4.8. 在src目錄下創建jdbc.properties數據庫連接配置
4.9. 在src目錄下創建applicationContext.xml
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 使用IOC的注解開發, 配置組件掃描, 哪些包下的類使用了IOC的注解 --><context:component-scan base-package="com.lywgames"></context:component-scan><!-- 通過context標簽引入屬性文件 --><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!-- 配置Spring內置連接池 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClassName}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><!-- 配置事務管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 開啟注解事務 --><tx:annotation-driven transaction-manager="transactionManager"/><!-- 配置Spring的Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean> </beans>4.10. 查看account表
4.11. 運行項目, 拋出異常, 查看數據庫結果, 轉載失敗, 每個人的錢數不變
4.12. 注釋掉異常, 運行項目, 查看數據庫結果, 轉賬成功
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的014_Spring事务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 013_JDBC模板使用第三方连接池
- 下一篇: 001_SpringMVC入门