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 Boot项目中实现Jeessite架构下的多数据源配置,包括DynamicDataSource的使用、DataSourceEnum的定义、DataSourceHolder的配合、AOP切面的部署,并解决Service层数据源切换问题。
3142

被折叠的 条评论
为什么被折叠?



