> 文章列表 > Spring Boot基于AbstractRoutingDataSource多数据源事务问题

Spring Boot基于AbstractRoutingDataSource多数据源事务问题

Spring Boot基于AbstractRoutingDataSource多数据源事务问题

项目场景:

方法加上@Transactional注解后,多数据源失效,使用的默认数据源。


问题描述

1、自定义注解

package com.test.datasources.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/* 多数据源注解*/
//同时支持方法注解和类注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {String value() default "";
}

2、dao层

package com.test.mapper;import com.baomidou.dynamic.datasource.annotation.DS;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;//数据源1
@DataSource("db1")
@Mapper
public interface Test1Dao {@Update("update test1 set name = #{name} where id = #{id}")void updateById(@Param("id")Integer id, @Param("name")String name);
}
package com.test.mapper;import com.baomidou.dynamic.datasource.annotation.DS;
import com.test.datasources.DataSourceNames;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;//数据源2
@DataSource(“db2”)
@Mapper
public interface Test2Dao {@Update("update test2 set name = #{name} where id = #{id}")void updateById(@Param("id")Integer id, @Param("name")String name);
}

 3、service层 

package com.test.service;import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.test.mapper.Test1Dao;
import com.test.mapper.Test2Dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class TestService {@Autowiredprivate Test1Dao test1Dao;@Autowiredprivate Test2Dao test2Dao;/* 同一个数据源中的事务,都是数据源2* 这里用的是spring的事务注解Transactional* 这里必须加上注解多数据源注解@DS("db2"),否则使用的是默认数据源*/@DataSource("db2")@Transactionalpublic void theSame() {test2Dao.updateById(2,"第一次修改");test2Dao.updateById(2,"第二次修改");//这里报错回滚int i = 1/0;}}

 4、问题代码

这里是错误示例:

    /* 多数据源中的事务,同时使用数据源1、2* 这里用spring的事务注解Transactional,那么使用的是默认数据源,数据源2失效*/@Transactionalpublic void notAlike() {test1Dao.updateById(1,"第一次修改");test2Dao.updateById(2,"第二次修改");//这里报错回滚int i = 1/0;}

解决方案:

在service层和dao层都加上@Transactional注解,事务传播方式使用Propagation.REQUIRES_NEW

1、dao层修改

修改数据源2,增加注解@Transactional(propagation=Propagation.REQUIRES_NEW),数据源1在前面不用修改

package com.test.mapper;import com.baomidou.dynamic.datasource.annotation.DS;
import com.test.datasources.DataSourceNames;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;//数据源2
@DataSource(“db2”)
@Mapper
public interface Test2Dao {@Transactional(propagation=Propagation.REQUIRES_NEW)@Update("update test2 set name = #{name} where id = #{id}")void updateById(@Param("id")Integer id, @Param("name")String name);
}

2、service层修改

    /* 多数据源中的事务,同时使用数据源1、2* 这里用spring的事务注解Transactional* 数据源2,test2Dao增加了注解@Transactional(propagation=Propagation.REQUIRES_NEW)*/@Transactionalpublic void notAlike() {test1Dao.updateById(1,"第一次修改");test2Dao.updateById(2,"第二次修改");//这里报错回滚int i = 1/0;}

说明:

1、Propagation.REQUIRES_NEW:如果当前存在事务,则挂起当前事务,开启一个新的事务,新事务提交后,则继续运行外部事务;

2、这里会重新开启一个新事物,所以数据源2也会执行;

3、这样无论在两个方法中哪个地方报错抛出异常都会使事务同时回滚;

缺点:代码侵入性大,逻辑复杂的代码比较麻烦


这里可以集成com.baomidou,引入dynamic-datasource依赖,使用@DSTransactional注解:Spring Boot多数据源事务@DSTransactional的使用_涛哥是个大帅比的博客-CSDN博客

spring boot实现多数据源:Spring Boot集成Druid实现多数据源的两种方式_涛哥是个大帅比的博客-CSDN博客 

老人咖美文