javascript
Spring事务详解与使用
Spring事務核心對象
J2EE開發使用分層設計的思想進行,對于簡單的業務層轉調數據層的單一操作,事務開啟在業務層或者數據層并無太大差別,當業務中包含多個數據層的調用時,需要在業務層開啟事務,對數據層中多個操作進行組合并歸屬于同一個事務進行處理
Spring為業務層提供了整套的事務解決方案:
- PlatformTransactionManager
- TransactionDefinition
- TransactionStatus
PlatformTransactionManager
PlatformTransactionManager:平臺事務管理器實現類,是一個接口,需要使用它的實現類
-
DataSourceTransactionManager 適用于Spring JDBC或MyBatis
-
HibernateTransactionManager 適用于Hibernate3.0及以上版本
-
JpaTransactionManager 適用于JPA
-
JdoTransactionManager 適用于JDO
-
JtaTransactionManager 適用于JTA
-
JPA(Java Persistence API)Java EE 標準之一,為POJO提供持久化標準規范,并規范了持久化開發的統一API,符合JPA規范的開發可以在不同的JPA框架下運行
-
JDO(Java Data Object )是Java對象持久化規范,用于存取某種數據庫中的對象,并提供標準化API。與JDBC相比,JDBC僅針對關系數據庫進行操作,JDO可以擴展到關系數據庫、文件、XML、對象數據庫(ODBMS)等,可移植性更強
-
JTA(Java Transaction API)Java EE 標準之一,允許應用程序執行分布式事務處理。與JDBC相比,JDBC事務則被限定在一個單一的數據庫連接,而一個JTA事務可以有多個參與者,比如JDBC連接、JDO 都可以參與到一個JTA事務中
此接口定義了事務的基本操作
- 獲取事務 :
- 提交事務 :
- 回滾事務 :
TransactionDefinition
此接口定義了事務的基本信息
- 獲取事務定義名稱
- 獲取事務的讀寫屬性
- 獲取事務隔離級別
- 獲事務超時時間
- 獲取事務傳播行為特征
TransactionStatus
此接口定義了事務在執行過程中某個時間點上的狀態信息及對應的狀態操作
獲取事務是否處于新開啟事務狀態
boolean isNewTransaction()獲取事務是否處于已完成狀態
boolean isCompleted()獲取事務是否處于回滾狀態
boolean isRollbackOnly()刷新事務狀態
void flush()獲取事務是否具有回滾存儲點
boolean hasSavepoint()設置事務處于回滾狀態
void setRollbackOnly()事務控制方式
-
編程式
-
聲明式(XML)
-
聲明式(注解)
案例:
模擬銀行轉賬業務說明,銀行轉賬操作中,涉及從A賬戶到B賬戶的資金轉移操作。數據層僅提供單條數據的基礎操作,未設計多賬戶間的業務操作。
案例環境(基于Spring、Mybatis整合):
pom.xml
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>test</scope></dependency></dependencies>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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:property-placeholder location="classpath:*.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="accountService" class="com.itzhuzhu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><property name="dataSource" ref="dataSource"/></bean><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.itzhuzhu.domain"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itzhuzhu.dao"/></bean></beans>jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db1 jdbc.username=root jdbc.password=不告訴你AccountDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itzhuzhu.dao.AccountDao"><update id="inMoney">update account set money = money + #{money} where name = #{name}</update><update id="outMoney">update account set money = money - #{money} where name = #{name}</update></mapper>dao包下AccountDao
public interface AccountDao {void inMoney(@Param("name") String name, @Param("money") Double money);void outMoney(@Param("name") String name, @Param("money") Double money); }domain包下Account
public class Account implements Serializable {private Integer id;private String name;private Double money; }service包下AccountService
public interface AccountService {/*** 轉賬操作** @param outName 出賬用戶名* @param inName 入賬用戶名* @param money 轉賬金額*/public void transfer(String outName, String inName, Double money); }service.impl包下AccountServiceImpl
public class AccountServiceImpl implements AccountService {private AccountDao accountDao;private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String outName, String inName, Double money) {// 開啟事務PlatformTransactionManager pt = new DataSourceTransactionManager(dataSource);// 事務定義TransactionDefinition td = new DefaultTransactionDefinition();// 事務狀態TransactionStatus ts = pt.getTransaction(td);accountDao.inMoney(outName, money);int i = 1 / 0;accountDao.outMoney(inName, money);// 提交事務pt.commit(ts);} }測試類:
public class Test {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = (AccountService) ctx.getBean("accountService");accountService.transfer("張三","李四",100D);} }編程式事務:
public void transfer(String outName,String inName,Double money){//創建事務管理器DataSourceTransactionManager dstm = new DataSourceTransactionManager();//為事務管理器設置與數據層相同的數據源dstm.setDataSource(dataSource);//創建事務定義對象TransactionDefinition td = new DefaultTransactionDefinition();//創建事務狀態對象,用于控制事務執行TransactionStatus ts = dstm.getTransaction(td);accountDao.inMoney(outName,money);int i = 1/0; //模擬業務層事務過程中出現錯誤accountDao.outMoney(inName,money);//提交事務dstm.commit(ts); }使用AOP控制事務
將業務層的事務處理功能抽取出來制作成AOP通知,利用環繞通知運行期動態織入。
aop包下TxAdvice
AccountServiceImpl
public class AccountServiceImpl implements AccountService {private AccountDao accountDao;private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String outName, String inName, Double money) {accountDao.inMoney(outName, money);int i = 1 / 0;accountDao.outMoney(inName, money);} }applicationContext.xml
<!--AOP配置--><bean id="txAdvice" class="com.itzhuzhu.aop.TxAdvice"><property name="dataSource" ref="dataSource"/></bean><aop:config><aop:pointcut id="pt" expression="execution(* *..transfer(..))"/><aop:aspect ref="txAdvice"><aop:around method="transactionManager" pointcut-ref="pt"/></aop:aspect></aop:config>聲明式事務(XML)
聲明式事務是由Spring操控事務,基于上面的案例改造
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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:property-placeholder location="classpath:*.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="accountService" class="com.itzhuzhu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><!-- <property name="dataSource" ref="dataSource"/>--></bean><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.itzhuzhu.domain"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itzhuzhu.dao"/></bean><!-- <!–AOP配置–><bean id="txAdvice" class="com.itzhuzhu.aop.TxAdvice"><property name="dataSource" ref="dataSource"/></bean><aop:config><aop:pointcut id="pt" expression="execution(* *..transfer(..))"/><aop:aspect ref="txAdvice"><aop:around method="transactionManager" pointcut-ref="pt"/></aop:aspect></aop:config>--><!--TX方式--><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--定義事務管理的通知類--><tx:advice id="txAdvice" transaction-manager="txManager"><!--定義控制的事務--><tx:attributes><tx:method name="*" read-only="false"/><tx:method name="get*" read-only="true"/><tx:method name="find*" read-only="true"/><tx:methodname="transfer"read-only="false"timeout="-1"isolation="DEFAULT"no-rollback-for=""rollback-for=""propagation="REQUIRED"/><!--<tx:method name="transfer" read-only="false"/>--></tx:attributes></tx:advice><aop:config><aop:pointcut id="pt" expression="execution(* com.itzhuzhu.service.*Service.*(..))"/><aop:pointcut id="pt2" expression="execution(* com.itzhuzhu.dao.*.b(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/></aop:config> </beans>使用tx命名空間配置事務專屬通知類
<tx:advice id="txAdvice" transaction-manager="txManager"><tx:attributes><tx:method name="*" read-only="false" /><tx:method name="get*" read-only="true" /><tx:method name="find*" read-only="true" /></tx:attributes> </tx:advice>使用aop:advisor在AOP配置中引用事務專屬通知類
<aop:config><aop:pointcut id="pt" expression="execution(* *..*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config>aop:advice與aop:advisor區別:
-
aop:advice配置的通知類可以是普通java對象,不實現接口,也不使用繼承關系
-
aop:advisor配置的通知類必須實現通知接口
-
MethodBeforeAdvice
-
AfterReturningAdvice
-
ThrowsAdvice
-
tx配置——tx:advice
-
名稱:tx:advice
-
類型:標簽
-
歸屬:beans標簽
-
作用:專用于聲明事務通知
-
格式:
-
基本屬性:
-
id :用于配置aop時指定通知器的id
-
transaction-manager :指定事務管理器bean
-
tx配置——tx:attributes
-
名稱:tx:attributes
-
類型:標簽
-
歸屬:tx:advice標簽
-
作用:定義通知屬性
-
格式:
-
基本屬性:
- 無
tx配置——tx:method
-
名稱:tx:method
-
類型:標簽
-
歸屬:tx:attribute標簽
-
作用:設置具體的事務屬性
-
格式:
-
說明:
通常事務屬性會配置多個,包含1個讀寫的全事務屬性,1個只讀的查詢類事務屬性
tx:method屬性:
<tx:methodname="transfer" 待添加事務的方法名表達式(支持*號通配符),例如get*、*、......read-only="false" 待設置事務的讀寫屬性,true為只讀,false為讀寫timeout="-1" 設置事務超時時長,單位秒isolation="DEFAULT" 設置事務隔離級別,該隔離級設定是基于spring的設定,非數據庫端no-rollback-for="" 設置事務中不回滾的異常,多個異常間使用逗號分割rollback-for="" 設置事務中必回滾的異常,多個異常間使用逗號分割propagation="REQUIRED" 設置事務的傳播行為事務傳播行為
-
事務管理員
-
事務協調員
-
事務傳播行為描述的是事務協調員對事務管理員所攜帶事務的處理態度
事務傳播應用:
-
場景A:生成訂單業務
-
子業務S1:記錄日志到數據庫表X
-
子業務S2:保存訂單數據到數據庫表Y
-
子業務S3:……
-
如果S2或S3或……事務提交失敗,此時S1是否回滾?如何控制?
-
(S1需要新事務)
-
-
場景B:生成訂單業務
-
背景1:訂單號生成依賴數據庫中一個專門用于控制訂單號編號生成的表M獲取
-
背景2:每次獲取完訂單號,表M中記錄的編號自增1
-
子業務S1:從表M中獲取訂單編號
-
子業務S2:保存訂單數據,訂單編號來自于表M
-
子業務S3:……
-
如果S2或S3或……事務提交失敗,此時S1是否回滾?如何控制?
-
(S1需要新事務)
-
聲明式事務(注解)
@Transactional
-
名稱:@Transactional
-
類型:方法注解,類注解,接口注解
-
位置:方法定義上方,類定義上方,接口定義上方(不要寫在實現類,實現類會換,一般寫接口上,接口方法寫詳細的,接口類可以做一個大的控制)
-
作用:設置當前類/接口中所有方法或具體方法開啟事務,并指定相關事務屬性
-
范例:
tx:annotation-driven
-
名稱:tx:annotation-driven
-
類型:標簽
-
歸屬:beans標簽
-
作用:開啟事務注解驅動,并指定對應的事務管理器
-
范例:
<tx:annotation-driven transaction-manager="txManager"/>
聲明式事務(純注解驅動)
-
名稱:@EnableTransactionManagement
-
類型:類注解
-
位置:Spring注解配置類上方
-
作用:開啟注解驅動,等同XML格式中的注解驅動
-
范例:
AccountDao
public interface AccountDao {@Update("update account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);}AccountServiceImpl
@Service("accountService") public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String outName, String inName, Double money) {accountDao.inMoney(outName,money);int i = 1/0;accountDao.outMoney(inName,money);} }JDBCConfig
public class JDBCConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Bean("dataSource")public DataSource getDataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}@Beanpublic PlatformTransactionManager getTransactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);} }MyBatisConfig
public class MyBatisConfig {@Beanpublic SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itzhuzhu.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer getMapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itzhuzhu.dao");return msc;} }SpringConfig
@Configuration @ComponentScan("com.itzhuzhu") @PropertySource("classpath:jdbc.properties") @Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class}) @EnableTransactionManagement public class SpringConfig { } public class TransactionManagerConfig {@Beanpublic PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){return new DataSourceTransactionManager(dataSource);} }總結
以上是生活随笔為你收集整理的Spring事务详解与使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电子科大计算机调试,电子科大计算机学院
- 下一篇: vscode用鼠标滚轮_前端开发神器 V