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配置事务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Springboot中,事务传播机制指定在一个方法调用期间,如果一个事务已经存在,当前方法应该如何处理事务。在正确使用事务传播机制时,我们需要考虑以下几点: 1. 选择正确的事务传播级别。Springboot提供了七种不同的事务传播级别,包括: - Propagation.REQUIRED(默认值):如果当前存在事务,则加入该事务,否则新创建一个事务; - Propagation.SUPPORTS:如果当前存在事务,则加入该事务,否则以非事务方式执行; - Propagation.MANDATORY:当前必须存在事务,否则抛出异常; - Propagation.REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务; - Propagation.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,暂停当前事务; - Propagation.NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常; - Propagation.NESTED:创建一个新的嵌套事务,如果当前存在事务,则在当前事务中嵌套执行。 2. 确定事务的边界。通常,我们希望将每个服务方法作为一个独立的事务来处理,因此需要确保每个服务方法都用@Transactional注解进行标记。 3. 处理异常情况。当一个方法抛出异常时,事务有两种可能的行为。一种是回滚整个事务,另一种是回滚当前方法执行的部分数据。在编写代码时需要考虑这两种情况,同时还需要处理异常并避免事务悬挂的问题。 4. 避免过度使用事务事务管理是一个消耗资源的过程,对于大规模的系统来说,存在过度使用事务的风险,需要在性能和功能之间做出权衡。 总之,在正确使用事务的过程中,需要根据具体的情况选择合适的事务传播级别、确定事务的边界、处理异常情况,避免过度使用事务。只有这样才能有效地管理事务,并保证系统的正确性和性能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值