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