整合
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&autoReconnect=true&zeroDateTimeBehavior=convertToNull&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);
}
}