SpringBoot2-第三章:springboot的事务控制

4,259 阅读3分钟

上一章我们简单的介绍了swagger相关的实现,毕竟我都是直接贴代码了,确实也是挺简单的,哈哈哈。 这一章我们主要介绍在springboot中的事务控制。

本项目的GitHub:https://github.com/pc859107393/Go2SpringBoot.git

有兴趣交流springboot进行快速开发的同学可以加一下下面的企鹅群。

行走的java全栈

回顾事务控制

在传统的Spring项目中,我们经典三层中主要在service层进行事务控制,常见的配置是什么呢?具体实现如图3.1所示。

图3.1

图3.1 传统Spring项目的事务控制

在传统的Spring项目中,我们的事务控制需要考虑如下几点:

  • 开启事务管理
  • 区别只读事务和读写事务
  • 配置Spring事务控制规则
  • 找到service层的实现类

既然我们知道了传统的事务控制我们需要做的事情,理解了这一点,我们也就可以在springboot项目中推测我们需要哪些东西。 ①. 事务控制相关依赖资源 ②. 事务管理开启 ③.事务读写规则控制 ④.事务读写的实现.

在前面我们构建项目的时候已经加入了spring-boot-starter-aop,所以依赖资源不是问题。接着我们需要编写代码来实现相关的规则、开启事务和实现事务。

首先我们可以上网查询一下springboot相关的事务,无外乎都是懒人办法直接在service方法上面添加注解@Transactional和在入口类上面使用@EnableTransactionManagement,这样就能在项目中指定的方法上面开启事务了。

但是有没有一种更懒得方式呢?一定是有的,估计也是和Spring项目中类似。我们检验的标准是什么?肯定是在service对应的方法内发生异常引起回滚操作。其他的原理不必解释太多,先上码。

@SpringBootApplication
@EnableWebMvc
@EnableSwagger2
@MapperScan(value = ["cn.acheng1314.base.dao"])
@Configuration
@EnableTransactionManagement
class BaseApplication : WebMvcConfigurer {
    //事务类型参数
    fun transactionAttributeSource(): TransactionAttributeSource {
        val source = NameMatchTransactionAttributeSource()
        //只读或可写事务
        val readOnlyTx = RuleBasedTransactionAttribute()
        readOnlyTx.isReadOnly = true
        readOnlyTx.propagationBehavior = TransactionDefinition.PROPAGATION_SUPPORTS

        //可写事务
        val requiredTx = RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED
                , Collections.singletonList(RollbackRuleAttribute(Exception::class.java)))

        val txMap = HashMap<String, TransactionAttribute>()
        txMap["add*"] = requiredTx
        txMap["save*"] = requiredTx
        txMap["insert*"] = requiredTx
        txMap["update*"] = requiredTx
        txMap["delete*"] = requiredTx
        txMap["get*"] = readOnlyTx
        txMap["query*"] = readOnlyTx
        txMap["find*"] = readOnlyTx
        source.setNameMap(txMap)
        return source
    }

    /*事务拦截器*/
    @Bean(value = ["txInterceptor"])
    fun getTransactionInterceptor(tx: PlatformTransactionManager): TransactionInterceptor {
        return TransactionInterceptor(tx, transactionAttributeSource())
    }

    /**切面拦截规则 参数会自动从容器中注入 */
    @Bean
    fun pointcutAdvisor(txInterceptor: TransactionInterceptor): AspectJExpressionPointcutAdvisor {
        val pointcutAdvisor = AspectJExpressionPointcutAdvisor()
        pointcutAdvisor.advice = txInterceptor
        pointcutAdvisor.expression = "execution (* cn.acheng1314.base.service.*ServiceImpl.*(..))"
        return pointcutAdvisor
    }

    //省略其他代码
}

在上面的事务规则参数中我们设置了两边倒事务(readOnlyTx,使用了TransactionDefinition.PROPAGATION_SUPPORTS设置为两边倒事务)和可写入事务(requiredTx)。接着我们在UserServiceImpl中写一个发生异常的方法,测试是否能够产生事务回滚,代码片段如下:


//    @Transactional
    @Throws(Exception::class)
    fun addUser() {
        val user = User()
        user.duty = "aaa"
        user.loginName = "aaa"
        user.name = "aaa"
        user.password = "aaa"
        user.createDate = Date()
        baseMapper.insert(user)
        throw Exception("测试事务")
    }

我们来一点点代码进行单元测试,如下:

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest(classes = [BaseApplication::class])
class UserServiceImplTest {
    @Autowired
    private lateinit var userService: UserServiceImpl

    @Test
    fun addUserTest() {
        userService.addUser()
    }
}

最后运行测试代码后,我们可以发现在数据库中并没有aaa这个用户信息存在,所以我们事务控制成功了。具体效果不用上图了,大家都能在各自的数据库工具中看到。