SpringBoot2 基礎案例(12):基於轉賬案例,演示事務管理操作

知了一笑發表於2020-01-06

本文原始碼: GitHub·點這裡 || GitEE·點這裡

一、事務管理簡介

1、事務基本概念
一組業務操作ABCD,要麼全部成功,要麼全部不成功。

2、特性:ACID
原子性:整體
一致性:完成
隔離性:併發
永續性:結果

3、隔離問題
髒讀:一個事務讀到另一個事務沒有提交的資料
不可重複讀:一個事務讀到另一個事務已提交的資料(update)
虛讀(幻讀):一個事務讀到另一個事務已提交的資料(insert)

4、隔離級別
read uncommitted:讀未提交。
read committed:讀已提交。解決髒讀。
repeatable read:可重複讀。解決:髒讀、不可重複讀。
serializable :序列化。都解決,單事務。

二、Spring管理事務

1、頂級介面

1)PlatformTransactionManager
平臺事務管理器,spring要管理事務,必須使用事務管理器進行事務配置時,必須配置事務管理器。
2)TransactionDefinition
事務詳情(事務定義、事務屬性),spring用於確定事務具體詳情,
例如:隔離級別、是否只讀、超時時間 等
進行事務配置時,必須配置詳情。spring將配置項封裝到該物件例項。
3)TransactionStatus
事務狀態,spring用於記錄當前事務執行狀態。例如:是否有儲存點,事務是否完成。
spring底層根據狀態進行相應操作。

2、事務狀態

3、事務定義

PROPAGATION_REQUIRED , required , 必須  【預設值】
    支援當前事務,A如果有事務,B將使用該事務。
    如果A沒有事務,B將建立一個新的事務。
PROPAGATION_SUPPORTS ,supports ,支援
    支援當前事務,A如果有事務,B將使用該事務。
    如果A沒有事務,B將以非事務執行。
PROPAGATION_MANDATORY,mandatory ,強制
    支援當前事務,A如果有事務,B將使用該事務。
    如果A沒有事務,B將拋異常。
PROPAGATION_REQUIRES_NEW , requires_new ,必須新的
    如果A有事務,將A的事務掛起,B建立一個新的事務
    如果A沒有事務,B建立一個新的事務
PROPAGATION_NOT_SUPPORTED ,not_supported ,不支援
    如果A有事務,將A的事務掛起,B將以非事務執行
    如果A沒有事務,B將以非事務執行
PROPAGATION_NEVER ,never,從不
    如果A有事務,B將拋異常
    如果A沒有事務,B將以非事務執行
PROPAGATION_NESTED ,nested ,巢狀
    A和B底層採用儲存點機制,形成巢狀事務。
掌握:PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED

三、SpringBoot2 管理事務

基於轉賬的案例演示,基於druid連線池配置。druid連線池在文章。

SpringBoot2.0 基礎案例(07):整合Druid連線池,配置監控介面

1、新建轉賬表

CREATE TABLE account(
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50),
  money INT
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO account(username,money) VALUES('jack','10000');
INSERT INTO account(username,money) VALUES('rose','10000');
SELECT * FROM account;

2、基於事務手動管理器

該配置用於測試事務的手動管理。

/**
 * 事物管理器
 */
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager (DruidDataSource dataSource){
    LOGGER.info("【transactionManager 初始化...】");
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(dataSource);
    return transactionManager;
}
/**
 * 建立事物手動管理模板
 */
@Bean(name = "transactionTemplate")
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager)
    LOGGER.info("【transactionTemplate 初始化...】");
    TransactionTemplate transactionTemplate = new TransactionTemplate() ;
    transactionTemplate.setTransactionManager(transactionManager);
    return transactionTemplate;
}

3、封裝轉賬介面

介面方法

public interface AccountService {
    /**
     * 匯款
     */
    void out (String outer , Integer money);
    /**
     * 收款
     */
    void in (String inner , Integer money);
}

介面實現

@Service
public class AccountServiceImpl implements AccountService {
    @Resource
    private JdbcTemplate jdbcTemplate ;
    public void out(String outer, Integer money) {
        String sql = "update account set money = money - ? where username = ?";
        jdbcTemplate.update(sql, money,outer);
    }
    public void in(String inner, Integer money) {
        String sql = "update account set money = money + ? where username = ?";
        jdbcTemplate.update(sql, money,inner);
    }
}

4、封裝三個測試介面

測試介面

public interface TradeService {
    /**
     * 轉賬交易:沒有事務管理
     */
    void trade1(String outer ,String inner ,Integer money);
    /**
     * 轉賬交易:手動管理事務
     */
    void trade2(String outer ,String inner ,Integer money);
    /**
     * 轉賬交易:註解管理事務
     */
    void trade3(String outer ,String inner ,Integer money);
}

介面實現

@Service
public class TradeServiceImpl implements TradeService {
    @Resource
    private AccountService accountService ;
    @Resource
    private TransactionTemplate transactionTemplate ;
    @Override
    public void trade1(String outer, String inner, Integer money) {
        accountService.out(outer, money);
        // 丟擲異常
        int i = 1/0;
        accountService.in(inner, money);
    }
    @Override
    public void trade2(String outer, String inner, Integer money) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus arg0) {
                accountService.out(outer, money);
                // 丟擲異常
                int i = 1/0;
                accountService.in(inner, money);
            }
        });
    }
    @Transactional(value="transactionManager",propagation= Propagation.REQUIRED)
    @Override
    public void trade3(String outer, String inner, Integer money) {
        accountService.out(outer, money);
        // 丟擲異常
        int i = 1/0;
        accountService.in(inner, money);
    }
}

5、編寫測試類

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TransactionApplication.class)
public class TradeTest {
    @Resource
    private TradeService tradeService ;
    /**
     * 沒有事務管理
     * jack 減少了1000塊錢,但是rose得到1000塊錢
     * 1    jack    9000
     * 2    rose    10000
     */
    @Test
    public void testTrade1 (){
        tradeService.trade1("jack", "rose", 1000);
    }
    /**
     * 手動管理事務
     * 1    jack    10000
     * 2    rose    10000
     */
    @Test
    public void testTrade2 (){
        tradeService.trade2("jack", "rose", 1000);
    }
    /**
     * 註解管理事務
     * 1    jack    10000
     * 2    rose    10000
     */
    @Test
    public void testTrade3 (){
        tradeService.trade3("jack", "rose", 1000);
    }
}

四、原始碼地址

GitHub·地址
https://github.com/cicadasmile/spring-boot-base
GitEE·地址
https://gitee.com/cicadasmile/spring-boot-base

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69957347/viewspace-2672209/,如需轉載,請註明出處,否則將追究法律責任。

相關文章