Spring Boot based on AbstractRoutingDataSource multi data source transaction problem

Project scenario:

After the method is annotated with @Transactional, multiple data sources are invalid and the default data source is used.


Problem Description

1. Custom annotations

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 layer

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 layer 

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. Question code

Here is an example of the error:

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

solution:

Add the @Transactional annotation to both the service layer and the dao layer, and the transaction propagation method uses Propagation.REQUIRES_NEW

1. Dao layer modification

Modify data source 2, add annotation @Transactional(propagation=Propagation.REQUIRES_NEW), data source 1 does not need to be modified in front

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 layer modification

    /**
     * 多数据源中的事务,同时使用数据源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;
    }

illustrate:

1. Propagation.REQUIRES_NEW: If there is a current transaction, suspend the current transaction, start a new transaction, and continue to run the external transaction after the new transaction is submitted;

2. A new thing will be restarted here, so data source 2 will also be executed;

3. In this way, no matter where an error is reported or an exception is thrown in the two methods, the transaction will be rolled back at the same time;

Disadvantages: The code is very intrusive, and the code with complex logic is troublesome


Here you can integrate com.baomidou, introduce dynamic-datasource dependencies, and use the @DSTransactional annotation: The use of Spring Boot multi-data source transactions @DSTransactional

Spring boot realizes multiple data sources: two ways of Spring Boot integrating Druid to realize multiple data sources - Taoge is a handsome blog - CSDN Blog 

Guess you like

Origin blog.csdn.net/u011974797/article/details/130154340