天天看點

redis、springboot事務

redis事務

參考:Redis之Redis事務

SpringBoot開啟Redis事務錯誤: Cannot use Jedis when in Multi. Please use Transaction or reset jedis state.

watch key1 key2 ... : 監視一或多個key,如果在事務執行之前,
				被監視的key被其他指令改動,則事務被打斷 ( 類似樂觀鎖 )
multi : 	标記一個事務塊的開始( queued )
exec : 		執行所有事務塊的指令 ( 一旦執行exec後,之前加的監控鎖都會被取消掉 ) 
discard : 	取消事務,放棄事務塊中的所有指令
unwatch :	取消watch對所有key的監控
           

watch指令類似于樂觀鎖,在事務送出時,如果watch監控的多個KEY中任何KEY的值已經被其他用戶端更改,則使用EXEC執行事務時,事務隊列将不會被執行,同時傳回Nullmulti-bulk應答以通知調用者事務執行失敗。

實戰:

@Autowired
    private StringRedisTemplate stringRedisTemplate;

public Object execTransactionCorrect() {
        // watch該key,如果送出事物時有外界調整則設定失敗
        String key = "redis";

        // 開始設定key為100
        stringRedisTemplate.opsForValue().set(key, "100");
        return stringRedisTemplate.execute(new SessionCallback<String>() {
            @Override
            public <K, V> String execute(RedisOperations<K, V> operations) throws DataAccessException {
                try {
                    operations.watch((K) key);
                    // 開啟事務
                    operations.multi();
                    // 修改資料
                    operations.opsForValue().set((K)key, (V)"2");
                    // operations.opsForValue().increment((K) key);
                    // 模拟異常
                    Integer.parseInt("a");
                    // 送出事物 之前的redis操作,
                    operations.exec();
                } catch (Exception e) {
                    e.printStackTrace();
                    // 出現異常則需要取消redis事務(不用復原,因為multi之後,相當于資料)
                    operations.discard();
                }
                return (String) operations.opsForValue().get(key);
            }
        });
    }
           

springboot手動開啟事務

//1,注入bean

    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

//2,結合try-catch使用
TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
try {
    dosomething....
    platformTransactionManager.commit(transactionStatus);
} catch (Exception e) {
  platformTransactionManager.rollback(transactionStatus);
}

           
@Autowired
    DataSourceTransactionManager txm;

    @Override
    public void test() {
        TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        TransactionStatus status = txm.getTransaction(transactionDefinition);

        this.insert(new PeopleHouseInfoEntity().setRoom("123131"));
        int a = 10 / 0;
        // txm.rollback(status);
        txm.commit(status);
    }
           

全局切面配置,比注解中優先級高。

package com.yymt.common.config;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


@Aspect
//切面
//表示該類相當于Spring的xml配置檔案中的<Beans>
@Configuration
public class TransactionAdviceConfig {

    /**
     * 定義切點路徑
     */
    private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.yymt.service..*.impl.*.*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    /**
     * @description 事務管理配置
     */
    @Bean
    public TransactionInterceptor TxAdvice() {
        // 事務管理規則,承載需要進行事務管理的方法名(模糊比對)及設定的事務管理屬性
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        // 設定第一個事務管理的模式(适用于“增删改”)
        RuleBasedTransactionAttribute transactionAttribute1 = new RuleBasedTransactionAttribute();
        // 當抛出設定的對應異常後,進行事務復原(此處設定為“Exception”級别)
        transactionAttribute1.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 設定隔離級别(存在事務則加入其中,不存在則建立事務)
        transactionAttribute1.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 設定傳播行為(讀已送出的資料)
        transactionAttribute1.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

        // 設定第二個事務管理的模式(适用于“查”)
        RuleBasedTransactionAttribute transactionAttribute2 = new RuleBasedTransactionAttribute();
        // 當抛出設定的對應異常後,進行事務復原(此處設定為“Exception”級别)
        transactionAttribute2.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 設定隔離級别(存在事務則挂起該事務,執行目前邏輯,結束後再恢複上下文事務) 。xl:挂起後會導緻本次插入的資料查詢不到()
        transactionAttribute2.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); 
        // transactionAttribute2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 設定傳播行為(讀已送出的資料)
        transactionAttribute2.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        // 設定事務是否“隻讀”(非必需,隻是聲明該事務中不會進行修改資料庫的操作,可減輕由事務造成的資料庫壓力,屬于性能優化的推薦配置)
        transactionAttribute2.setReadOnly(true);

        // 建立一個map,用來儲存要需要進行事務管理的方法名(模糊比對)
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("insert*", transactionAttribute1);
        txMap.put("add*", transactionAttribute1);
        txMap.put("send*", transactionAttribute1);
        txMap.put("save*", transactionAttribute1);
        txMap.put("update*", transactionAttribute1);
        txMap.put("modify*", transactionAttribute1);
        txMap.put("change*", transactionAttribute1);
        txMap.put("delete*", transactionAttribute1);
        txMap.put("remove*", transactionAttribute1);
        txMap.put("check*", transactionAttribute1);
        txMap.put("upload*", transactionAttribute1);
        txMap.put("export*", transactionAttribute1);
        txMap.put("create*", transactionAttribute1);
        txMap.put("download*", transactionAttribute1);
        txMap.put("query*", transactionAttribute2);
        txMap.put("get*", transactionAttribute2);
        txMap.put("select*", transactionAttribute2);
        txMap.put("find*", transactionAttribute2);
        // 注入設定好的map
        source.setNameMap(txMap);
        // 執行個體化事務攔截器
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
        return txAdvice;
    }

    /**
     * @description 利用AspectJExpressionPointcut設定切面
     */
    @Bean
    public Advisor txAdviceAdvisor() {
        // 聲明切點要切入的面
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 設定需要被攔截的路徑
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        // 設定切面和配置好的事務管理
        return new DefaultPointcutAdvisor(pointcut, TxAdvice());
    }
}