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博客 

你可能感兴趣的:(Spring,spring,boot,事务,多数据源,DataSource)