开发过程中需要用到多数据库,此文基于 Spring Boot + MyBatis
一、配置数据源:修改application.properties如下
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
mybatis.config-locations=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/*.xml
mybatis.type-aliases-package=model
#mysql1 注意:数据库名为testdb1
spring.datasource.url=jdbc:mysql://localhost:3306/testdb1?allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=******
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.test-on-borrow=false
spring.datasource.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=3600000
#mysql2 注意:数据库名为testdb2 mysql版本大于5.6时,需增加&useSSL=false
sqls.datasource.url=jdbc:mysql://localhost:3308/testdb2?allowMultiQueries=true&useSSL=false
sqls.datasource.username=root
sqls.datasource.password=******
sqls.datasource.driver-class-name=com.mysql.jdbc.Driver
sqls.datasource.test-on-borrow=false
sqls.datasource.test-while-idle=true
sqls.datasource.time-between-eviction-runs-millis=3600000
二、列举数据源的key,在common包下创建DatabaseType.java
package common;
/**
* 列出所有的数据源key(用数据库名称来命名)
* 注意:
* 1)这里数据源与数据库是一对一的
* 2)DatabaseType中的变量名称就是数据库的名称
*/
public enum DatabaseType {
testdb1,testdb2;
}
三、在common包下创建DynamicDataSource.java
package common;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static void setDatabaseType(DatabaseType type){
contextHolder.set(type);
}
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
protected Object determineCurrentLookupKey() {
return getDatabaseType();
}
}
四、在common包下创建DataSourceAspect.java
package common;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import service.TestService;
@Aspect
@Component
public class DataSourceAspect {
/**
* 使用空方法定义切点表达式
* 指定包含service包下的所有类
*/
@Pointcut("execution(* service.*.*(..))")
public void declareJointPointExpression() {
}
/**
* 使用定义切点表达式的方法进行切点表达式的引入
*/
@Before("declareJointPointExpression()")
public void setDataSourceKey(JoinPoint point) {
// TestService是指要指定数据源的类,不这样指定则采用默认数据库testdb1
if (point.getTarget() instanceof TestService) {
DynamicDataSource.setDatabaseType(DatabaseType.testdb2);//这里指明采用数据库testdb2
}
}
}
五、配置Spring Boot的启动项
package Application;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import common.DatabaseType;
import common.DynamicDataSource;
@EnableAutoConfiguration
@Configuration
@EnableTransactionManagement
@EnableScheduling
@ComponentScan(basePackages={"controller","service","dao","common"})
@MapperScan({"dao"})
public class StartApplication extends SpringBootServletInitializer {
@Autowired
private Environment env;
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(StartApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
//创建数据源 注意名称
@Bean
public DataSource testdb1DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("spring.datasource.driver-class-name"));
props.put("url", env.getProperty("spring.datasource.url"));
props.put("username", env.getProperty("spring.datasource.username"));
props.put("password", env.getProperty("spring.datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
//创建数据源 注意名称
@Bean
public DataSource testdb2DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("sqls.datasource.driver-class-name"));
props.put("url", env.getProperty("sqls.datasource.url"));
props.put("username", env.getProperty("sqls.datasource.username"));
props.put("password", env.getProperty("sqls.datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
* 注意这里用到的名称:testdb1DataSource、testdb2DataSource、testdb1、testdb2 都是对应的,请根据
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("testdb1DataSource") DataSource testdb1DataSource,
@Qualifier("testdb2DataSource") DataSource testdb2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.testdb1, testdb1DataSource);
targetDataSources.put(DatabaseType.testdb2, testdb2DataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(testdb1DataSource);// 默认的datasource设置为myTestDbDataSource
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("testdb1DataSource") DataSource testdb1DataSource,
@Qualifier("testdb2DataSource") DataSource testdb2DataSource) throws Exception{
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(this.dataSource(testdb1DataSource, testdb2DataSource));
// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
fb.setTypeAliasesPackage(env.getProperty("dao"));// 指定基包
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
fb.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
六、TestMapper
package dao;
import org.springframework.stereotype.Component;
@Component
public interface TestMapper {
//获取服务数
int GetGoodsCount();
//获取店铺数
int GetShopCount();
}
七、TestService
package service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import common.DatabaseType;
import common.DynamicDataSource;
import dao.TestMapper;
import dao.UsersMapper;
@Service("TestService")
public class TestService {
@Autowired
private TestMapper testMapper;
//调用示例-返回int 获取服务总数
public int GetGoodsCount() {
return testMapper.GetGoodsCount();
}
//调用示例-返回int 获取店铺总数
public int GetShopCount() {
DynamicDataSource.setDatabaseType(DatabaseType.testdb1);//写了这一句则按指定数据源,不会按照DataSourceAspect里面对整个类的指定
return testMapper.GetShopCount();
}
}
本过程只给出了与多数据源相关的地方,请根据自己的项目适当调整。
本文是基于大神的文章,做了少许调整。
原创地址:
https://www.cnblogs.com/java-zhao/p/5413845.html
http://www.cnblogs.com/java-zhao/p/5415896.html