您现在的位置是:首页 >学无止境 >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 {
@Autowired
private Test1Dao test1Dao;
@Autowired
private Test2Dao test2Dao;
/**
* 同一个数据源中的事务,都是数据源2
* 这里用的是spring的事务注解Transactional
* 这里必须加上注解多数据源注解@DS("db2"),否则使用的是默认数据源
*/
@DataSource("db2")
@Transactional
public void theSame() {
test2Dao.updateById(2,"第一次修改");
test2Dao.updateById(2,"第二次修改");
//这里报错回滚
int i = 1/0;
}
}
4、问题代码
这里是错误示例:
/**
* 多数据源中的事务,同时使用数据源1、2
* 这里用spring的事务注解Transactional,那么使用的是默认数据源,数据源2失效
*/
@Transactional
public 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)
*/
@Transactional
public 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博客
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。