SpringBoot项目中编程式事务与声明式事务的区别

一、什么是事务?

我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务逻辑并未正确的完成,之前成功操作数据的并不可靠,需要在这种情况下进行回退。

事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。

事务管理是Spring框架中最为常用的功能之一,我们在使用Spring Boot开发应用时,大部分情况下也都需要使用事务。

事务我们都知道分为编程式事务与声明式事务,我们之前都知道在Spring中的区别,但在SpringBoot项目中是啥区别呢?我们通过两个例子来分析一下。

二、编程式事务

编程式事务是代码级别的,类似于JDBC的事务管理,Spring管理使用TransactionTemplate事务。

1、配置事务模板

package com.riemann.springbootdemo.transaction;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;

class MyTransactionTemplate {

    /**
     * 数据源加入事务管理
     * @param masterDataSource
     * @return
     */
    @Bean(name = "transactionManager")
    @Primary
    public DataSourceTransactionManager masterDataSourceTransactionManager(@Qualifier("masterDataSource") DataSource masterDataSource) {
        final DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(masterDataSource);
        return dataSourceTransactionManager;
    }

    @Bean(name = "transactionTemplate")
    @Primary
    public TransactionTemplate masterTransactionTemplate (@Qualifier("transactionManager") DataSourceTransactionManager transactionManager) {
        final TransactionTemplate transactionTemplate = new TransactionTemplate();
        transactionTemplate.setTransactionManager(transactionManager);
        transactionTemplate.setIsolationLevelName("ISOLATION_DEFAULT");
        transactionTemplate.setPropagationBehaviorName("PROPAGATION_REQUIRED");
        return transactionTemplate;
    }
}

2、测试类

package com.riemann.springbootdemo.transaction;

import com.riemann.springbootdemo.model.UserDomain;
import com.riemann.springbootdemo.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class MyTransactionTemplateTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyTransactionTemplateTest.class);

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserService userService;

    @GetMapping(value = "/addUser",produces = "application/json;charset=UTF-8")
    public void addUser() {

        UserDomain user1 = new UserDomain();
        user1.setUserId(2);
        user1.setUserName("andy");
        user1.setPassword("andy");
        user1.setPhone("15875571889");

        UserDomain user2 = new UserDomain();
        user2.setUserId(1);
        user2.setUserName("edgar");
        user2.setPassword("root");
        user2.setPhone("13607961855");

        List<UserDomain> userDomainList = new ArrayList<>();
        userDomainList.add(user1);
        userDomainList.add(user2);

        TransactionCallback transactionCallback = new TransactionCallback() {
            @Nullable
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                try {
                    for (UserDomain user : userDomainList) {
                        int count = userService.addUser(user);
                        if (count == 1) {
                            LOGGER.info("userId 为:" + user.getUserId() + " 添加成功");
                        } else {
                            LOGGER.info("userId 为:" + user.getUserId() + " 添加失败");
                        }
                    }
                } catch (Exception e) {
                    LOGGER.error("出现异常", e);
                    // 设置事务回滚
                    transactionStatus.setRollbackOnly();
                }
                return null;
            }
        };
        transactionTemplate.execute(transactionCallback);
    }

}

3、事先创建好表

事先插入一条 userId 为1的数据,并且 userId 为主键。
在这里插入图片描述

在这里插入图片描述
相信小伙伴们一看这报错就明白了什么意思了。

我们数据库有了 userId 为1的数据,插入两条数据,第一条插入为2的数据显示添加成功,第二条为1的数据跟数据库已有主键为1的userId冲突了,导致异常。然后我们再去数据库看,表里还是刚开始的那一条数据。

在这里插入图片描述

由结果得出结论,操作两条数据,只要有一条操作失败,那么编程式事务就会回滚。

三、声明式事务

管理建立在AOP基础上,本质是对方法前后进行拦截,所以声明式事务是方法级别的,使用的时候只需要在方法前面加上@Transactional注解。

声明式事务的代码如下:

package com.riemann.springbootdemo.transaction;

import com.riemann.springbootdemo.model.UserDomain;
import com.riemann.springbootdemo.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.ArrayList;
import java.util.List;

public class MyTransactionTemplateTest2 {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyTransactionTemplateTest.class);

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserService userService;

    @GetMapping(value = "/addUser",produces = "application/json;charset=UTF-8")
    @Transactional(rollbackFor = Exception.class)
    public void addUser() {

        UserDomain user1 = new UserDomain();
        user1.setUserId(2);
        user1.setUserName("andy");
        user1.setPassword("andy");
        user1.setPhone("15875571889");

        UserDomain user2 = new UserDomain();
        user2.setUserId(1);
        user2.setUserName("edgar");
        user2.setPassword("root");
        user2.setPhone("13607961855");

        List<UserDomain> userDomainList = new ArrayList<>();
        userDomainList.add(user1);
        userDomainList.add(user2);

        for (UserDomain user : userDomainList) {
            int count = userService.addUser(user);
            if (count == 1) {
                LOGGER.info("userId 为:" + user.getUserId() + " 添加成功");
            } else {
                LOGGER.info("userId 为:" + user.getUserId() + " 添加失败");
            }
        }
    }

}

测试效果跟编程式事务的结果是一样的。

四、小结

1、编程式事务需要你在代码中直接加入处理事务的逻辑,可能需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,如在执行a方法时候需要事务处理,你需要在a方法开始时候开启事务,处理完后。在方法结束时候,关闭事务。

2、声明式的事务的做法是在a方法外围添加注解或者直接在配置文件中定义,a方法需要事务处理,在spring中会通过配置文件在a方法前后拦截,并添加事务。其实使用的AOP面向切面的思想。

3、编程式事务侵入性比较强,但处理粒度更细。

发布了332 篇原创文章 · 获赞 198 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/riemann_/article/details/103739337
今日推荐