spring mybatis 多数据源配置 jeesite 多数据源配置

本文详细介绍如何在Spring Boot项目中实现Jeessite架构下的多数据源配置,包括DynamicDataSource的使用、DataSourceEnum的定义、DataSourceHolder的配合、AOP切面的部署,并解决Service层数据源切换问题。

spring mybatis 多数据源配置 jeesite 多数据源配置

一、情景描述

        在系统数据达到一定的访问量时,遇到单个数据库瓶颈,所以需要扩展数据库,启用第二个数据源资源,项目架构变成 一个服务对应多个数据源的形式。

二、步骤

        1、原理:org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,实现动态数据源的切换。

        2、创建 DynamicDataSource 继承 AbstractRoutingDataSource , 重写 determineCurrentLookupKey 方法,配置数据源切换

        3、创建 DataSourceEnum 定义数据源类型

        4、创建 DataSourceHolder ,提供数据源切换

        5、创建多个数据源 JK , SEC

        6、配置到 DynamicDataSource 中

        7、同步调 配置文件中数据源改为: dynamicDataSource

  • org.mybatis.spring.SqlSessionFactoryBean (spring-context.xml 文件中)
  • org.springframework.jdbc.datasource.DataSourceTransactionManager (spring-context.xml 文件中)
  • org.activiti.spring.SpringProcessEngineConfiguration (spring-context-activiti.xml)

        8、在Controller中,使用: DataSourceHolder.setDataSource(DataSourceEnum.SEC); 可实现数据源的切换。

        9、实现数据源动态切换:

  • 定义 DataSource 注解
  • 配置AOP切面: DataSourceAspect
  • 在 Controller方法上,使用 @DataSource(DataSourceEnum.SEC) 注解,实现数据源的切换

        10、遗留问题:在Service层,切换数据源无效,经过日志打印发现,在从 Controller 进入到Service时,会调用数据源 , 之后可以进行切换,但是遇到切换无效 ... 个人推测,是因为 Service 继承的 Dao层的缘故 ... 问题有待确认!

三、代码实现

        1、创建 DynamicDataSource 类

import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * description: 动态数据源切换
 * @version v1.0
 * @author w
 * @date 2021年5月7日下午2:56:09
 **/
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);


	public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
		super.setDefaultTargetDataSource(defaultTargetDataSource);
		super.setTargetDataSources(targetDataSources);
		super.afterPropertiesSet();
	}
	
	public DynamicDataSource() {
	}

	@Override
	protected Object determineCurrentLookupKey() {
		logger.info("use dataSource , {}" , DataSourceHolder.getDataSource());
		return DataSourceHolder.getDataSource();
	}
}

        2、创建 DataSourceEnum 定义数据源类型

/**
 * description: 数据源类型
 * @version v1.0
 * @author w
 * @date 2021年5月10日下午3:40:07
 **/
public enum DataSourceEnum {
	
	/**
	 * 默认数据源JK
	 */
	JK ,
	
	/**
	 * 第二个数据源
	 */
	SEC ; 
}

        3、创建 DataSourceHolder ,提供数据源切换

import com.thinkgem.jeesite.common.persistence.enums.DataSourceEnum;

/**
 * description: 数据源提供
 * @version v1.0
 * @author w
 * @date 2021年5月10日下午2:44:22
 **/
public class DataSourceHolder {
	
	private static final ThreadLocal<String> CONTAINER = new ThreadLocal<String>();
	
	public static String getDataSource() {
		return CONTAINER.get();
	}
	
	public static void setDataSource(String type) {
		CONTAINER.set(type);
	}
	
	public static void setDataSource(DataSourceEnum dataSourceEnum) {
		CONTAINER.set(dataSourceEnum.name());
	}
	
	public static void clearDataSource() {
		CONTAINER.remove();
	}
	
}

        4、创建多个数据源 JK , SEC

<bean id="secDataSource" class="com.alibaba.druid.pool.DruidDataSource" 
    init-method="init" destroy-method="close"> 
	    <property name="driverClassName" value="${jdbc.driver}" />
     ... 略 ...
     

        5、配置到 DynamicDataSource 中

