<del id="nnjnj"></del><track id="nnjnj"></track>

<p id="nnjnj"></p>

<address id="nnjnj"></address>

    <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

      <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>

      • 自動秒收錄
      • 軟件:1973
      • 資訊:57811|
      • 收錄網站:279872|

      IT精英團

      長文解讀春季交易!隔離?傳播性?網被消滅了!

      長文解讀春季交易!隔離?傳播性?網被消滅了!

      瀏覽次數:
      評論次數:
      編輯: 樂詠
      信息來源: ITPUB
      更新日期: 2022-09-16 15:34:49
      摘要

      1.什么是事務2.Spring中的事務2.1兩種用法2.2三大基礎設施3.編程式事務4.聲明式事務4.1XML配置4.2Java配置4.3混合配置5.事務屬性5.1隔離性5

      • 正文開始
      • 相關閱讀
      • 推薦作品

      1.什么是交易?

      2.春季交易

      2.1兩種用法

      2.2三大基礎設施

      3.程序化交易

      4.聲明性事務

      4.1 XML配置

      4.2 Java配置

      4.3混合配置

      5.交易屬性

      5.1隔離

      5.2傳播

      5.3回滾規則

      5.4只讀?

      5.5超時時間

      6.預防措施

      7.摘要

      事務的重要性不言而喻,Spring也為事務提供了豐富的支持,各種支持的屬性都有。

      但是,很多朋友都知道,這里有兩個特殊的屬性:

      隔離

      傳染性

      它有多圓?宋大哥已經懶得寫文章總結了。不過最近有朋友問這個問題,正好有時間總結一下。概念我就不干巴巴的給你講了,宋歌會通過具體案例給你演示以下所有內容。

      好了,廢話不多說,請看大屏幕。

      1.什么是交易?

      數據庫是指作為單個邏輯工作單元執行的一系列操作,這些操作要么一起成功,要么一起失敗。這是一個不可分割的工作單元。

      在我們的日常工作中,有很多涉及交易的場景。在一個服務中,經常需要調用不同的dao層方法,這些方法要么同時成功,要么同時失敗。我們需要在服務層確保這一點。

      說到交易,最典型的例子就是轉賬:

      張三要轉500元給李四。這里涉及到兩個操作,從張三的賬戶上減去500元,在李四的賬戶上增加500元。這兩個操作要么同時成功,要么同時失敗。我們如何確保他們同時成功或失???答案是生意。

      交易有四大特征(ACID):

      一筆交易中的所有操作在原子性(Atomicity):要么完成,要么未完成,不會在中間環節結束。如果在事務執行過程中發生錯誤,它將回滾到事務開始前的狀態,就好像該事務從未被執行過一樣。也就是說,事務是不可分的,不可約的。

      一致性(Consistency):事務開始之前和結束之后,數據庫的完整性沒有被破壞。這意味著寫入的數據必須完全符合所有預設的約束、觸發器、級聯回滾等。

      隔離性(Isolation):允許多個并發事務同時讀寫和修改其數據,隔離可以防止多個事務并發執行時交叉執行導致的數據不一致。事務隔離分為不同的級別,包括未提交讀、提交讀、可重復讀和可序列化。

      持久性(Durability):交易完成后,對數據的修改是永久的,即使系統出現故障,也不會丟失。

      這是交易的四個特征。

      2.春季交易

      2.1兩種用法

      作為Java開發的基礎設施,Spring也為事務提供了很好的支持。一般來說,Spring支持兩種類型的事務,聲明式事務和編程式事務。

      編程事務類似于Jdbc事務,需要將事務代碼嵌入到業務邏輯中,這樣代碼耦合度高,而聲明式事務通過AOP的思想可以有效地將事務與業務邏輯代碼解耦。因此,在實際開發中,聲明式事務被廣泛使用,而編程式事務很少使用??紤]到文章的完整性,本文將介紹這兩種交易方式。

      2.2三大基礎設施

      Spring中的事務支持提供了三種基礎設施。讓我們先來看看它們。

    1. PlatformTransactionManager
    2. TransactionDefinition
    3. TransactionStatus
    4. 這三個核心類是 Spring 處理事務的核心類。

      2.2.1 PlatformTransactionManager

      PlatformTransactionManager 是事務處理的核心,它有諸多的實現類,如下:

      PlatformTransactionManager 的定義如下:

      public interface PlatformTransactionManager {
       TransactionStatus getTransaction(@Nullable TransactionDefinition definition);
       void commit(TransactionStatus status) throws TransactionException;
       void rollback(TransactionStatus status) throws TransactionException;
      }

      可以看到 PlatformTransactionManager中定義了基本的事務操作方法,這些事務操作方法都是平臺無關的,具體的實現都是由不同的子類來實現的。

      這就像 JDBC 一樣,SUN 公司制定標準,其他數據庫廠商提供具體的實現。這么做的好處就是我們 Java 程序員只需要掌握好這套標準即可,不用去管接口的具體實現。以 PlatformTransactionManager為例,它有眾多實現,如果你使用的是 JDBC 那么可以將 DataSourceTransactionManager作為事務管理器;如果你使用的是 Hibernate,那么可以將 HibernateTransactionManager作為事務管理器;如果你使用的是 JPA,那么可以將 JpaTransactionManager作為事務管理器。DataSourceTransactionManager、HibernateTransactionManager以及 JpaTransactionManager都是 PlatformTransactionManager的具體實現,但是我們并不需要掌握這些具體實現類的用法,我們只需要掌握好 PlatformTransactionManager的用法即可。

      PlatformTransactionManager中主要有如下三個方法:

      1.getTransaction()

      getTransaction() 是根據傳入的 TransactionDefinition 獲取一個事務對象,TransactionDefinition 中定義了一些事務的基本規則,例如傳播性、隔離級別等。

      2.commit()

      commit() 方法用來提交事務。

      3.rollback()

      rollback() 方法用來回滾事務。

      2.2.2 TransactionDefinition

      TransactionDefinition用來描述事務的具體規則,也稱作事務的屬性。事務有哪些屬性呢?看下圖:

      可以看到,主要是五種屬性:

      1. 隔離性
      2. 傳播性
      3. 回滾規則
      4. 超時時間
      5. 是否只讀

      這五種屬性接下來松哥會和大家詳細介紹。

      TransactionDefinition類中的方法如下:

      可以看到一共有五個方法:

      1. getIsolationLevel(),獲取事務的隔離級別
      2. getName(),獲取事務的名稱
      3. getPropagationBehavior(),獲取事務的傳播性
      4. getTimeout(),獲取事務的超時時間
      5. isReadOnly(),獲取事務是否是只讀事務

      TransactionDefinition 也有諸多的實現類,如下:

      如果開發者使用了編程式事務的話,直接使用 DefaultTransactionDefinition即可。

      2.2.3 TransactionStatus

      TransactionStatus 可以直接理解為事務本身,該接口源碼如下:

      public interface TransactionStatus extends SavepointManager, Flushable {
       boolean isNewTransaction();
       boolean hasSavepoint();
       void setRollbackOnly();
       boolean isRollbackOnly();
       void flush();
       boolean isCompleted();
      }
      1. isNewTransaction() 方法獲取當前事務是否是一個新事務。
      2. hasSavepoint() 方法判斷是否存在 savePoint()。
      3. setRollbackOnly() 方法設置事務必須回滾。
      4. isRollbackOnly() 方法獲取事務只能回滾。
      5. flush() 方法將底層會話中的修改刷新到數據庫,一般用于 Hibernate/JPA 的會話,對如 JDBC 類型的事務無任何影響。
      6. isCompleted() 方法用來獲取是一個事務是否結束。

      這就是 Spring 中支持事務的三大基礎設施。

      3. 編程式事務

      我們先來看看編程式事務怎么玩。

      通過 PlatformTransactionManager 或者 TransactionTemplate 可以實現編程式事務。如果是在 Spring Boot 項目中,這兩個對象 Spring Boot 會自動提供,我們直接使用即可。但是如果是在傳統的 SSM 項目中,則需要我們通過配置來提供這兩個對象,松哥給一個簡單的配置參考,如下(簡單起見,數據庫操作我們使用 JdbcTemplate):

      <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
          <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql:///spring_tran?serverTimezone=Asia/Shanghai"/>
          <property name="username" value="root"/>
          <property name="password" value="123"/>
      </bean>
      <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
          <property name="dataSource" ref="dataSource"/>
      </bean>
      <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
          <property name="transactionManager" ref="transactionManager"/>
      </bean>
      <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
          <property name="dataSource" ref="dataSource"/>
      </bean>

      有了這兩個對象,接下來的代碼就簡單了:

      @Service
      public class TransferService {
          @Autowired
          JdbcTemplate jdbcTemplate;
          @Autowired
          PlatformTransactionManager txManager;

          public void transfer() {
              DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
              TransactionStatus status = txManager.getTransaction(definition);
              try {
                  jdbcTemplate.update("update user set account=account+100 where username='zhangsan'");
                  int i = 1 / ;
                  jdbcTemplate.update("update user set account=account-100 where username='lisi'");
                  txManager.commit(status);
              } catch (DataAccessException e) {
                  e.printStackTrace();
                  txManager.rollback(status);
              }
          }
      }

      這段代碼很簡單,沒啥好解釋的,在 try...catch...中進行業務操作,沒問題就 commit,有問題就 rollback。如果我們需要配置事務的隔離性、傳播性等,可以在 DefaultTransactionDefinition 對象中進行配置。

      上面的代碼是通過 PlatformTransactionManager 實現的編程式事務,我們也可以通過 TransactionTemplate 來實現編程式事務,如下:

      @Service
      public class TransferService {
          @Autowired
          JdbcTemplate jdbcTemplate;
          @Autowired
          TransactionTemplate tranTemplate;
          public void transfer() {
              tranTemplate.execute(new TransactionCallbackWithoutResult() {
                  @Override
                  protected void doInTransactionWithoutResult(TransactionStatus status) {
                      try {
                          jdbcTemplate.update("update user set account=account+100 where username='zhangsan'");
                          int i = 1 / ;
                          jdbcTemplate.update("update user set account=account-100 where username='lisi'");
                      } catch (DataAccessException e) {
                          status.setRollbackOnly();
                          e.printStackTrace();
                      }
                  }
              });
          }
      }

      直接注入 TransactionTemplate,然后在 execute 方法中添加回調寫核心的業務即可,當拋出異常時,將當前事務標注為只能回滾即可。注意,execute 方法中,如果不需要獲取事務執行的結果,則直接使用 TransactionCallbackWithoutResult 類即可,如果要獲取事務執行結果,則使用 TransactionCallback 即可。

      這就是兩種編程式事務的玩法。

      編程式事務由于代碼入侵太嚴重了,因為在實際開發中使用的很少,我們在項目中更多的是使用聲明式事務。

      4. 聲明式事務

      聲明式事務如果使用 XML配置,可以做到無侵入;如果使用 Java配置,也只有一個 @Transactional注解侵入而已,相對來說非常容易。

      以下配置針對傳統 SSM 項目(因為在 Spring Boot 項目中,事務相關的組件已經配置好了):

      4.1 XML 配置

      XML 配置聲明式事務大致上可以分為三個步驟,如下:

      1. 配置事務管理器
      <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
          <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql:///spring_tran?serverTimezone=Asia/Shanghai"/>
          <property name="username" value="root"/>
          <property name="password" value="123"/>
      </bean>
      <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
          <property name="dataSource" ref="dataSource"/>
      </bean>
      1. 配置事務通知
      <tx:advice transaction-manager="transactionManager" id="txAdvice">
          <tx:attributes>
              <tx:method name="m3"/>
              <tx:method name="m4"/>
          </tx:attributes>
      </tx:advice>
      1. 配置 AOP
      <aop:config>
          <aop:pointcut id="pc1" expression="execution(* org.javaboy.demo.*.*(..))"/>
          <aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
      </aop:config>

      第二步和第三步中定義出來的方法交集,就是我們要添加事務的方法。

      配置完成后,如下一些方法就自動具備事務了:

      public class UserService {
          public void m3(){
              jdbcTemplate.update("update user set money=997 where username=?", "zhangsan");
          }
      }

      4.2 Java 配置

      我們也可以使用 Java 配置來實現聲明式事務:

      @Configuration
      @ComponentScan
      //開啟事務注解支持
      @EnableTransactionManagement
      public class JavaConfig {
          @Bean
          DataSource dataSource() {
              DriverManagerDataSource ds = new DriverManagerDataSource();
              ds.setPassword("123");
              ds.setUsername("root");
              ds.setUrl("jdbc:mysql:///test01?serverTimezone=Asia/Shanghai");
              ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
              return ds;
          }

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

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

      這里要配置的東西其實和 XML 中配置的都差不多,最最關鍵的就兩個:

      • 事務管理器 PlatformTransactionManager。
      • @EnableTransactionManagement 注解開啟事務支持。

      配置完成后,接下來,哪個方法需要事務就在哪個方法上添加 @Transactional注解即可,向下面這樣:

      @Transactional(noRollbackFor = ArithmeticException.class)
      public void update4() {
          jdbcTemplate.update("update account set money = ? where username=?;", 998, "lisi");
          int i = 1 / ;
      }

      當然這個稍微有點代碼入侵,不過問題不大,日常開發中這種方式使用較多。當@Transactional注解加在類上面的時候,表示該類的所有方法都有事務,該注解加在方法上面的時候,表示該方法有事務。

      4.3 混合配置

      也可以 Java 代碼和 XML 混合配置來實現聲明式事務,就是一部分配置用 XML 來實現,一部分配置用 Java 代碼來實現:

      假設 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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

          <!--
          開啟事務的注解配置,添加了這個配置,就可以直接在代碼中通過 @Transactional 注解來開啟事務了
          -->
          <tx:annotation-driven />

      </beans>

      那么 Java 代碼中的配置如下:

      @Configuration
      @ComponentScan
      @ImportResource(locations = "classpath:applicationContext3.xml")
      public class JavaConfig {
          @Bean
          DataSource dataSource() {
              DriverManagerDataSource ds = new DriverManagerDataSource();
              ds.setPassword("123");
              ds.setUsername("root");
              ds.setUrl("jdbc:mysql:///test01?serverTimezone=Asia/Shanghai");
              ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
              return ds;
          }

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

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

      Java 配置中通過 @ImportResource 注解導入了 XML 配置,XML 配置中的內容就是開啟 @Transactional注解的支持,所以 Java 配置中省略了 @EnableTransactionManagement 注解。

      這就是聲明式事務的幾種配置方式。好玩吧!

      5. 事務屬性

      在前面的配置中,我們只是簡單說了事務的用法,并沒有和大家詳細聊一聊事務的一些屬性細節,那么接下來我們就來仔細捋一捋事務中的五大屬性。

      5.1 隔離性

      首先就是事務的隔離性,也就是事務的隔離級別。

      MySQL 中有四種不同的隔離級別,這四種不同的隔離級別在 Spring 中都得到了很好的支持。Spring 中默認的事務隔離級別是 default,即數據庫本身的隔離級別是啥就是啥,default 就能滿足我們日常開發中的大部分場景。

      不過如果項目有需要,我們也可以調整事務的隔離級別。

      調整方式如下:

      5.1.1 編程式事務隔離級別

      如果是編程式事務,通過如下方式修改事務的隔離級別:

      TransactionTemplate

      transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);

      TransactionDefinition 中定義了各種隔離級別。

      PlatformTransactionManager

      public void update2() {
          //創建事務的默認配置
          DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
          definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
          TransactionStatus status = platformTransactionManager.getTransaction(definition);
          try {
              jdbcTemplate.update("update account set money = ? where username=?;", 999, "zhangsan");
              int i = 1 / ;
              //提交事務
              platformTransactionManager.commit(status);
          } catch (DataAccessException e) {
                e.printStackTrace();
              //回滾
              platformTransactionManager.rollback(status);
          }
      }

      這里是在 DefaultTransactionDefinition 對象中設置事務的隔離級別。

      5.1.2 聲明式事務隔離級別

      如果是聲明式事務通過如下方式修改隔離級別:

      XML:

      <tx:advice id="txAdvice" transaction-manager="transactionManager">
          <tx:attributes>
              <!--以 add 開始的方法,添加事務-->
              <tx:method name="add*"/>
              <tx:method name="insert*" isolation="SERIALIZABLE"/>
          </tx:attributes>
      </tx:advice>

      Java:

      @Transactional(isolation = Isolation.SERIALIZABLE)
      public void update4() {
          jdbcTemplate.update("update account set money = ? where username=?;", 998, "lisi");
          int i = 1 / ;
      }

      關于事務的隔離級別,如果大家還不熟悉,可以參考松哥之前的文章:四個案例看懂 MySQL 事務隔離級別。

      5.2 傳播性

      先來說說何謂事務的傳播性:

      ?

      事務傳播行為是為了解決業務層方法之間互相調用的事務問題,當一個事務方法被另一個事務方法調用時,事務該以何種狀態存在?例如新方法可能繼續在現有事務中運行,也可能開啟一個新事務,并在自己的事務中運行,等等,這些規則就涉及到事務的傳播性。

      關于事務的傳播性,Spring 主要定義了如下幾種:

      public enum Propagation {
       REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
       SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
       MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
       REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
       NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
       NEVER(TransactionDefinition.PROPAGATION_NEVER),
       NESTED(TransactionDefinition.PROPAGATION_NESTED);
       private final int value;
       Propagation(int value) { this.value = value; }
       public int value() { return this.value; }
      }

      具體含義如下:

      傳播性描述
      REQUIRED如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務
      SUPPORTS如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行
      MANDATORY如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常
      REQUIRES_NEW創建一個新的事務,如果當前存在事務,則把當前事務掛起
      NOT_SUPPORTED以非事務方式運行,如果當前存在事務,則把當前事務掛起
      NEVER以非事務方式運行,如果當前存在事務,則拋出異常
      NESTED如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于 TransactionDefinition.PROPAGATION_REQUIRED

      一共是七種傳播性,具體配置也簡單:

      TransactionTemplate中的配置

      transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

      PlatformTransactionManager中的配置

      public void update2() {
          //創建事務的默認配置
          DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
          definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
          definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
          TransactionStatus status = platformTransactionManager.getTransaction(definition);
          try {
              jdbcTemplate.update("update account set money = ? where username=?;", 999, "zhangsan");
              int i = 1 / ;
              //提交事務
              platformTransactionManager.commit(status);
          } catch (DataAccessException e) {
                e.printStackTrace();
              //回滾
              platformTransactionManager.rollback(status);
          }
      }

      聲明式事務的配置(XML)

      <tx:advice id="txAdvice" transaction-manager="transactionManager">
          <tx:attributes>
              <!--以 add 開始的方法,添加事務-->
              <tx:method name="add*"/>
              <tx:method name="insert*" isolation="SERIALIZABLE" propagation="REQUIRED"/>
          </tx:attributes>
      </tx:advice>

      聲明式事務的配置(Java)

      @Transactional(noRollbackFor = ArithmeticException.class,propagation = Propagation.REQUIRED)
      public void update4() {
          jdbcTemplate.update("update account set money = ? where username=?;", 998, "lisi");
          int i = 1 / ;
      }

      用就是這么來用,至于七種傳播的具體含義,松哥來和大家一個一個說。

      5.2.1 REQUIRED

      REQUIRED 表示如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。

      例如我有如下一段代碼:

      @Service
      public class AccountService {
          @Autowired
          JdbcTemplate jdbcTemplate;
          @Transactional
          public void handle1() {
              jdbcTemplate.update("update user set money = ? where id=?;", 1, 2);
          }
      }
      @Service
      public class AccountService2 {
          @Autowired
          JdbcTemplate jdbcTemplate;
          @Autowired
          AccountService accountService;
          public void handle2() {
              jdbcTemplate.update("update user set money = ? where username=?;", 1, "zhangsan");
              accountService.handle1();
          }
      }

      我在 handle2 方法中調用 handle1。

      那么:

      1. 如果 handle2 方法本身是有事務的,則 handle1 方法就會加入到 handle2 方法所在的事務中,這樣兩個方法將處于同一個事務中,一起成功或者一起失?。ú还苁?handle2 還是 handle1 誰拋異常,都會導致整體回滾)。
      2. 如果 handle2 方法本身是沒有事務的,則 handle1 方法就會自己開啟一個新的事務,自己玩。

      舉一個簡單的例子:handle2 方法有事務,handle1 方法也有事務(小伙伴們根據前面的講解自行配置事務),項目打印出來的事務日志如下:

      o.s.jdbc.support.JdbcTransactionManager  : Creating new transaction with name [org.javaboy.spring_tran02.AccountService2.handle2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@875256468 wrapping com.mysql.cj.jdbc.ConnectionImpl@9753d50] for JDBC transaction
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@875256468 wrapping com.mysql.cj.jdbc.ConnectionImpl@9753d50] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where username=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Participating in existing transaction
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where id=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@875256468 wrapping com.mysql.cj.jdbc.ConnectionImpl@9753d50]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@875256468 wrapping com.mysql.cj.jdbc.ConnectionImpl@9753d50] after transaction

      從日志中可以看到,前前后后一共就開啟了一個事務,日志中有這么一句:

      Participating in existing transaction

      這個就說明 handle1 方法沒有自己開啟事務,而是加入到 handle2 方法的事務中了。

      5.2.2 REQUIRES_NEW

      REQUIRES_NEW 表示創建一個新的事務,如果當前存在事務,則把當前事務掛起。換言之,不管外部方法是否有事務,REQUIRES_NEW 都會開啟自己的事務。

      這塊松哥要多說兩句,有的小伙伴可能覺得 REQUIRES_NEW 和 REQUIRED 太像了,似乎沒啥區別。其實你要是單純看最終回滾效果,可能確實看不到啥區別。但是,大家注意松哥上面的加粗,在 REQUIRES_NEW 中可能會同時存在兩個事務,外部方法的事務被掛起,內部方法的事務獨自運行,而在 REQUIRED 中則不會出現這種情況,如果內外部方法傳播性都是 REQUIRED,那么最終也只是一個事務。

      還是上面那個例子,假設 handle1 和 handle2 方法都有事務,handle2 方法的事務傳播性是 REQUIRED,而 handle1 方法的事務傳播性是 REQUIRES_NEW,那么最終打印出來的事務日志如下:

      o.s.jdbc.support.JdbcTransactionManager  : Creating new transaction with name [org.javaboy.spring_tran02.AccountService2.handle2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@422278016 wrapping com.mysql.cj.jdbc.ConnectionImpl@732405c2] for JDBC transaction
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@422278016 wrapping com.mysql.cj.jdbc.ConnectionImpl@732405c2] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where username=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Suspending current transaction, creating new transaction with name [org.javaboy.spring_tran02.AccountService.handle1]
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@247691344 wrapping com.mysql.cj.jdbc.ConnectionImpl@14ad4b95] for JDBC transaction
      com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@14ad4b95
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@247691344 wrapping com.mysql.cj.jdbc.ConnectionImpl@14ad4b95] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where id=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@247691344 wrapping com.mysql.cj.jdbc.ConnectionImpl@14ad4b95]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@247691344 wrapping com.mysql.cj.jdbc.ConnectionImpl@14ad4b95] after transaction
      o.s.jdbc.support.JdbcTransactionManager  : Resuming suspended transaction after completion of inner transaction
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@422278016 wrapping com.mysql.cj.jdbc.ConnectionImpl@732405c2]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@422278016 wrapping com.mysql.cj.jdbc.ConnectionImpl@732405c2] after transaction

      分析這段日志我們可以看到:

      1. 首先為 handle2 方法開啟了一個事務。
      2. 執行完 handle2 方法的 SQL 之后,事務被刮起(Suspending)。
      3. 為 handle1 方法開啟了一個新的事務。
      4. 執行 handle1 方法的 SQL。
      5. 提交 handle1 方法的事務。
      6. 恢復被掛起的事務(Resuming)。
      7. 提交 handle2 方法的事務。

      從這段日志中大家可以非常明確的看到 REQUIRES_NEW 和 REQUIRED 的區別。

      松哥再來簡單總結下(假設 handle1 方法的事務傳播性是 REQUIRES_NEW):

      1. 如果 handle2 方法沒有事務,handle1 方法自己開啟一個事務自己玩。
      2. 如果 handle2 方法有事務,handle1 方法還是會開啟一個事務。此時,如果 handle2 發生了異常進行回滾,并不會導致 handle1 方法回滾,因為 handle1 方法是獨立的事務;如果 handle1 方法發生了異常導致回滾,并且 handle1 方法的異常沒有被捕獲處理傳到了 handle2 方法中,那么也會導致 handle2 方法回滾。
      ?

      這個地方小伙伴們要稍微注意一下,我們測試的時候,由于是兩個更新 SQL,如果更新的查詢字段不是索引字段,那么 InnoDB 將使用表鎖,這樣就會發生死鎖(handle2 方法執行時開啟表鎖,導致 handle1 方法陷入等待中,而必須 handle1 方法執行完,handle2 才能釋放鎖)。所以,在上面的測試中,我們要將 username 字段設置為索引字段,這樣默認就使用行鎖了。

      5.2.3 NESTED

      NESTED 表示如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于 TransactionDefinition.PROPAGATION_REQUIRED。

      假設 handle2 方法有事務,handle1 方法也有事務且傳播性為 NESTED,那么最終執行的事務日志如下:

      o.s.jdbc.support.JdbcTransactionManager  : Creating new transaction with name [org.javaboy.demo.AccountService2.handle2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@2025689131 wrapping com.mysql.cj.jdbc.ConnectionImpl@2ed3628e] for JDBC transaction
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@2025689131 wrapping com.mysql.cj.jdbc.ConnectionImpl@2ed3628e] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where username=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Creating nested transaction with name [org.javaboy.demo.AccountService.handle1]
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where id=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing transaction savepoint
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@2025689131 wrapping com.mysql.cj.jdbc.ConnectionImpl@2ed3628e]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@2025689131 wrapping com.mysql.cj.jdbc.ConnectionImpl@2ed3628e] after transaction

      關鍵一句在 Creating nested transaction。

      此時,NESTED 修飾的內部方法(handle1)屬于外部事務的子事務,外部主事務回滾的話,子事務也會回滾,而內部子事務可以單獨回滾而不影響外部主事務和其他子事務(需要處理掉內部子事務的異常)。

      5.2.4 MANDATORY

      MANDATORY 表示如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。

      這個好理解,我舉兩個例子:

      假設 handle2 方法有事務,handle1 方法也有事務且傳播性為 MANDATORY,那么最終執行的事務日志如下:

      o.s.jdbc.support.JdbcTransactionManager  : Creating new transaction with name [org.javaboy.demo.AccountService2.handle2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@768820610 wrapping com.mysql.cj.jdbc.ConnectionImpl@14840df2] for JDBC transaction
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@768820610 wrapping com.mysql.cj.jdbc.ConnectionImpl@14840df2] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where username=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Participating in existing transaction
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where id=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@768820610 wrapping com.mysql.cj.jdbc.ConnectionImpl@14840df2]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@768820610 wrapping com.mysql.cj.jdbc.ConnectionImpl@14840df2] after transaction

      從這段日志可以看出:

      1. 首先給 handle2 方法開啟事務。
      2. 執行 handle2 方法的 SQL。
      3. handle1 方法加入到已經存在的事務中。
      4. 執行 handle1 方法的 SQL。
      5. 提交事務。

      假設 handle2 方法無事務,handle1 方法有事務且傳播性為 MANDATORY,那么最終執行時會拋出如下異常:

      No existing transaction found for transaction marked with propagation 'mandatory'

      由于沒有已經存在的事務,所以出錯了。

      5.2.5 SUPPORTS

      SUPPORTS 表示如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。

      這個也簡單,舉兩個例子大家就明白了。

      假設 handle2 方法有事務,handle1 方法也有事務且傳播性為 SUPPORTS,那么最終事務執行日志如下:

      o.s.jdbc.support.JdbcTransactionManager  : Creating new transaction with name [org.javaboy.demo.AccountService2.handle2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@1780573324 wrapping com.mysql.cj.jdbc.ConnectionImpl@44eafcbc] for JDBC transaction
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@1780573324 wrapping com.mysql.cj.jdbc.ConnectionImpl@44eafcbc] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where username=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Participating in existing transaction
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where id=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@1780573324 wrapping com.mysql.cj.jdbc.ConnectionImpl@44eafcbc]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@1780573324 wrapping com.mysql.cj.jdbc.ConnectionImpl@44eafcbc] after transaction

      這段日志很簡單,沒啥好說的,認準 Participating in existing transaction表示加入到已經存在的事務中即可。

      假設 handle2 方法無事務,handle1 方法有事務且傳播性為 SUPPORTS,這個最終就不會開啟事務了,也沒有相關日志。

      5.2.6 NOT_SUPPORTED

      NOT_SUPPORTED 表示以非事務方式運行,如果當前存在事務,則把當前事務掛起。

      假設 handle2 方法有事務,handle1 方法也有事務且傳播性為 NOT_SUPPORTED,那么最終事務執行日志如下:

      o.s.jdbc.support.JdbcTransactionManager  : Creating new transaction with name [org.javaboy.demo.AccountService2.handle2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
      o.s.jdbc.support.JdbcTransactionManager  : Acquired Connection [HikariProxyConnection@1365886554 wrapping com.mysql.cj.jdbc.ConnectionImpl@3198938b] for JDBC transaction
      o.s.jdbc.support.JdbcTransactionManager  : Switching JDBC Connection [HikariProxyConnection@1365886554 wrapping com.mysql.cj.jdbc.ConnectionImpl@3198938b] to manual commit
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where username=?;]
      o.s.jdbc.support.JdbcTransactionManager  : Suspending current transaction
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
      o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update user set money = ? where id=?;]
      o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
      o.s.jdbc.support.JdbcTransactionManager  : Resuming suspended transaction after completion of inner transaction
      o.s.jdbc.support.JdbcTransactionManager  : Initiating transaction commit
      o.s.jdbc.support.JdbcTransactionManager  : Committing JDBC transaction on Connection [HikariProxyConnection@1365886554 wrapping com.mysql.cj.jdbc.ConnectionImpl@3198938b]
      o.s.jdbc.support.JdbcTransactionManager  : Releasing JDBC Connection [HikariProxyConnection@1365886554 wrapping com.mysql.cj.jdbc.ConnectionImpl@3198938b] after transaction

      這段日志大家認準這兩句就行了 :Suspending current transaction表示掛起當前事務;Resuming suspended transaction表示恢復掛起的事務。

      5.2.7 NEVER

      NEVER 表示以非事務方式運行,如果當前存在事務,則拋出異常。

      假設 handle2 方法有事務,handle1 方法也有事務且傳播性為 NEVER,那么最終會拋出如下異常:

      Existing transaction found for transaction marked with propagation 'never'

      5.3 回滾規則

      默認情況下,事務只有遇到運行期異常(RuntimeException 的子類)以及 Error 時才會回滾,在遇到檢查型(Checked Exception)異常時不會回滾。

      像 1/0,空指針這些是 RuntimeException,而 IOException 則算是 Checked Exception,換言之,默認情況下,如果發生 IOException 并不會導致事務回滾。

      如果我們希望發生 IOException 時也能觸發事務回滾,那么可以按照如下方式配置:

      Java 配置:

      @Transactional(rollbackFor = IOException.class)
      public void handle2() {
          jdbcTemplate.update("update user set money = ? where username=?;", 1, "zhangsan");
          accountService.handle1();
      }

      XML 配置:

      <tx:advice transaction-manager="transactionManager" id="txAdvice">
          <tx:attributes>
              <tx:method name="m3" rollback-for="java.io.IOException"/>
          </tx:attributes>
      </tx:advice>

      另外,我們也可以指定在發生某些異常時不回滾,例如當系統拋出 ArithmeticException 異常并不要觸發事務回滾,配置方式如下:

      Java 配置:

      @Transactional(noRollbackFor = ArithmeticException.class)
      public void handle2() {
          jdbcTemplate.update("update user set money = ? where username=?;", 1, "zhangsan");
          accountService.handle1();
      }

      XML 配置:

      <tx:advice transaction-manager="transactionManager" id="txAdvice">
          <tx:attributes>
              <tx:method name="m3" no-rollback-for="java.lang.ArithmeticException"/>
          </tx:attributes>
      </tx:advice>

      5.4 是否只讀

      只讀事務一般設置在查詢方法上,但不是所有的查詢方法都需要只讀事務,要看具體情況。

      一般來說,如果這個業務方法只有一個查詢 SQL,那么就沒必要添加事務,強行添加最終效果適得其反。

      但是如果一個業務方法中有多個查詢 SQL,情況就不一樣了:多個查詢 SQL,默認情況下,每個查詢 SQL 都會開啟一個獨立的事務,這樣,如果有并發操作修改了數據,那么多個查詢 SQL 就會查到不一樣的數據。此時,如果我們開啟事務,并設置為只讀事務,那么多個查詢 SQL 將被置于同一個事務中,多條相同的 SQL 在該事務中執行將會獲取到相同的查詢結果。

      設置事務只讀的方式如下:

      Java 配置:

      @Transactional(readOnly = true)

      XML 配置:

      <tx:advice transaction-manager="transactionManager" id="txAdvice">
          <tx:attributes>
              <tx:method name="m3" read-only="true"/>
          </tx:attributes>
      </tx:advice>

      5.5 超時時間

      超時時間是說一個事務允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。

      事務超時時間配置方式如下(單位為秒):

      Java 配置:

      @Transactional(timeout = 10)

      XML 配置:

      <tx:advice transaction-manager="transactionManager" id="txAdvice">
          <tx:attributes>
              <tx:method name="m3" read-only="true" timeout="10"/>
          </tx:attributes>
      </tx:advice>

      TransactionDefinition中以 int 的值來表示超時時間,其單位是秒,默認值為-1。

      6. 注意事項

      1. 事務只能應用到 public 方法上才會有效。
      2. 事務需要從外部調用,Spring 自調事務用會失效。即相同類里邊,A 方法沒有事務,B 方法有事務,A 方法調用 B 方法,則 B 方法的事務會失效,這點尤其要注意,因為代理模式只攔截通過代理傳入的外部方法調用,所以自調用事務是不生效的。
      3. 建議事務注解 @Transactional 一般添加在實現類上,而不要定義在接口上,如果加在接口類或接口方法上時,只有配置基于接口的代理這個注解才會生效。

      7. 小結

      好啦,這就是松哥和大家分享的 Spring 事務的玩法,不知道小伙伴們搞明白沒有?配套視頻已經錄好,正在上傳,敬請期待~


      標簽:事務 方法 存在
      當老板讓我從Java8升級到Java11時
      ? 上一篇 2022-09-16
      海量小文件總結及解決策略(三)NameNode、Hadoop歸檔文件等
      下一篇 ? 2022-09-17
      • 如何在Ubuntu中保留文件系統并備份當前開發板鏡像
        0閱讀 0條評論 個贊
        在Ubuntu保留文件系統或者說備份當前開發板鏡像的需求在不斷增加。比如Ubuntu文件系統需要安裝庫文件的話直接使用apt-get工具就可以下載,但由于需要下載的核心板較多,比較費時間,這時需要將安……
      • 國產核心板全志T507助力消防系統升級
        0閱讀 0條評論 個贊
        9月16日下午,位于湖南長沙市區內的中國電信大樓發生火災,建筑高度218米,現場濃煙滾滾,數十層樓體燃燒劇烈。消防救援人員趕到現場后很快將火勢控制住,目前大樓火勢已被撲滅,所幸未發現人員傷亡。湖南電信……
      • 教大家如何處理Spring Boot易流中的用戶和群體!
        0閱讀 0條評論 個贊
        1.準備工作2.用戶操作2.1添加用戶2.2修改用戶2.3刪除用戶2.4查詢用戶3.組操作3.1添加組3.2修改組3.3刪除組3.4查詢組4.查看表詳情雖然說我們在實際開發中,……
      • 從PG15開始WAL壓縮優化
        0閱讀 0條評論 個贊
        PG15傳聞中的超級令人激動的功能大多數跳票了,年初我也寫過一個關于PG15新功能跳票的文章。PG15BETA已經發出幾個月了,似乎PG15里令人激動人心的功能不多,不過從長長的新功能列表里,……
      • 深入了解美團葉子發射器開源方案
        0閱讀 0條評論 個贊
        大家好,我是樹哥。之前我們有聊過「如何設計一個分布式ID發號器」,其中有講過4種解決方案,分別是:UUID類雪花算法數據庫自增主鍵Redis原子自增美團以第2、3種解決方案為基礎,開發出……
      發表評論 共有條評論
      用戶名: 密碼:
      驗證碼: 匿名發表
      • SQL Server合并(刪除)分區的歧義消除
        4閱讀 0條評論 個贊
        一、準備在SQLServer2005版本之后就有了表分區的概念與應用,在分區操作里面有一個叫做合并分區的功能,也被稱為刪除分區。分區所處的文件組和文件是不會被刪除的,只會對數據進行轉移合并。合并分……
      • Hadoop(簡介)大數據技術概述 運行環境構建 運行模式
        0閱讀 0條評論 個贊
        文章目錄1Hadoop概述1.1Hadoop是什么1.2Hadoop優勢1.3Hadoop組成(面試重點)1.3.1HDFS架構概述1.3.2YARN架構概述1.3.3Map……
      • 深入了解美團葉子發射器開源方案
        0閱讀 0條評論 個贊
        大家好,我是樹哥。之前我們有聊過「如何設計一個分布式ID發號器」,其中有講過4種解決方案,分別是:UUID類雪花算法數據庫自增主鍵Redis原子自增美團以第2、3種解決方案為基礎,開發出……
      • smile——Java機器學習引擎
        2閱讀 0條評論 個贊
        資源https://haifengl.github.io/https://github.com/haifengl/smile介紹Smile(統計機器智能和學習引擎)是一個基于Java和Scala的快速……
      • Java接口自動測試框架系列(1)自動測試框架
        0閱讀 0條評論 個贊
        一、什么是自動化測試自動化測試是把以人為驅動的測試行為轉化為機器執行的一種過程。通常,在設計了測試用例并通過評審之后,由測試人員根據測試用例一步步執行測試,得到實際結果與期望結果的比較。為了節省人力、……
      • SQL SERVER存儲過程學習筆記
        6閱讀 0條評論 個贊
        將常用的或很復雜的工作,預先用SQL語句寫好并用一個指定的名稱存儲起來,那么以后要叫數據庫提供與已定義好的存儲過程的功能相同的服務時,只需調用execute,即可自動完成命令。存儲過程的優點1.存儲……
      • 用戶自定義注釋 AOP實現的日志保存(數據庫) 所有代碼都可以粘貼復制
        0閱讀 0條評論 個贊
        前言1,在一些特定的場景我們往往需要看一下接口的入參,特別是跨系統的接口調用(下發,推送),這個時候的接口入參就很重要,我們保存入參入庫,如果出問題就可以馬上定位是上游還是下游的問題(方便扯皮)2,還……
      • Java可以重新鎖定的那些東西(1)
        0閱讀 0條評論 個贊
        本文主要包含的內容:可重入鎖(ReedtrantLock)、公平鎖、非公平鎖、可重入性、同步隊列、CAS等概念的理解顯式鎖……
      • 圖解架構| SaaS、PaaS、IaaS
        0閱讀 0條評論 個贊
        這是悟空的第164篇原創文章官網:www.passjava.cn你好,我是悟空哥。上次聊到了架構圖如何畫,其中涉及到了云服務的架構圖,里面提到了很重要的三個概念PaaS、IaaS、SaaS,很有必要……
      • sql server索引I摘要
        0閱讀 0條評論 個贊
        一、存儲結構在SQLServer中,有許多不同的可用排列規則選項。二進制:按字符的數字表示形式排序(ASCII碼中,用數字32表示空格,用68表示字母"D")。因為所有內容都表示為數字,所以……
      • 金牛座入門 MVC微服務框架開發教程
        0閱讀 0條評論 個贊
        前言:對于Taurus.MVC的微服務的注冊中心而言:什么樣的應用中心,有權利注冊服務?什么樣的網關中心,有權利調取服務列表?在默認沒有進行相關配置時,只要引用Taurus.MVC的框架,都擁有該權限……
      • 談ASP.NET核心認證與授權
        0閱讀 0條評論 個贊
        使用asp.netcore開發應用系統過程中,基本上都會涉及到用戶身份的認證,及授權訪問控制,因此了解認證和授權流程也相當重要,下面通過分析asp.netcore框架中的認證和授權的源碼來分析……
      • 教大家如何處理Spring Boot易流中的用戶和群體!
        0閱讀 0條評論 個贊
        1.準備工作2.用戶操作2.1添加用戶2.2修改用戶2.3刪除用戶2.4查詢用戶3.組操作3.1添加組3.2修改組3.3刪除組3.4查詢組4.查看表詳情雖然說我們在實際開發中,……
      • MQ系列5:5的發送模式:RocketMQ消息
        0閱讀 0條評論 個贊
        在之前的篇章中,我們學習了RocketMQ的原理,以及RocketMQ中命名服務ServiceName的運行流程,本篇從消息的生產、消費來理解一條消息的生命周期。1消息生產在RocketMQ中……
      • RDD星火基金簡介(01)
        0閱讀 0條評論 個贊
        1,基本概念RDD(ResilientDistributedDataset):彈性分布式數據集它是Spark中最基本的數據抽象,是編寫Spark程序的基礎。簡單的來講,一個Spark程序可以概括……
      • 公司排班系統遇到瓶頸?我自己造一個!
        10閱讀 0條評論 個贊
        本期名人堂我們有幸邀請到了白鯨開源的聯合創始人代立冬先生。代立冬先生與我們分享了自己社區開源的經歷,詳細介紹了一個開源項目是如何加入Apache孵化器,如何在導師的輔導下慢慢孵化、成長的過程,以及自己……
      • 拓撲嶺CEO雷鵬:創業不只是技術
        0閱讀 0條評論 個贊
        云計算的出現,加速了企業信息技術的發展。數據庫是最常用的基礎軟件之一,它通常能提供計算和存儲的能力。雖然分布式數據庫也做到了很好的擴展性,但它不能算是「云原生」?!冈圃鷶祿臁沟暮诵?,是要設計一種更……
      • spring項目中啟動包的原理 以及自定義啟動包的使用
        0閱讀 0條評論 個贊
        MAVEN項目中starter的原理一.原始方式我們最早配置spring應用的時候,必須要經歷的步驟:1.pom文件中引入相關的jar包,包括spring,redis,jdbc等等2.通過prope……
      • 開發者如何在應用后臺直接控制用戶的運動狀態?
        18閱讀 0條評論 個贊
        酷暑終于過去,很多人伴著涼爽的秋風開啟了新一輪的健身計劃。當用戶進行戶外運動或使用跑步機、橢圓機等器械時,他們會希望在運動健康類App里點擊即可開啟運動并記錄運動數據。而對于開發者自己開發的應用來說,……
      • 基于位置變化的市縣彈出引導切換
        31閱讀 0條評論 個贊
        目錄1.背景2.模型方案產出3.總結01背景58App業務中有城市和縣域兩個首頁,兩者中間有一個過渡選擇頁,用戶通過點擊該頁面下的條目內容可以切換到對應條目的首頁;比如:點擊“北京”會跳轉到北京(市)……
      最近發布資訊
      更多
      警花高潮嗷嗷叫
      <del id="nnjnj"></del><track id="nnjnj"></track>

      <p id="nnjnj"></p>

      <address id="nnjnj"></address>

        <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

          <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>