1.首先声明一个事物注解,用于传入多个数据源的事物组,代码如下
import java.lang.annotation.*;
/**
* 事物注解
* @author xuxx
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DataSourceTransactionals {
/**
* 事物管理器祖
*/
String[] transactionManagers();
}
2.需要定义两个数据源的事物组,内容在数据源的配置里面 数据源1和数据源2 代码如下
数据源1 @Configuration @MapperScan(basePackages ="com.zjiec.erp.web.dao", sqlSessionTemplateRef = "ds1SqlSessionTemplate") public class MybatisPlusConfig4ds1 { //主数据源 ds1数据源 @Primary @Bean("ds1SqlSessionFactory") public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("ds1DataSource") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); configuration.setJdbcTypeForNull(JdbcType.NULL); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver(). getResources("classpath*:com/zjiec/erp/web/mp/*.xml")); sqlSessionFactory.setPlugins(new Interceptor[]{ new PaginationInterceptor(), new PerformanceInterceptor() // .setFormat(true), }); sqlSessionFactory.setGlobalConfig(new GlobalConfig().setBanner(false)); return sqlSessionFactory.getObject(); } @Primary @Bean(name = "ds1TransactionManager") public DataSourceTransactionManager ds1TransactionManager(@Qualifier("ds1DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Primary @Bean(name = "ds1SqlSessionTemplate") public SqlSessionTemplate ds1SqlSessionTemplate(@Qualifier("ds1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } 数据源2 @Configuration @MapperScan(basePackages ="com.zjiec.erp.web.yyxmldao", sqlSessionTemplateRef = "ds2SqlSessionTemplate") public class MybatisPlusConfig4ds2 { //从数据源 ds2数据源 @Bean("ds2SqlSessionFactory") public SqlSessionFactory ds2SqlSessionFactory(@Qualifier("ds2DataSource") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); configuration.setJdbcTypeForNull(JdbcType.NULL); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver(). getResources("classpath*:com/zjiec/erp/web/yyxmlmp/*.xml")); sqlSessionFactory.setPlugins(new Interceptor[]{ new PaginationInterceptor(), new PerformanceInterceptor() // .setFormat(true), }); sqlSessionFactory.setGlobalConfig(new GlobalConfig().setBanner(false)); return sqlSessionFactory.getObject(); } @Bean(name = "ds2TransactionManager") public DataSourceTransactionManager ds2TransactionManager(@Qualifier("ds2DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "ds2SqlSessionTemplate") public SqlSessionTemplate ds2SqlSessionTemplate(@Qualifier("ds2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }
3.声明一个事物切面用于获取到 事物注解,来进行手动提交事物 代码如下
import javafx.util.Pair; import org.aspectj.lang.annotation.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import java.util.Stack; /** * 数据源切面类 * @author xuxx * */ @Component @Aspect public class DataSourceTransactionAspect { /** * 线程本地变量 为什么使用栈?※为了达到后进先出的效果※ */ private static final ThreadLocal<Stack<Pair<DataSourceTransactionManager, TransactionStatus>>> THREAD_LOCAL = new ThreadLocal<>(); /** * 用于获取事务管理器 */ @Autowired private ApplicationContext applicationContext; /** * 事务声明 */ private DefaultTransactionDefinition def = new DefaultTransactionDefinition(); { // 非只读模式 def.setReadOnly(false); // 事务隔离级别;采用数据库的 def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT); // 事务传播行为 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); } /** * 切面 */ @Pointcut("@annotation(com.zjiec.erp.web.annotation.DataSourceTransactionals)") public void pointcut(){ } /** * 事务声明 */ @Before("pointcut() && @annotation(transactional)") public void before(DataSourceTransactionals transactional){ // 根据设置的事务名称按顺序声明;并放到ThreadLocal里 String[] transactionManagerNames = transactional.transactionManagers(); Stack<Pair<DataSourceTransactionManager, TransactionStatus>> pairStack = new Stack<>(); for (String transactionManagerName : transactionManagerNames) { DataSourceTransactionManager transactionManager = applicationContext.getBean(transactionManagerName, DataSourceTransactionManager.class); TransactionStatus transactionStatus = transactionManager.getTransaction(def); pairStack.push(new Pair(transactionManager, transactionStatus)); } THREAD_LOCAL.set(pairStack); } /** * 提交事务 */ @AfterReturning("pointcut()") public void afterReturning() { // ※栈顶弹出(后进先出); Stack<Pair<DataSourceTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get(); while (!pairStack.empty()) { Pair<DataSourceTransactionManager, TransactionStatus> pair = pairStack.pop(); pair.getKey().commit(pair.getValue()); } THREAD_LOCAL.remove(); } /** * 回滚事务 */ @AfterThrowing(value = "pointcut()") public void afterThrowing() { // ※栈顶弹出(后进先出) Stack<Pair<DataSourceTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get(); while (!pairStack.empty()) { Pair<DataSourceTransactionManager, TransactionStatus> pair = pairStack.pop(); pair.getKey().rollback(pair.getValue()); } THREAD_LOCAL.remove(); } }
4.事物注解的使用,直接在serviceImpl类的方法上加入注解
@DataSourceTransactionals(transactionManagers = {"ds1TransactionManager","ds2TransactionManager"})