多数据源事务(非分布式)多数据源同时回滚

此项目为单体应用,不涉及到分布式。所以没有采用分布式事务来解决此次问题。 

springboot在多数据源时默认只能开启一个主数据库的事务,如果要同时开启多个数据源的事务,并回滚,需要在切面中手动开启所有数据源事务,并同时回滚。

1、pom.xml

        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            com.oracle
            ojdbc6
            11.2.0.3
        
        
        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            org.springframework.boot
            spring-boot-starter-aop
        

2、application.properties

server.port=8080
##数据源1
spring.datasource.one.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.one.jdbc-url=jdbc:oracle:thin:@192.168.111.200:1521:orcl
spring.datasource.one.username=root
spring.datasource.one.password=root
##数据源2
spring.datasource.two.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.two.jdbc-url=jdbc:oracle:thin:@192.168.111.201:1521:orcl
spring.datasource.two.username=root
spring.datasource.two.password=root

3、多数据源配置

package com.study.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * 数据源配置
 */
@Configuration
public class DataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.one")
    DataSource dsOne() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.two")
    DataSource dsTwo() {
        return DataSourceBuilder.create().build();
    }

    @Primary//系统应该默认执行该方法,如获取时不指定名称,则默认获取这个数据源,如果不添加,则启动时候会报错
    @Bean("jdbcTemplateOne")
    JdbcTemplate jdbcTemplateOne(@Qualifier("dsOne") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean("jdbcTemplateTwo")
    JdbcTemplate jdbcTemplateTwo(@Qualifier("dsTwo") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }


    /**
     * 配置第一个数据源的事务管理
     */
    @Bean(name = "oneTransactionManager")
    @Primary
    public PlatformTransactionManager oneTransactionManager(@Qualifier("dsOne") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 配置第二个数据源的事务管理
     */
    @Bean(name = "twoTransactionManager")
    public PlatformTransactionManager twoTransactionManager(@Qualifier("dsTwo") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

4、自定义注解

package com.study.anno;

import java.lang.annotation.*;

/**
 * 自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)//控制注解的生命周期,定义我们自己写的注解何时有效
@Target(ElementType.METHOD)//定义我们写的注解可以描述的成员,这里表示只能在方法上使用
public @interface MyTransactional {
    String[] transactionManagers();

}

5、AOP处理事务提交或回滚

package com.study.aop;

import com.study.anno.MyTransactional;
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;

/**
 * 多数据源切面
 */
@Component
@Aspect
public class TransactionAspect {
    /**
     * 线程本地变量:为什么使用栈?※为了达到后进先出的效果※
     */
    private static final ThreadLocal>> THREAD_LOCAL = new ThreadLocal<>();
    /**
     * 用于获取事务管理器
     */
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 切面
     */
    @Pointcut("@annotation(transactional)")
    public void pointcut(MyTransactional transactional) {
    }

    /**
     * 声明事务
     * @param transactional 注解
     */
    @Before("pointcut(transactional)")
    public void before(MyTransactional transactional) {
        // 根据设置的事务名称按顺序声明,并放到ThreadLocal里
        String[] transactionManagerNames = transactional.transactionManagers();
        Stack> pairStack = new Stack<>();
        for (String transactionManagerName : transactionManagerNames) {
            DataSourceTransactionManager transactionManager = applicationContext.getBean(transactionManagerName, DataSourceTransactionManager.class);
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 非只读模式
            def.setReadOnly(false);
            // 事务隔离级别:采用数据库的
            def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
            // 事务传播行为
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            TransactionStatus transactionStatus = transactionManager.getTransaction(def);
            pairStack.push(new Pair(transactionManager, transactionStatus));
        }
        THREAD_LOCAL.set(pairStack);
    }

    /**
     * 提交事务
     */
    @AfterReturning("pointcut(transactional)")
    public void afterReturning(MyTransactional transactional) {
        // ※栈顶弹出(后进先出)
        Stack> pairStack = THREAD_LOCAL.get();
        while (!pairStack.empty()) {
            Pair pair = pairStack.pop();
            pair.getKey().commit(pair.getValue());
        }
        THREAD_LOCAL.remove();
    }

    /**
     * 回滚事务
     */
    @AfterThrowing(value = "pointcut(transactional)")
    public void afterThrowing(MyTransactional transactional) {
        // ※栈顶弹出(后进先出)
        Stack> pairStack = THREAD_LOCAL.get();
        while (!pairStack.empty()) {
            Pair pair = pairStack.pop();
            pair.getKey().rollback(pair.getValue());
        }
        THREAD_LOCAL.remove();
    }
}

6、使用自定义注解加在需要使用事务的方法上

package com.study.service;

import com.study.anno.MyTransactional;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class DemoService {
    //@Autowired默认按类型装配,如果想使用名称装配可以结合@Qualifier注解进行使用。
    //@Resource,默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。
    @Resource
    private JdbcTemplate jdbcTemplateOne;
    @Resource
    private JdbcTemplate jdbcTemplateTwo;

    @MyTransactional(transactionManagers = {"oneTransactionManager", "twoTransactionManager"})
    public void test() {
        insert1();
        insert2();
        int c = 1 / 0;
    }

    public void insert1() {
        String sql1 = "insert into mytable1(a) values ('5555')";
        int update1 = jdbcTemplateOne.update(sql1);
        System.out.println(update1);
    }

    public void insert2() {
        String sql2 = "insert into mytable2(a) values ('5555')";
        int update2 = jdbcTemplateTwo.update(sql2);
        System.out.println(update2);
    }
}

参考

多数据源事务(非分布式)_SomeOtherTime的博客-CSDN博客_多数据源事务

你可能感兴趣的:(java,事务,多数据源,多数据源事务)