<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          小姐姐非要問我:spring編程式事務(wù)是啥?

          共 12666字,需瀏覽 26分鐘

           ·

          2020-10-22 01:23


          本文開始,大概用10篇左右的文章來詳解spring中事務(wù)的使用,吃透spring事務(wù)。

          本文內(nèi)容

          詳解spring中編程式事務(wù)的使用。

          spring中使用事務(wù)的2種方式

          spring使事務(wù)操作變的異常容易了,spring中控制事務(wù)主要有2種方式

          • 編程式事務(wù):硬編碼的方式
          • 聲明式事務(wù):大家比較熟悉的注解@Transaction的方式

          編程式事務(wù)

          什么是編程式事務(wù)?

          通過硬編碼的方式使用spring中提供的事務(wù)相關(guān)的類來控制事務(wù)。

          編程式事務(wù)主要有2種用法

          • 方式1:通過PlatformTransactionManager控制事務(wù)
          • 方式2:通過TransactionTemplate控制事務(wù)

          方式1:PlatformTransactionManager

          這種是最原始的方式,代碼量比較大,后面其他方式都是對(duì)這種方式的封裝。

          直接看案例,有詳細(xì)的注釋,大家一看就懂。

          案例代碼位置

          準(zhǔn)備sql

          DROP?DATABASE?IF?EXISTS?javacode2018;
          CREATE?DATABASE?if?NOT?EXISTS?javacode2018;

          USE?javacode2018;
          DROP?TABLE?IF?EXISTS?t_user;
          CREATE?TABLE?t_user(
          ??id?int?PRIMARY?KEY?AUTO_INCREMENT,
          ??name?varchar(256)?NOT?NULL?DEFAULT?''?COMMENT?'姓名'
          );

          maven配置



          ????org.springframework
          ????spring-jdbc
          ????5.2.3.RELEASE



          ????org.springframework
          ????spring-tx
          ????5.2.3.RELEASE

          測(cè)試代碼

          代碼中會(huì)用到JdbcTemplate,對(duì)這個(gè)不理解的可以看一下:JdbcTemplate使用詳解

          @Test
          public?void?test1()?throws?Exception?{
          ????//定義一個(gè)數(shù)據(jù)源
          ????org.apache.tomcat.jdbc.pool.DataSource?dataSource?=?new?org.apache.tomcat.jdbc.pool.DataSource();
          ????dataSource.setDriverClassName("com.mysql.jdbc.Driver");
          ????dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
          ????dataSource.setUsername("root");
          ????dataSource.setPassword("root123");
          ????dataSource.setInitialSize(5);
          ????//定義一個(gè)JdbcTemplate,用來方便執(zhí)行數(shù)據(jù)庫增刪改查
          ????JdbcTemplate?jdbcTemplate?=?new?JdbcTemplate(dataSource);
          ????//1.定義事務(wù)管理器,給其指定一個(gè)數(shù)據(jù)源(可以把事務(wù)管理器想象為一個(gè)人,這個(gè)人來負(fù)責(zé)事務(wù)的控制操作)
          ????PlatformTransactionManager?platformTransactionManager?=?new?DataSourceTransactionManager(dataSource);
          ????//2.定義事務(wù)屬性:TransactionDefinition,TransactionDefinition可以用來配置事務(wù)的屬性信息,比如事務(wù)隔離級(jí)別、事務(wù)超時(shí)時(shí)間、事務(wù)傳播方式、是否是只讀事務(wù)等等。
          ????TransactionDefinition?transactionDefinition?=?new?DefaultTransactionDefinition();
          ????//3.開啟事務(wù):調(diào)用platformTransactionManager.getTransaction開啟事務(wù)操作,得到事務(wù)狀態(tài)(TransactionStatus)對(duì)象
          ????TransactionStatus?transactionStatus?=?platformTransactionManager.getTransaction(transactionDefinition);
          ????//4.執(zhí)行業(yè)務(wù)操作,下面就執(zhí)行2個(gè)插入操作
          ????try?{
          ????????System.out.println("before:"?+?jdbcTemplate.queryForList("SELECT?*?from?t_user"));
          ????????jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"test1-1");
          ????????jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"test1-2");
          ????????//5.提交事務(wù):platformTransactionManager.commit
          ????????platformTransactionManager.commit(transactionStatus);
          ????}?catch?(Exception?e)?{
          ????????//6.回滾事務(wù):platformTransactionManager.rollback
          ????????platformTransactionManager.rollback(transactionStatus);
          ????}
          ????System.out.println("after:"?+?jdbcTemplate.queryForList("SELECT?*?from?t_user"));
          }

          運(yùn)行輸出

          before:[]
          after:[{id=1,?name=test1-1},?{id=2,?name=test1-2}]

          代碼分析

          代碼中主要有5個(gè)步驟

          步驟1:定義事務(wù)管理器PlatformTransactionManager

          事務(wù)管理器相當(dāng)于一個(gè)管理員,這個(gè)管理員就是用來幫你控制事務(wù)的,比如開啟事務(wù),提交事務(wù),回滾事務(wù)等等。

          spring中使用PlatformTransactionManager這個(gè)接口來表示事務(wù)管理器,

          public?interface?PlatformTransactionManager?{

          ?//獲取一個(gè)事務(wù)(開啟事務(wù))
          ?TransactionStatus?getTransaction(@Nullable?TransactionDefinition?definition)
          ???throws?TransactionException
          ;

          ?//提交事務(wù)
          ?void?commit(TransactionStatus?status)?throws?TransactionException;


          ?//回滾事務(wù)
          ?void?rollback(TransactionStatus?status)?throws?TransactionException;

          }

          PlatformTransactionManager多個(gè)實(shí)現(xiàn)類,用來應(yīng)對(duì)不同的環(huán)境

          JpaTransactionManager:如果你用jpa來操作db,那么需要用這個(gè)管理器來幫你控制事務(wù)。

          DataSourceTransactionManager:如果你用是指定數(shù)據(jù)源的方式,比如操作數(shù)據(jù)庫用的是:JdbcTemplate、mybatis、ibatis,那么需要用這個(gè)管理器來幫你控制事務(wù)。

          HibernateTransactionManager:如果你用hibernate來操作db,那么需要用這個(gè)管理器來幫你控制事務(wù)。

          JtaTransactionManager:如果你用的是java中的jta來操作db,這種通常是分布式事務(wù),此時(shí)需要用這種管理器來控制事務(wù)。

          上面案例代碼中我們使用的是JdbcTemplate來操作db,所以用的是DataSourceTransactionManager這個(gè)管理器。

          PlatformTransactionManager?platformTransactionManager?=?new?DataSourceTransactionManager(dataSource);

          步驟2:定義事務(wù)屬性TransactionDefinition

          定義事務(wù)屬性,比如事務(wù)隔離級(jí)別、事務(wù)超時(shí)時(shí)間、事務(wù)傳播方式、是否是只讀事務(wù)等等。

          spring中使用TransactionDefinition接口來表示事務(wù)的定義信息,有個(gè)子類比較常用:DefaultTransactionDefinition。

          關(guān)于事務(wù)屬性細(xì)節(jié)比較多,篇幅比較長,后面會(huì)專門有文章來詳解。

          步驟3:開啟事務(wù)

          調(diào)用事務(wù)管理器的getTransaction方法,即可以開啟一個(gè)事務(wù)

          TransactionStatus?transactionStatus?=?platformTransactionManager.getTransaction(transactionDefinition);

          這個(gè)方法會(huì)返回一個(gè)TransactionStatus表示事務(wù)狀態(tài)的一個(gè)對(duì)象,通過TransactionStatus提供的一些方法可以用來控制事務(wù)的一些狀態(tài),比如事務(wù)最終是需要回滾還是需要提交。

          執(zhí)行了getTransaction后,spring內(nèi)部會(huì)執(zhí)行一些操作,為了方便大家理解,咱們看看偽代碼:

          //有一個(gè)全局共享的threadLocal對(duì)象?resources
          static?final?ThreadLocal>?resources?=?new?NamedThreadLocal<>("Transactional?resources");
          //獲取一個(gè)db的連接
          DataSource?datasource?=?platformTransactionManager.getDataSource();
          Connection?connection?=?datasource.getConnection();
          //設(shè)置手動(dòng)提交事務(wù)
          connection.setAutoCommit(false);
          Map?map?=?new?HashMap<>();
          map.put(datasource,connection);
          resources.set(map);

          上面代碼,將數(shù)據(jù)源datasource和connection映射起來放在了ThreadLocal中,ThreadLocal大家應(yīng)該比較熟悉,用于在同一個(gè)線程中共享數(shù)據(jù);后面我們可以通過resources這個(gè)ThreadLocal獲取datasource其對(duì)應(yīng)的connection對(duì)象。

          步驟4:執(zhí)行業(yè)務(wù)操作

          我們使用jdbcTemplate插入了2條記錄。

          jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"test1-1");
          jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"test1-2");

          大家看一下創(chuàng)建JdbcTemplate的代碼,需要指定一個(gè)datasource

          JdbcTemplate?jdbcTemplate?=?new?JdbcTemplate(dataSource);

          再來看看創(chuàng)建事務(wù)管理器的代碼

          PlatformTransactionManager?platformTransactionManager?=?new?DataSourceTransactionManager(dataSource);

          2者用到的是同一個(gè)dataSource,而事務(wù)管理器開啟事務(wù)的時(shí)候,會(huì)創(chuàng)建一個(gè)連接,將datasource和connection映射之后丟在了ThreadLocal中,而JdbcTemplate內(nèi)部執(zhí)行db操作的時(shí)候,也需要獲取連接,JdbcTemplate會(huì)以自己內(nèi)部的datasource去上面的threadlocal中找有沒有關(guān)聯(lián)的連接,如果有直接拿來用,若沒找到將重新創(chuàng)建一個(gè)連接,而此時(shí)是可以找到的,那么JdbcTemplate就參與到spring的事務(wù)中了。

          步驟5:提交 or 回滾

          //5.提交事務(wù):platformTransactionManager.commit
          platformTransactionManager.commit(transactionStatus);

          //6.回滾事務(wù):platformTransactionManager.rollback
          platformTransactionManager.rollback(transactionStatus);

          方式2:TransactionTemplate

          方式1中部分代碼是可以重用的,所以spring對(duì)其進(jìn)行了優(yōu)化,采用模板方法模式就其進(jìn)行封裝,主要省去了提交或者回滾事務(wù)的代碼。

          案例代碼位置

          測(cè)試代碼

          @Test
          public?void?test1()?throws?Exception?{
          ????//定義一個(gè)數(shù)據(jù)源
          ????org.apache.tomcat.jdbc.pool.DataSource?dataSource?=?new?org.apache.tomcat.jdbc.pool.DataSource();
          ????dataSource.setDriverClassName("com.mysql.jdbc.Driver");
          ????dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
          ????dataSource.setUsername("root");
          ????dataSource.setPassword("root123");
          ????dataSource.setInitialSize(5);
          ????//定義一個(gè)JdbcTemplate,用來方便執(zhí)行數(shù)據(jù)庫增刪改查
          ????JdbcTemplate?jdbcTemplate?=?new?JdbcTemplate(dataSource);
          ????//1.定義事務(wù)管理器,給其指定一個(gè)數(shù)據(jù)源(可以把事務(wù)管理器想象為一個(gè)人,這個(gè)人來負(fù)責(zé)事務(wù)的控制操作)
          ????PlatformTransactionManager?platformTransactionManager?=?new?DataSourceTransactionManager(dataSource);
          ????//2.定義事務(wù)屬性:TransactionDefinition,TransactionDefinition可以用來配置事務(wù)的屬性信息,比如事務(wù)隔離級(jí)別、事務(wù)超時(shí)時(shí)間、事務(wù)傳播方式、是否是只讀事務(wù)等等。
          ????DefaultTransactionDefinition?transactionDefinition?=?new?DefaultTransactionDefinition();
          ????transactionDefinition.setTimeout(10);//如:設(shè)置超時(shí)時(shí)間10s
          ????//3.創(chuàng)建TransactionTemplate對(duì)象
          ????TransactionTemplate?transactionTemplate?=?new?TransactionTemplate(platformTransactionManager,?transactionDefinition);
          ????/**
          ?????*?4.通過TransactionTemplate提供的方法執(zhí)行業(yè)務(wù)操作
          ?????*?主要有2個(gè)方法:
          ?????*?(1).executeWithoutResult(Consumer action):沒有返回值的,需傳遞一個(gè)Consumer對(duì)象,在accept方法中做業(yè)務(wù)操作
          ?????*?(2). T execute(TransactionCallback action):有返回值的,需要傳遞一個(gè)TransactionCallback對(duì)象,在doInTransaction方法中做業(yè)務(wù)操作
          ?????*?調(diào)用execute方法或者executeWithoutResult方法執(zhí)行完畢之后,事務(wù)管理器會(huì)自動(dòng)提交事務(wù)或者回滾事務(wù)。
          ?????*?那么什么時(shí)候事務(wù)會(huì)回滾,有2種方式:
          ?????*?(1)transactionStatus.setRollbackOnly();將事務(wù)狀態(tài)標(biāo)注為回滾狀態(tài)
          ?????*?(2)execute方法或者executeWithoutResult方法內(nèi)部拋出異常
          ?????*?什么時(shí)候事務(wù)會(huì)提交?
          ?????*?方法沒有異常?&&?未調(diào)用過transactionStatus.setRollbackOnly();
          ?????*/

          ????transactionTemplate.executeWithoutResult(new?Consumer()?{
          ????????@Override
          ????????public?void?accept(TransactionStatus?transactionStatus)?{
          ????????????jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"transactionTemplate-1");
          ????????????jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"transactionTemplate-2");

          ????????}
          ????});
          ????System.out.println("after:"?+?jdbcTemplate.queryForList("SELECT?*?from?t_user"));
          }

          運(yùn)行輸出

          after:[{id=1,?name=transactionTemplate-1},?{id=2,?name=transactionTemplate-2}]

          代碼分析

          TransactionTemplate,主要有2個(gè)方法:

          executeWithoutResult:無返回值場(chǎng)景

          executeWithoutResult(Consumer action):沒有返回值的,需傳遞一個(gè)Consumer對(duì)象,在accept方法中做業(yè)務(wù)操作

          transactionTemplate.executeWithoutResult(new?Consumer()?{
          ????@Override
          ????public?void?accept(TransactionStatus?transactionStatus)?{
          ????????//執(zhí)行業(yè)務(wù)操作
          ????}
          });

          execute:有返回值場(chǎng)景

          T execute(TransactionCallback action):有返回值的,需要傳遞一個(gè)TransactionCallback對(duì)象,在doInTransaction方法中做業(yè)務(wù)操作

          Integer?result?=?transactionTemplate.execute(new?TransactionCallback()?{
          ????@Nullable
          ????@Override
          ????public?Integer?doInTransaction(TransactionStatus?status)?{
          ????????return?jdbcTemplate.update("insert?into?t_user?(name)?values?(?)",?"executeWithoutResult-3");
          ????}
          });

          通過上面2個(gè)方法,事務(wù)管理器會(huì)自動(dòng)提交事務(wù)或者回滾事務(wù)。

          什么時(shí)候事務(wù)會(huì)回滾,有2種方式

          方式1

          在execute或者executeWithoutResult內(nèi)部執(zhí)行transactionStatus.setRollbackOnly();將事務(wù)狀態(tài)標(biāo)注為回滾狀態(tài),spring會(huì)自動(dòng)讓事務(wù)回滾

          方式2

          execute方法或者executeWithoutResult方法內(nèi)部拋出任意異常即可回滾。

          什么時(shí)候事務(wù)會(huì)提交?

          方法沒有異常 && 未調(diào)用過transactionStatus.setRollbackOnly();

          編程式事務(wù)正確的使用姿勢(shì)

          如果大家確實(shí)想在系統(tǒng)中使用編程式事務(wù),那么可以參考下面代碼,使用spring來管理對(duì)象,更簡潔一些。

          先來個(gè)配置類,將事務(wù)管理器PlatformTransactionManager、事務(wù)模板TransactionTemplate都注冊(cè)到spring中,重用。

          package?com.javacode2018.tx.demo3;

          import?org.springframework.context.annotation.Bean;
          import?org.springframework.context.annotation.ComponentScan;
          import?org.springframework.context.annotation.Configuration;
          import?org.springframework.jdbc.core.JdbcTemplate;
          import?org.springframework.jdbc.datasource.DataSourceTransactionManager;
          import?org.springframework.transaction.PlatformTransactionManager;
          import?org.springframework.transaction.support.TransactionTemplate;

          import?javax.sql.DataSource;

          @Configuration
          @ComponentScan
          public?class?MainConfig3?{
          ????@Bean
          ????public?DataSource?dataSource()?{
          ????????org.apache.tomcat.jdbc.pool.DataSource?dataSource?=?new?org.apache.tomcat.jdbc.pool.DataSource();
          ????????dataSource.setDriverClassName("com.mysql.jdbc.Driver");
          ????????dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
          ????????dataSource.setUsername("root");
          ????????dataSource.setPassword("root123");
          ????????dataSource.setInitialSize(5);
          ????????return?dataSource;
          ????}

          ????@Bean
          ????public?JdbcTemplate?jdbcTemplate(DataSource?dataSource)?{
          ????????return?new?JdbcTemplate(dataSource);
          ????}

          ????@Bean
          ????public?PlatformTransactionManager?transactionManager(DataSource?dataSource)?{
          ????????return?new?DataSourceTransactionManager(dataSource);
          ????}

          ????@Bean
          ????public?TransactionTemplate?transactionTemplate(PlatformTransactionManager?transactionManager)?{
          ????????return?new?TransactionTemplate(transactionManager);
          ????}
          }

          通常我們會(huì)將業(yè)務(wù)操作放在service中,所以我們也來個(gè)service:UserService。

          package?com.javacode2018.tx.demo3;

          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.jdbc.core.JdbcTemplate;
          import?org.springframework.stereotype.Component;
          import?org.springframework.transaction.support.TransactionTemplate;

          import?java.util.List;

          @Component
          public?class?UserService?{
          ????@Autowired
          ????private?JdbcTemplate?jdbcTemplate;
          ????@Autowired
          ????private?TransactionTemplate?transactionTemplate;

          ????//模擬業(yè)務(wù)操作1
          ????public?void?bus1()?{
          ????????this.transactionTemplate.executeWithoutResult(transactionStatus?->?{
          ????????????//先刪除表數(shù)據(jù)
          ????????????this.jdbcTemplate.update("delete?from?t_user");
          ????????????//調(diào)用bus2
          ????????????this.bus2();
          ????????});
          ????}

          ????//模擬業(yè)務(wù)操作2
          ????public?void?bus2()?{
          ????????this.transactionTemplate.executeWithoutResult(transactionStatus?->?{
          ????????????this.jdbcTemplate.update("insert?into?t_user?(name)?VALUE?(?)",?"java");
          ????????????this.jdbcTemplate.update("insert?into?t_user?(name)?VALUE?(?)",?"spring");
          ????????????this.jdbcTemplate.update("insert?into?t_user?(name)?VALUE?(?)",?"mybatis");
          ????????});
          ????}

          ????//查詢表中所有數(shù)據(jù)
          ????public?List?userList()?{
          ????????return?jdbcTemplate.queryForList("select?*?from?t_user");
          ????}
          }

          bus1中會(huì)先刪除數(shù)據(jù),然后調(diào)用bus2,此時(shí)bus1中的所有操作和bus2中的所有操作會(huì)被放在一個(gè)事務(wù)中執(zhí)行,這是spring內(nèi)部默認(rèn)實(shí)現(xiàn)的,bus1中調(diào)用executeWithoutResult的時(shí)候,會(huì)開啟一個(gè)事務(wù),而內(nèi)部又會(huì)調(diào)用bus2,而bus2內(nèi)部也調(diào)用了executeWithoutResult,bus內(nèi)部會(huì)先判斷一下上線文環(huán)境中有沒有事務(wù),如果有就直接參與到已存在的事務(wù)中,剛好發(fā)現(xiàn)有bus1已開啟的事務(wù),所以就直接參與到bus1的事務(wù)中了,最終bus1和bus2會(huì)在一個(gè)事務(wù)中運(yùn)行。

          上面bus1代碼轉(zhuǎn)換為sql腳本如下:

          start?transaction;?//開啟事務(wù)
          delete?from?t_user;
          insert?into?t_user?(name)?VALUE?('java');
          insert?into?t_user?(name)?VALUE?('spring');
          insert?into?t_user?(name)?VALUE?('mybatis');
          commit;

          來個(gè)測(cè)試案例,看一下效果

          package?com.javacode2018.tx.demo3;

          import?org.junit.Test;
          import?org.springframework.context.annotation.AnnotationConfigApplicationContext;

          public?class?Demo3Test?{
          ????@Test
          ????public?void?test1()?{
          ????????AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(MainConfig3.class);
          ????????UserService?userService?=?context.getBean(UserService.class);
          ????????userService.bus1();
          ????????System.out.println(userService.userList());
          ????}
          }

          運(yùn)行test1()輸出

          [{id=18,?name=java},?{id=19,?name=spring},?{id=20,?name=mybatis}]

          上面代碼中,bus1或者bus2中,如果有異?;蛘邎?zhí)行transactionStatus.setRollbackOnly(),此時(shí)整個(gè)事務(wù)都會(huì)回滾,大家可以去試試!

          總結(jié)一下

          大家看了之后,會(huì)覺得這樣用好復(fù)雜啊,為什么要這么玩?

          的確,看起來比較復(fù)雜,代碼中融入了大量spring的代碼,耦合性比較強(qiáng),不利于擴(kuò)展,本文的目標(biāo)并不是讓大家以后就這么用,主要先讓大家從硬編碼上了解spring中事務(wù)是如何控制的,后面學(xué)起來才會(huì)更容易。

          我們用的最多的是聲明式事務(wù),聲明式事務(wù)的底層還是使用上面這種方式來控制事務(wù)的,只不過對(duì)其進(jìn)行了封裝,讓我們用起來更容易些。


          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区三清视频 | 亚欧洲精品在线视频免费观看 | 大鸡吧操影院 | 日韩欧美性 | 精品欧美无人区乱码毛片 |