SpringBoot使用事务(SpringBoot学习五)

SpringBoot使用事务

​ 上次了解了MySQL的事务概念,下面就开始编写代码来实际理解一下概念。

​ 配置上面没有什么新加的配置,使用的数据库是MySQL,集成的Mybatis。按照之前的博客配置就可以了,这里不再累述。下面就直接上代码。然后说一下遇到的问题。

package com.psq.train.mysql;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * TransactionalTrain.java
 * Description: SpringBoot数据库事务练习
 *
 * @author Peng Shiquan
 * @date 2020/6/13
 */
@Service
public class TransactionalTrain extends SqlSessionDaoSupport {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Override
    @Resource
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }


    //@PostConstruct
    public void mybatisTransactionalTrain() {
        //开启事务
        SqlSession sqlSession = sqlSessionFactory.openSession(false);
        TestUser testUser = new TestUser();
        testUser.setId(3);
        testUser.setName("qsp");
        testUser.setPassword("87654321");
        try {
            Integer saveResult = sqlSession.insert("com.psq.train.dao.UserMapper.insertUser", testUser);
            //重复插入,id相同一定回产生异常
            Integer saveResult2 = sqlSession.insert("com.psq.train.dao.UserMapper.insertUser", testUser);
            sqlSession.commit();
        } catch (Exception e) {
            System.err.println("事务开始回滚");
            sqlSession.rollback();
            System.err.println("事务回滚成功");
            throw e;
        } finally {
            sqlSession.close();
        }
    }
}

​ 首先是SqlSessionFactory的注入,因为Mybatis取消了SqlSessionFactory的自动注入,所以这里需要继承一下SqlSessionDaoSupport,再重写一下setSqlSessionFactory方法。后面其他方法就可以使用一下代码正常注入了:

@Autowired
private SqlSessionFactory sqlSessionFactory;

​ 下面就是逻辑代码的堆叠了,需要注意的就是sqlSessionFactory.openSession(false);的意思是关闭自动提交,这样就可以测试事务了。然后就是插入两个主键相同的列,第一个可以正常插入,第二个因为逐渐相同会插入失败,导致异常,捕获异常后执行sqlSession.rollback();就可以回滚了。这里也有一个地方,执行sqlSession.close();的时候也会执行回滚,通过看源码的时候可以发现最后是执行了回滚操作的。

​ 然后问题就来了,这里我尝试了很多次,事务回滚一直没有执行成功,数据库中最后仍然是一条数据。排查了很多的地方还是不行,这里也希望大神能够指导一下。

​ 最后没有办法,只能通过Spring来实现事务。

​ Spring中的事务管理分为编程式事务管理和声明式事务管理。编程式事务管理现在使用的已经很少了,现在大部分都是声明式事务管理。我这里也是通过注解的形式来实现事务的管理。上代码。

package com.psq.train.mysql;

import com.psq.train.dao.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * SpringTransactionalTrain.java
 * Description:  使用Spring来管理事务
 *
 * @author Peng Shiquan
 * @date 2020/6/13
 */
@Component
public class SpringTransactionalTrain {

    @Autowired
    private UserMapper userMapper;

    @Transactional
    public void mybatisTransactionalTrain() {
        TestUser testUser = new TestUser();
        testUser.setId(3);
        testUser.setName("psq");
        testUser.setPassword("9876");
        System.err.println(testUser.toString());
        try {
            Integer saveResult = userMapper.insertUser(testUser);
            System.err.println("插入成功");
            //一定会报异常
            Integer saveResult2 = userMapper.insertUser(testUser);
        } catch (Exception e) {
            System.err.println(e);
            System.err.println("开始回滚");
            throw e;
        }

    }
}

​ 通过controller层的调用,controller层代码如下:

package com.psq.train.controller;

import com.psq.train.dao.UserMapper;
import com.psq.train.mysql.SpringTransactionalTrain;
import com.psq.train.mysql.TestUser;
import com.psq.train.mysql.TransactionalTrain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * TransactionalController.java
 * Description:  controller类,用于测试
 *
 * @author Peng Shiquan
 * @date 2020/6/13
 */
@RestController
public class TransactionalController {

    @Autowired
    private SpringTransactionalTrain springTransactionalTrain;
    

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String saveTestUser() {
        springTransactionalTrain.mybatisTransactionalTrain();
        return "hello";
    }
}

​ 按照上面的代码,事务就可以正常回滚。数据库中就没有新插入的记录。

​ 下面就简单说一下spring中的事务的一些信息。

  • 事务的传播行为
    • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。
    • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
    • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
    • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
    • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
    • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。
  • 事务的实现方式
    • 编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
    • 基于 TransactionProxyFactoryBean的声明式事务管理。
    • 基于 @Transactional 的声明式事务管理。
    • 基于Aspectj AOP配置事务。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章