spring boot 多数据源实现

1、在application.properties文件中配置多个数据源

#主数据源
spring.datasource.core.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.core.jdbc-url=jdbc:mysql://127.0.0.1:3306/boot_v2?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
spring.datasource.core.username=root
spring.datasource.core.password=root

#日志数据源
spring.datasource.log.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.log.jdbc-url=jdbc:mysql://127.0.0.1:3306/log?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
spring.datasource.log.username=root
spring.datasource.log.password=root

2、编写自定义注解TargetDataSource,后面将通过自定义注解@TargetDataSource + AOP的方式实现数据源动态切换

package com.ldy.bootv2.demo.datasource.annotation;

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

/**
 * <p>类名: TargetDataSource</p>
 * <p>描述: 数据源切换注解</p>
 * <p>修改时间: 2019年4月18日 上午10:39:01</p> 
 * @author lidongyang
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface TargetDataSource {
	
	public static final String LOG = "log";
	public static final String CORE = "core";

	String value() default CORE;

}

3、编写DataSourceContextHolder,根据当前线程来选择具体的数据源

package com.ldy.bootv2.demo.datasource;

/**
 * <p>类名: DataSourceContextHolder</p>
 * <p>描述:根据当前线程来选择具体的数据源</p>
 * <p>修改时间: 2019年4月18日 上午10:34:48</p> 
 * @author lidongyang
 */
public class DataSourceContextHolder {

	/**
	 * 使用ThreadLocal存储数据源
	 */
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

	/**
	 * 指定要使用的数据源
	 */
	public static void setDataSource(String dbName) {
		contextHolder.set(dbName);
	}

	/**
	 * 获取数据源
	 */
	public static String getDataSource() {
		return (contextHolder.get());
	}

	/**
	 * 清空当前指定数据源,使用默认数据源
	 */
	public static void clearDataSource() {
		contextHolder.remove();
	}
	
}

 4、编写DynamicDataSource,继承AbstractRoutingDataSource,动态设置数据源

package com.ldy.bootv2.demo.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * <p>类名: DynamicDataSource</p>
 * <p>描述: 动态数据源</p>
 * <p>修改时间: 2019年4月18日 上午10:33:33</p> 
 * @author lidongyang
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		return DataSourceContextHolder.getDataSource();
	}
}

5、编写多数据源配置类DataSourceConfig

package com.ldy.bootv2.demo.datasource.config;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.ldy.bootv2.demo.datasource.DynamicDataSource;
import com.ldy.bootv2.demo.datasource.annotation.TargetDataSource;

/**
 * <p>类名: DataSourceConfig</p>
 * <p>描述: 多数据源配置类</p>
 * <p>修改时间: 2019年4月18日 上午10:29:17</p>
 * 
 * @author lidongyang
 */
@Configuration
public class DataSourceConfig {

	/**
	 * 默认数据源
	 */
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.core")
	public DataSource dataSourceCore() {
		return DataSourceBuilder.create().build();
	}

	/**
	 * 日志数据库
	 */
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.log")
	public DataSource dataSourceLog() {
		return DataSourceBuilder.create().build();
	}

	/**
	 * 动态数据源: 通过AOP在不同数据源之间动态切换
	 * 将数据库实例写入到targetDataSources属性中,并且使用defaultTargetDataSource属性设置默认数据源。
	 * Primary注解用于标识默认使用的 DataSource Bean,并注入到SqlSessionFactory的dataSource属性中去。
	 */
	@Primary
	@Bean
	public DataSource dynamicDataSource() {
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		// 默认数据源
		dynamicDataSource.setDefaultTargetDataSource(dataSourceCore());
		// 配置多数据源
		Map<Object, Object> dsMap = new HashMap<Object, Object>();
		dsMap.put(TargetDataSource.CORE, dataSourceCore());
		dsMap.put(TargetDataSource.LOG, dataSourceLog());

		dynamicDataSource.setTargetDataSources(dsMap);

		return dynamicDataSource;
	}

	/**
	 * 配置@Transactional注解事物
	 * 使用dynamicDataSource作为transactionManager的入参来构造DataSourceTransactionManager
	 */
	@Bean
	public PlatformTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dynamicDataSource());
	}
}

6、编写 DataSourceAspect,通过AOP动态切换数据源

package com.ldy.bootv2.demo.datasource.aop;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.ldy.bootv2.demo.datasource.DataSourceContextHolder;
import com.ldy.bootv2.demo.datasource.annotation.TargetDataSource;


/**
 * <p>类名: DynamicDataSourceAspect</p>
 * <p>描述: 自定义注解@TargetDataSource + AOP的方式实现数据源动态切换。</p>
 * <p>修改时间: 2019年4月18日 上午10:42:50</p> 
 * @author lidongyang
 */
@Aspect
@Component
@Order(-1) // 保证该AOP在@Transactional之前执行
public class DataSourceAspect {
	
	private static Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

	@Before("@annotation(dataSource)")
	public void beforeSwitchDataSource(JoinPoint point, TargetDataSource dataSource) throws Throwable {
		logger.info("切换数据库 [" + dataSource.value() + "] " + point.getSignature());
		try {
			// 切换数据源
			DataSourceContextHolder.setDataSource(dataSource.value());
		} catch (Exception e) {
			logger.error("切换数据库失败,使用默认数据库!原因:" + e.getMessage());
		}

	}

	@After("@annotation(dataSource)")
	public void afterSwitchDataSource(JoinPoint point, TargetDataSource dataSource) {
		logger.info("移除数据库 [" + dataSource.value() + "] " + point.getSignature());
		//方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
		DataSourceContextHolder.clearDataSource();
	}

}

7、在service层通过自定义注解TargetDataSource指定要使用的数据源,不指定则使用配置中的默认数据源

(1)使用默认数据源,不用加自定义注解TargetDataSource

public List<UserEntity> getAll() {
    return userMapper.getAll();
}

public UserEntity getOne(Integer id) {
    return userMapper.getOne(id);
}

(2)使用指定数据源,需要通过自定义注解TargetDataSource指定要使用的数据源

@TargetDataSource(value=TargetDataSource.LOG)
public List<UserLogEntity> getAll() {
    return userLogMapper.getAll();
}

@TargetDataSource(value=TargetDataSource.LOG)
public UserLogEntity getOne(Integer id) {
    return userLogMapper.getOne(id);
}

最后附一张项目结构图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值