企业开发中声明式事务和切面事务和编程式事务区别

304 阅读2分钟

前言:

声明式事务缺点:细粒度中等,开发接口时要考虑事务场景,12种事务场景用不好还很多坑,费时费力

编程式事务:细粒度比较细,相对比声明式事务,编程式事务不需要考虑所谓的12种场景,能避开一些不必要的坑

切面试事务:细粒度比较粗,比较符合约定大于配置的理念,开发过程中只考虑写业务逻辑不必要关心事务,提高开发效率

推荐:如果公司团队比较规范切面事务会好点,如果是小的公司一些杂的项目大家得过且过没有约定的话,建议使用编程式事务

SpringBoot声明式事务

声明式事务@Transactional可以使用在类上,也可以使用在public方法上. 如果是使用在类上,则是对所有的public方法都开启事务,如果类和方法上都有则方法上的事务生效

可以在类上使用

@Transactional(rollbackFor=Exception.class)
public class TransactionServiceImpl implements TransactionService {
}

工作中声明式事务用的比较多的还是在方法上使用,因为声明事务的使用场景有12种

@Override
@Transactional(rollbackFor=Exception.class)
public void t1(Student one) {
}

缺点:开发接口时要考虑事务场景,12种事务场景用不好还很多坑,费时费力

SpringBoot编程式事务

编程式事务

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;

/**
 * @program: wym-parent
 * @description: 编程式事务
 * @author: 尉一民
 * @create: 2023-02-28 09:28

@Component
public class TransactionTemplates extends DefaultTransactionDefinition implements InitializingBean {
    
    public static final Logger logger = LoggerFactory.getLogger(TransactionTemplates.class);
     /**
     * 事务管理器
     */
    @Autowired
    private PlatformTransactionManager transactionManager;

    @Override
    public void afterPropertiesSet() {
        // 校验管理器是否被spring注入
        if (this.transactionManager == null) {
            throw new IllegalArgumentException("Property 'transactionManager' is required");
        }
    }

    /**
     * 事务执行器
     *
     * @param action
     * @param <T>
     * @return
     */
    @Transactional
    public <T> T execute(TransactionCallback<T> action) {
        TransactionStatus status = this.transactionManager.getTransaction(this);
        T result = null;
        try {
            result = action.doInTransaction(status);
        } catch (Exception e) {
            // 事务回滚
            this.transactionManager.rollback(status);
            logger.error("事务执行异常", e);
            return result;
        }
        // 事务提交
        this.transactionManager.commit(status);
        return result;
    }

}

使用方式

在需要的地方注入TransactionTemplates

@Resource
private TransactionTemplates transactionTemplates;

然后在代码中使用

Boolean execute = transactionTemplates.execute(status -> {
    //写自己的数据库crud业务
    return Boolean.TRUE;
});

SpringBoot切面式事务

此种方式基于AOP功能,所以需要添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

写配置类

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.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;

/**
 * 事务配置
 *
 * @author lizhixiao
 */
@Aspect
@Configuration
public class TransactionAdviceConfig {
    //切面路径
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.wym..service..*.*(..)) or execution (* com.wym..module..*.*(..))";

    @Autowired
    private TransactionManager transactionManager;

    @Bean
    public TransactionInterceptor txAdvice() {

        DefaultTransactionAttribute txAttrRequired = new DefaultTransactionAttribute();
        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        DefaultTransactionAttribute txAttrRequiredReadonly = new DefaultTransactionAttribute();
        txAttrRequiredReadonly.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        txAttrRequiredReadonly.setReadOnly(true);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        source.addTransactionalMethod("add*", txAttrRequired);
        source.addTransactionalMethod("insert*", txAttrRequired);
        source.addTransactionalMethod("create*", txAttrRequired);
        source.addTransactionalMethod("save*", txAttrRequired);
        source.addTransactionalMethod("delete*", txAttrRequired);
        source.addTransactionalMethod("remove*", txAttrRequired);
        source.addTransactionalMethod("update*", txAttrRequired);
        source.addTransactionalMethod("merge*", txAttrRequired);
        source.addTransactionalMethod("modify*", txAttrRequired);
        source.addTransactionalMethod("exec*", txAttrRequired);
        source.addTransactionalMethod("set*", txAttrRequired);

        source.addTransactionalMethod("*", txAttrRequiredReadonly);
        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }

}

使用时在方法名要和source中配置的前缀相匹配,列如add,update,等