package com.chinaweal.aiccs.config;

/**
 * Created by IDEA.
 * Code: Yangmc
 * Date: 2021/10/28
 * Time: 19:37
 */

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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.*;

import javax.annotation.Resource;
import java.util.Collections;

/**
 * 全局事物配置
 *
 * REQUIRED ：如果当前存在事务，则加入该事务；如果当前没有事务，则创建一个新的事务。
 * SUPPORTS ：如果当前存在事务，则加入该事务；如果当前没有事务，则以非事务的方式继续运行。
 * MANDATORY ：如果当前存在事务，则加入该事务；如果当前没有事务，则抛出异常。
 * REQUIRES_NEW ：创建一个新的事务，如果当前存在事务，则把当前事务挂起。
 * NOT_SUPPORTED ：以非事务方式运行，如果当前存在事务，则把当前事务挂起。
 * NEVER ：以非事务方式运行，如果当前存在事务，则抛出异常。
 * NESTED ：如果当前存在事务，则创建一个事务作为当前事务的嵌套事务来运行；如果当前没有事务，则该取值等价于 REQUIRED 。
 * 指定方法：通过使用 propagation 属性设置，例如：@Transactional(propagation = Propagation.REQUIRED)
 * 参考文章；https://www.cnblogs.com/pojo/p/15049434.html
 */
@Aspect
@Configuration
public class TransactionAdviceConfig {

    /**
     * 全局事务位置配置 在哪些地方需要进行事务处理：具体如下
     * 配置切入点表达式,这里解释一下表达式的含义
     * 1.execution(): 表达式主体
     * 2.第一个*号:表示返回类型，*号表示所有的类型
     * 3.com.chinaweal.aiccs.service表示切入点的包名
     * 4.第二个*号:表示实现包
     * 5.*(..)*号表示所有方法名,..表示所有类型的参数
     */
    // 切入点
    private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.chinaweal..*service.*.*(..))";

    // 需要添加事务切入的方法列表, 使用这些前缀的方法都会被添加事务
    private static final String[] REQUIRED_RULE_TRANSACTION = {"insert*", "create*", "add*", "save*", "update*", "del*", "delete*", "remove*", "*"};
    private static final String[] READONLY_RULE_TRANSACTION = {"select*", "get*", "query*", "search*", "count*", "find*", "list*"};

    @Resource
    private TransactionManager transactionManager;

    @Bean
    public TransactionInterceptor txAdvice() {
        // 增删改事务
        RuleBasedTransactionAttribute REQUIRED = new RuleBasedTransactionAttribute();
        // 当抛出设置的对应异常后，进行事务回滚（此处设置为“Exception”级别）
        REQUIRED.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 设置隔离级别（读已提交的数据）
        REQUIRED.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        // 设置传播行为（存在事务则加入其中，不存在则新建事务）
        REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        // 只读事务，不做更新操作
        RuleBasedTransactionAttribute READONLY = new RuleBasedTransactionAttribute();
        // 当抛出设置的对应异常后，进行事务回滚（此处设置为“Exception”级别）
//        READONLY.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        // 设置隔离级别（读已提交的数据）
//        READONLY.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        // 设置传播行为（如果当前存在事务，则加入这个事务，如果当前没有事务，就以非事务方式执行）
        READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
        // 设置事务是否“只读”（非必需，只是声明该事务中不会进行修改数据库的操作，可减轻由事务造成的数据库压力，属于性能优化的推荐配置）
        READONLY.setReadOnly(true);

        // 事务管理规则，承载需要进行事务管理的方法名（模糊匹配）及设置的事务管理属性
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        // 增删改方法的前缀
        for (String s : REQUIRED_RULE_TRANSACTION) {
            source.addTransactionalMethod(s, REQUIRED);
        }

        // 查询方法的前缀
        for (String s : READONLY_RULE_TRANSACTION) {
            source.addTransactionalMethod(s, READONLY);
        }

        // 实例化事务拦截器
        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        // 声明切点要切入的面
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 设置需要被拦截的路径
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        // 设置切面和配置好的事务管理
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}