SSM 多数据源同一事务控制

整合

springboot基于注解动态配置多数据源以及多数据源的事务统一 - 炫舞风中 - 博客园

Spring多数据源事务管理_bug猫的博客-CSDN博客_spring多数据源事务管理 两位博有中出现的不足,进行优化后得出新的总结,废话不多说 直接上代码, 本人亲测有效

1.application.xml 数据源配置

<!--base 配置基础数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 
        <property name="url" value="jdbc:mysql://XXXXX:XXXX/XXXX?characterEncoding=UTF-8&amp;autoReconnect=true&amp;zeroDateTimeBehavior=convertToNull&amp;serverTimezone=UTC" />  
        <property name="username" value="XXXX" />  
        <property name="password" value="XXXXX" />


        <!-- 初始化连接大小 -->
        <property name="initialSize" value="5"></property>
        <!-- 连接池最大数量 -->
        <property name="maxActive" value="200"></property>
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="100"></property>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="10"></property>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="10"></property>

        <property name="validationQuery" value="SELECT 1" />
        <property name="testOnBorrow" value="true" />
    </bean>
    
    <bean id="basesqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
        <!-- 自动扫描mapping.xml文件 -->  
        <property name="mapperLocations" value="classpath:sqlmap/mybatis/base/*.xml"></property>  
        
        <property name="configLocation" value="WEB-INF/config/mybatis-config.xml"></property>
        
        <property name="plugins">
            <array>
                <bean id="sqlStatementInterceptor" class="com.zhsh.base.utils.SqlStatementInterceptor"/>
            </array>
        </property>
        
    </bean> 
    
    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
    <bean id="baseconfigurer"  class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.local.*.dao" />
        <property name="sqlSessionFactoryBeanName" value="basesqlSessionFactory"></property>  
    </bean>  
      
      <!-- 事务管理 -->
    <bean id="basetx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
  
      <!-- 使用注解开启事务,事务管理器为 basetx-->
    <tx:annotation-driven transaction-manager="basetx" proxy-target-class="true" />
    

 <!-- oracle -->
    <bean id="dataSource_oracle" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@XXXXXX:1521:orcl" />
        <property name="username" value="XXXXXX"></property>
        <property name="password" value="XXXX"></property>

        
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="5"></property>
        <!-- 连接池最大数量 -->
        <property name="maxActive" value="200"></property>
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="100"></property>
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="10"></property>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="10"></property>

        <property name="validationQuery" value="SELECT 'ORACLE' from dual" />
        <property name="testOnBorrow" value="true" />
        
    </bean>

    <bean id="oraclesqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource_oracle" />  
        <!-- 自动扫描mapping.xml文件 -->  
        <property name="mapperLocations" value="classpath:sqlmap/mybatis/ora/*.xml"></property>  
        
        <property name="configLocation" value="WEB-INF/config/mybatis-config.xml"></property>
        
        <property name="plugins">
            <array>
                <bean id="sqlStatementInterceptor" class="com.zhsh.base.utils.SqlStatementInterceptor"/>
            </array>
        </property>
        
    </bean> 
    
    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
    <bean id="oracleconfigurer"  class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.local.*.oradao" />
        <property name="sqlSessionFactoryBeanName" value="oraclesqlSessionFactory"></property>  
    </bean>  
      
      <!-- 事务管理 -->
    <bean id="oracletx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource_oracle" />
    </bean>

2.核心java代码

2.1 Pointcuts 切面

import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Stack;


@Aspect
@Component
public class Pointcuts {

    /**
     * 多数据源组合事务控制
     * */
    @Pointcut("@annotation(com.local.datasouse.TransactionalControl)")
    public void pointcut() {};


    @Around(value = "pointcut()&&@annotation(annotation)")
    public Object twiceAsOld(ProceedingJoinPoint point, TransactionalGroup annotation) throws Throwable {
        Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
        Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();
        try {
            if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, annotation)) {
                return null;
            }
            Object ret = point.proceed();
            commit(dataSourceTransactionManagerStack, transactionStatuStack);
            return ret;
        } catch (Throwable e) {
            rollback(dataSourceTransactionManagerStack, transactionStatuStack);
            throw e;
        }
    }
    /**
     * 开启事务处理方法
     *
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     * @param multiTransactional
     * @return
     */
    private boolean openTransaction(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                                    Stack<TransactionStatus> transactionStatuStack, TransactionalGroup multiTransactional) {

        String[] transactionMangerNames = multiTransactional.name();
        if (ArrayUtils.isEmpty(multiTransactional.name())) {
            return false;
        }

        for (String beanName : transactionMangerNames) {
            //根据事务名称获取具体的事务
            DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) SpringContextUtil
                    .getBean(beanName);
            TransactionStatus transactionStatus = dataSourceTransactionManager
                    .getTransaction(new DefaultTransactionDefinition());
            transactionStatuStack.push(transactionStatus);
            dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
        }
        return true;
    }

    /**
     * 提交处理方法
     *
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void commit(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                        Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().commit(transactionStatuStack.pop());
        }
    }
    /**
     * 回滚处理方法
     *
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuStack
     */
    private void rollback(Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
                          Stack<TransactionStatus> transactionStatuStack) {
        while (!dataSourceTransactionManagerStack.isEmpty()) {
            dataSourceTransactionManagerStack.pop().rollback(transactionStatuStack.pop());
        }
    }
}
2.2 TransactionalControl

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionalControl {
    String[] name() default {"basetx"};
}

3.获取spring容器中的bean

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware{
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    /**
     * @Description: 获取spring容器中的bean,通过bean名称获取
     * @param beanName bean名称
     * @return: Object 返回Object,需要做强制类型转换
     */
    public static Object getBean(String beanName){
        return applicationContext.getBean(beanName);
    }

    /**
     * @Description: 获取spring容器中的bean, 通过bean类型获取
     * @param beanClass bean 类型
     * @return: T 返回指定类型的bean实例
     */
    public static <T> T getBean(Class<T> beanClass) {
        return applicationContext.getBean(beanClass);
    }

    /**
     * @Description: 获取spring容器中的bean, 通过bean名称和bean类型精确获取
     * @param beanName bean 名称
     * @param beanClass bean 类型
     * @return: T 返回指定类型的bean实例
     */
    public static <T> T getBean(String beanName, Class<T> beanClass){
        return applicationContext.getBean(beanName,beanClass);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值