<bean id="dynamicDataSource" class="com.thinkgem.jeesite.common.persistence.DynamicDataSource">
	<property name="targetDataSources">
		<map key-type="java.lang.String">
			<description>build dataSource key value mapping</description>
			<entry key="JK" value-ref="jkDataSource"></entry>
			<entry key="SEC" value-ref="secDataSource"></entry>
		</map>
	</property>
	<property name="defaultTargetDataSource" ref="jkDataSource"></property>
</bean>

        6、同步调整:spring-context.xml 中,原 dataSource 调整为 dynamicDataSource spring-context-activiti.xml dataSource 调整为 dynamicDataSource

        7、创建 DataSource 注解

**
 * description: 切换数据源的注解
 * @version v1.0
 * @author w
 * @date 2021年5月10日下午3:42:14
 **/
@Target(value = { ElementType.METHOD , ElementType.TYPE })
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DataSource {

	/**
	 * description: 实现数据源的切换
	 * @return DataSourceEnum
	 * @version v1.0
	 * @author w
	 * @date 2021年5月10日 下午3:45:40
	 */
	DataSourceEnum value() default DataSourceEnum.JK ;
}

        8、创建 DataSourceAspect 类,配置AOP切面 (需要在 spring-mvc.xml 文件中,配置启用注解 <aop:aspectj-autoproxy/>

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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.thinkgem.jeesite.common.persistence.DataSourceHolder;
import com.thinkgem.jeesite.common.persistence.annotation.DataSource;

/**
 * description: 数据源的切面 配置
 * @version v1.0
 * @author w
 * @date 2021年5月10日下午4:09:17
 **/
@Component
@Order(10)
@Aspect
public class DataSourceAspect {
	
    private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
    
    /**
     * description: 配置切点
     * @return void
     * @version v1.0
     * @author w
     * @date 2021年5月10日 下午4:13:32
     */
    @Pointcut(value = "@annotation(com.thinkgem.jeesite.common.persistence.annotation.DataSource)"
    				  +"|| @within(com.thinkgem.jeesite.common.persistence.annotation.DataSource)")
    public void pointCut() {
    	
    }
	
    /**
     * description: 配置环绕 
     * @param point
     * @throws Throwable
     * @return Object
     * @version v1.0
     * @author w
     * @date 2021年5月10日 下午4:25:51
     */
	@Around("pointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		MethodSignature methodSignature = (MethodSignature)point.getSignature();
		DataSource annotation = methodSignature.getMethod().getAnnotation(DataSource.class);
		if(null != annotation) {
			DataSourceHolder.setDataSource(annotation.value().name());
			logger.info("数据源切换成功~~ use dataSource , {}" , annotation.value());
		}else {
			// 尝试从类上获取
			DataSource findAnnotation = AnnotationUtils.findAnnotation(methodSignature.getDeclaringType(), DataSource.class);
			if(null != findAnnotation) {
				DataSourceHolder.setDataSource(findAnnotation.value().name());
				logger.info("数据源切换成功~~ use dataSource findAnnotation , {}" , findAnnotation.value());
			}
		}
		try {
			return	point.proceed();
		} finally {
			DataSourceHolder.clearDataSource();
		}
	}
	
}

        9、在Controller中的方法上,使用 @DataSource(DataSourceEnum.SEC) 注解,即可实现数据源切换。

    @DataSource(DataSourceEnum.SEC)
	@RequestMapping(value = {"list", ""})
	@ResponseBody
	public ResultVO list(args...) {
		return ResultVO.success(page);
	}

四、总结

        1、使用Spring提供的 AbstractRoutingDataSource 类,实现数据源的切换。

        2、使用Spring AOP 实现,动态数据源切换,避免在Controller中硬编码。

参考资料:Spring, MyBatis 多数据源的配置和管理

【Mybatis】MyBatis之配置多数据源(十)

ThreadLocal作用理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值