解讀Spring事務(wù)是如何實現(xiàn)的
Spring事務(wù)如何實現(xiàn)
1.Spring事務(wù)底層是基于數(shù)據(jù)庫事務(wù)和AOP機制的
2.首先對于使用了@Transactional注解的Bean,Spring會創(chuàng)建一個代理對象作為Bean
3.當調(diào)用代理對象的方法時,會先判斷該方法上是否加了@Transactional注解
4.如果加了,那么則利用事務(wù)管理器創(chuàng)建一個數(shù)據(jù)庫連接
5.并且修改數(shù)據(jù)庫連接的autocommit屬性為false,禁止此連接的自動提交,這是實現(xiàn)Spring事務(wù)非常重要的一步
6.然后執(zhí)行當前方法,方法中會執(zhí)行sql
7.執(zhí)行完當前方法后,如果沒有出現(xiàn)異常就直接提交事務(wù)
8.如果出現(xiàn)了異常,并且這個異常是需要回滾的就會回滾事務(wù),否則仍然提交事務(wù)
注:
1.Spring事務(wù)的隔離級別對應(yīng)的就是數(shù)據(jù)庫的隔離級別
2.Spring事務(wù)的傳播機制是Spring事務(wù)自己實現(xiàn)的,也是Spring事務(wù)中最復(fù)雜的
3.Spring事務(wù)的傳播機制是基于數(shù)據(jù)庫連接來做的,一個數(shù)據(jù)庫連接就是一個事務(wù),如果傳播機制配置為需要新開一個事務(wù),那么實際上就是先新建一個數(shù)據(jù)庫連接,在此新數(shù)據(jù)庫連接上執(zhí)行sql
Spring事務(wù)實現(xiàn)的幾種方式
事務(wù)幾種實現(xiàn)方式
(1)編程式事務(wù)管理對基于 POJO 的應(yīng)用來說是唯一選擇。我們需要在代碼中調(diào)用beginTransaction()、commit()、rollback()等事務(wù)管理相關(guān)的方法,這就是編程式事務(wù)管理。
(2)基于 TransactionProxyFactoryBean的聲明式事務(wù)管理
(3)基于 @Transactional 的聲明式事務(wù)管理
(4)基于Aspectj AOP配置事務(wù)
編程式事務(wù)管理
1、transactionTemplate
此種方式是自動的事務(wù)管理,無需手動開啟、提交、回滾。
配置事務(wù)管理器
<!-- 配置事務(wù)管理器 ,封裝了所有的事務(wù)操作,依賴于連接池 --> ?? ? ? <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ?? ? ? ?? ??? ?<property name="dataSource" ref="dataSource"></property> ?? ? ? </bean>
配置事務(wù)模板對象
<!-- 配置事務(wù)模板對象 --> ? ? ? ?<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> ? ? ? ? ? ? <property name="transactionManager" ref="transactionManager"></property> ? ? ? ? </bean>
測試
@Controller @RequestMapping("/tx") @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class TransactionController { ?? ?@Resource ?? ?public TransactionTemplate transactionTemplate; ?? ?@Resource ?? ?public DataSource dataSource; ?? ?private static JdbcTemplate jdbcTemplate; ?? ?private static final String INSERT_SQL = "insert into cc(id) values(?)"; ?? ?private static final String COUNT_SQL = "select count(*) from cc"; ?? ?@Test ?? ?public void TransactionTemplateTest(){ ?? ??? ?//獲取jdbc核心類對象,進而操作數(shù)據(jù)庫 ?? ??? ?jdbcTemplate = new JdbcTemplate(dataSource); ?? ??? ?//通過注解 獲取xml中配置的 事務(wù)模板對象 ?? ??? ?transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); ?? ??? ?//重寫execute方法實現(xiàn)事務(wù)管理 ?? ??? ?transactionTemplate.execute(new TransactionCallbackWithoutResult() { ?? ??? ??? ?@Override ?? ??? ??? ?protected void doInTransactionWithoutResult(TransactionStatus status) { ?? ??? ??? ??? ?jdbcTemplate.update(INSERT_SQL, "33"); ? //字段sd為int型,所以插入肯定失敗報異常,自動回滾,代表TransactionTemplate自動管理事務(wù) ?? ??? ??? ?} ?? ??? ?}); ?? ??? ?int i = jdbcTemplate.queryForInt(COUNT_SQL); ?? ??? ?System.out.println("表中記錄總數(shù):"+i); ?? ?} }
2、PlatformTransactionManager
使用 事務(wù)管理器 PlatformTransactionManager 對象,PlatformTransactionManager是DataSourceTransactionManager實現(xiàn)的接口類
此方式,可手動開啟、提交、回滾事務(wù)。
只需要:配置事務(wù)管理
<!-- 配置事務(wù)管理 ,封裝了所有的事務(wù)操作,依賴于連接池 --> ?? ? ? <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ?? ? ? ?? ??? ?<property name="dataSource" ref="dataSource"></property> ?? ? ? </bean>
測試
@Controller @RequestMapping("/tx") @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class TransactionController { ? ?@Resource ?? ?public PlatformTransactionManager transactionManager;//這里就是將配置數(shù)據(jù)管理對象注入進來, ?? ? ?? ?@Resource ?? ?public DataSource dataSource; ?? ? ?? ?private static JdbcTemplate jdbcTemplate; ?? ?private static final String INSERT_SQL = "insert into cc(id) values(?)"; ?? ?private static final String COUNT_SQL = "select count(*) from cc"; ?? ?@Test ?? ?public void showTransaction(){ ?? ??? ?//定義使用隔離級別,傳播行為 ?? ??? ?DefaultTransactionDefinition def = new DefaultTransactionDefinition(); ?? ??? ?def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); ?? ??? ?def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); ?? ??? ?//事務(wù)狀態(tài)類,通過PlatformTransactionManager的getTransaction方法根據(jù)事務(wù)定義獲取;獲取事務(wù)狀態(tài)后,Spring根據(jù)傳播行為來決定如何開啟事務(wù) ?? ??? ?TransactionStatus transaction = transactionManager.getTransaction(def); ?? ??? ?jdbcTemplate = new JdbcTemplate(dataSource); ?? ??? ?int i = jdbcTemplate.queryForInt(COUNT_SQL); ?? ??? ?System.out.println("表中記錄總數(shù):"+i); ?? ??? ?try { ?? ??? ??? ?jdbcTemplate.update(INSERT_SQL,"2"); ?? ??? ??? ?jdbcTemplate.update(INSERT_SQL,"是否");//出現(xiàn)異常,因為字段為int類型,會報異常,自動回滾 ?? ??? ??? ?transactionManager.commit(transaction); ?? ??? ?}catch (Exception e){ ?? ??? ??? ?e.printStackTrace(); ?? ??? ??? ?transactionManager.rollback(transaction); ?? ??? ?} ?? ??? ?int i1 = jdbcTemplate.queryForInt(COUNT_SQL); ?? ??? ?System.out.println("表中記錄總數(shù):"+i1); ?? ?} }
聲明式事務(wù)管理
1、基于Aspectj AOP開啟事務(wù)
配置事務(wù)通知
<!-- ?? ? ? 配置事務(wù)增強 --> ?? ? ? <tx:advice id="txAdvice" ?transaction-manager="transactionManager"> ? ? ? ?? ?<tx:attributes> ?? ? ? ? ??? ?<tx:method name="*" propagation="REQUIRED" rollback-for="Exception" /> ? ? ? ?? ?</tx:attributes> ? ? ? ?</tx:advice>
配置織入
<!-- ? ? ? aop代理事務(wù)。掃描 cn.sys.service 路徑下所有的方法 --> ? ? ? ?<aop:config> ? ? ? ?<!-- ? ? 掃描 cn.sys.service 路徑下所有的方法,并加入事務(wù)處理 --> ? ? ? ?? ?<aop:pointcut id="tx" ?expression="execution(* cn.sys.service.*.*(..))" /> ? ? ? ?? ?<aop:advisor advice-ref="txAdvice" pointcut-ref="tx" /> ? ? ? </aop:config>
一個完整的例子
<?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: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-3.2.xsd ? ? ? ? ? ?http://www.springframework.org/schema/aop ? ? ? ? ? ?http://www.springframework.org/schema/aop/spring-aop-3.2.xsd ? ? ? ? ? ?http://www.springframework.org/schema/context ? ? ? ? ? ?http://www.springframework.org/schema/context/spring-context-3.2.xsd ? ? ? ? ? ?http://www.springframework.org/schema/tx ? ? ? ? ? ?http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> ? ? ? ? ? ? ? ? ? ?<!-- 創(chuàng)建加載外部Properties文件對象 --> ? ? ? ?<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> ? ? ? ??? ??? ?<property name="location" value="classpath:dataBase.properties"></property> ? ? ? ?</bean> ? ? <!-- 引入redis屬性配置文件 --> ? ? <import resource="classpath:redis-context.xml"/> ? ? ? ?<!-- 配置數(shù)據(jù)庫連接資源 --> ? ? ? ?<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" scope="singleton"> ? ? ? ??? ??? ?<property name="driverClassName" value="${driver}"></property> ? ? ? ??? ??? ?<property name="url" value="${url}"></property> ? ? ? ??? ??? ?<property name="username" value="${username}"></property> ? ? ? ??? ??? ?<property name="password" value="${password}"></property> ? ? ? ??? ??? ?<property name="maxActive" value="${maxActive}"></property> ? ? ? ??? ??? ?<property name="maxIdle" value="${maxIdle}"></property> ? ? ? ??? ??? ?<property name="minIdle" value="${minIdle}"></property> ? ? ? ??? ??? ?<property name="initialSize" value="${initialSize}"></property> ? ? ? ??? ??? ?<property name="maxWait" value="${maxWait}"></property> ? ? ? ??? ??? ?<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}"></property> ? ? ? ??? ??? ?<property name="removeAbandoned" value="${removeAbandoned}"></property> ? ? ? ??? ??? ?<!-- 配置sql心跳包 --> ? ? ? ??? ??? ?<property name= "testWhileIdle" value="true"/> ?? ??? ??? ?<property name= "testOnBorrow" value="false"/> ?? ??? ??? ?<property name= "testOnReturn" value="false"/> ?? ??? ??? ?<property name= "validationQuery" value="select 1"/> ?? ??? ??? ?<property name= "timeBetweenEvictionRunsMillis" value="60000"/> ?? ??? ??? ?<property name= "numTestsPerEvictionRun" value="${maxActive}"/> ? ? ? ?</bean> <!--創(chuàng)建SQLSessionFactory對象 ?--> ? ? ? ?<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> ? ? ? ??? ??? ?<property name="dataSource" ref="dataSource"></property> ? ? ? ??? ??? ?<property name="configLocation" value="classpath:MyBatis_config.xml"></property> ? ? ? ?</bean> ? ? ? ?<!-- 創(chuàng)建MapperScannerConfigurer對象 --> ? ? ? ?<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> ? ? ? ??? ??? ?<property name="basePackage" value="cn.sys.dao"></property> ? ? ? ?</bean> ? ? ? ?<!-- 配置掃描器 ? IOC 注解 --> ?? ? ? <context:component-scan base-package="cn.sys" /> ?? ? ? <!-- 配置事務(wù)管理 ,封裝了所有的事務(wù)操作,依賴于連接池 --> ?? ? ? <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ?? ? ? ?? ??? ?<property name="dataSource" ref="dataSource"></property> ?? ? ? </bean> ? ? ? ? <!-- 配置事務(wù)模板對象 --> ? ? ? ?<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> ? ? ? ? ? ? <property name="transactionManager" ref="transactionManager"></property> ? ? ? ? </bean> <!-- ?? ? ?配置事務(wù)增強 --> ?? ? ? <tx:advice id="txAdvice" ?transaction-manager="transactionManager"> ? ? ? ?? ?<tx:attributes> ?? ? ? ? ??? ?<tx:method name="*" propagation="REQUIRED" rollback-for="Exception" /> ? ? ? ?? ?</tx:attributes> ? ? ? ?</tx:advice> ? ? ? ? <!-- ? ? aop代理事務(wù) --> ? ? ? ?<aop:config> ? ? ? ?? ?<aop:pointcut id="tx" ?expression="execution(* cn.sys.service.*.*(..))" /> ? ? ? ?? ?<aop:advisor advice-ref="txAdvice" pointcut-ref="tx" /> ? ? ? </aop:config> </beans>
這樣就算是給 cn.sys.service下所有的方法加入了事務(wù)
也可以用springboot的配置類方式:
package com.junjie.test; @Configurationpublic? class TxAnoConfig { ? ? ? ? /*事務(wù)攔截類型*/ ? ? ? ? @Bean("txSource") ? ? public TransactionAttributeSource transactionAttributeSource() { ?? ? ? ? ? NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();? ? ? ? ? /*只讀事務(wù),不做更新操作*/ ? ? ? ? ? ? ? ? RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, Collections.singletonList(new RollbackRuleAttribute(Exception.class))); ?? ? ? ? ? requiredTx.setTimeout(60); ?? ? ? ? ? Map<String, TransactionAttribute> txMap = new HashMap<>(); ?? ? ? ? ? txMap.put("*", requiredTx); ? ? ? ? ? source.setNameMap(txMap); ? ? ? ? ? ? return source;? ? ? } ?? ? ? /** ? ? * 切面攔截規(guī)則 參數(shù)會自動從容器中注入 ? ? */ ? ? ? ? @Bean? ? ? public AspectJExpressionPointcutAdvisor pointcutAdvisor(TransactionInterceptor txInterceptor) {? ? ? ? ? AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor(); ? ? ? ? ? pointcutAdvisor.setAdvice(txInterceptor); ? ? ? ? ? ? pointcutAdvisor.setExpression("execution (* com.cmb..*Controller.*(..))"); ?? ? ? ? ? return pointcutAdvisor; ?? ? ? }? ? ? /*事務(wù)攔截器*/? ? ? @Bean("txInterceptor") ?? ? ? TransactionInterceptor getTransactionInterceptor(PlatformTransactionManager tx) { ? ? ? ? ? ? return new TransactionInterceptor(tx, transactionAttributeSource());? ? ? } }
2、基于注解的 @Transactional 的聲明式事務(wù)管理
@Transactional public int saveRwHist(List list) { return rwDao.saveRwHist(list); }
這個注解的開啟需要在spring.xml里加上一個開啟注解事務(wù)的配置
以上的開啟事務(wù)方式,僅需要了解即可,如今在工作中,一般不會用到這幾種方式,過于繁瑣。一般都是直接用springboot自帶的@Transactional 注解,就可以完成這些事務(wù)管理操作。但是如果想知道事務(wù)底層的實現(xiàn)原理,以上的幾種原始方式,還是可以參考的。
總結(jié)
這些僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
如何解決報錯:java.net.BindException:無法指定被請求的地址問題
在Linux虛擬機上安裝并啟動Tomcat時遇到啟動失敗的問題,通過檢查端口及配置文件未發(fā)現(xiàn)異常,后發(fā)現(xiàn)/etc/hosts文件中缺少localhost的映射,添加后重啟Tomcat成功,Tomcat啟動時會檢查localhost的IP映射,缺失或錯誤都可能導(dǎo)致啟動失敗2024-10-10Java利用Map實現(xiàn)計算文本中字符個數(shù)
這篇文章主要為大家詳細介紹了Java如何利用Map集合實現(xiàn)計算文本中字符個數(shù),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08Jenkins一鍵打包部署SpringBoot應(yīng)用的方法步驟
本文主要介紹了使用Jenkins一鍵打包部署SpringBoot應(yīng)用的方法步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12Java中的while無限循環(huán)結(jié)構(gòu)及實例
這篇文章主要介紹了Java中的while無限循環(huán)結(jié)構(gòu)及實例,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01