本项目使用spring-boot + mybatis(tk.mybatis)
项目结构如下
![项目结构](https://img-blog.csdn.net/20171027164202247?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGh5MTA3MTA3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1、设置springboot 启动类,禁用自动数据源配置
@SpringBootApplication(scanBasePackages = "me.smallyellow.hhy", exclude = {DataSourceAutoConfiguration.class})
@EnableWebPath
public class Application {
public static void main( String[] args ) {
SpringApplication.run(Application.class, args);
}
}
2、在config包下新建MyBatisConfig.java,对数据源进行相关设置,项目启动会走这个类,读取相关配置信息。
@Configuration
public class MyBatisConfig implements EnvironmentAware{
private Environment environment;
@Override
public void setEnvironment(Environment arg0) {
this.environment = arg0;
}
/**
* 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
*/
@Bean
public DataSource test1DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", environment.getProperty("test1-datasource.driverClassName"));
props.put("url", environment.getProperty("test1-datasource.url"));
props.put("username", environment.getProperty("test1-datasource.username"));
props.put("password", environment.getProperty("test1-datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
@Bean
public DataSource test2DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", environment.getProperty("test2-datasource.driverClassName"));
props.put("url", environment.getProperty("test2-datasource.url"));
props.put("username", environment.getProperty("test2-datasource.username"));
props.put("password", environment.getProperty("test2-datasource.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("test1DataSource") DataSource test1DataSource,
@Qualifier("test2DataSource") DataSource test2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.test1, test1DataSource);
targetDataSources.put(DatabaseType.test2, test2DataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(test1DataSource);
return dataSource;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
scannerConfigurer.setBasePackage("me.smallyellow.hhy.mapper");
Properties props = new Properties();
props.setProperty("mappers", "tk.mybatis.mapper.common.Mapper");
props.setProperty("IDENTITY", "MYSQL");
props.setProperty("notEmpty", "true");
scannerConfigurer.setProperties(props);
return scannerConfigurer;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);
fb.setTypeAliasesPackage("me.smallyellow.hhy.model");
fb.setMapperLocations(resolver.getResources("classpath:mapper/**/*.xml"));
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
3、设置application.properties
如上代码所示,我们test1-datasource.xx 和 test2-datasource.xx中读取了相关值。故而,我们需要在application.properties设置相关属性值。
test1-datasource.url=jdbc:mysql://localhost:3306/test
test1-datasource.username=root
test1-datasource.password=
test1-datasource.driverClassName=com.mysql.jdbc.Driver
test2-datasource.url=jdbc:mysql://localhost:3306/test2
test2-datasource.username=root
test2-datasource.password=
test2-datasource.driverClassName=com.mysql.jdbc.Driver
以上,成功配置多数据源
解释:MyBatisConfig 中 DynamicDataSource类进行了一个数据源路由的功能,DatabaseType里定义了数据源
public enum DatabaseType {
test1,
test2
}
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
再看DatabaseContextHolder 类,保证了始终只有一个DatabaseType容器
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
public static void setDatabaseType(DatabaseType type) {
contextHolder.set(type);
}
}
至此,已经可以在service中对不同数据源中的数据进行操作了,但如果不是操作默认数据源,需要在操作之前设置数据源
public List<UserInfo> test() {
List<UserInfo> list= userInfoMapper.selectAll();
return list;
}
public List<UserInfo> test2() {
DatabaseContextHolder.setDatabaseType(DatabaseType.test2);
List<UserInfo> list= userInfoMapper.selectAll();
return list;
}
但这样显得有点麻烦,所以,打算通过注解的方式进行配置
1、新建OtherDataSource.java 内容如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OtherDataSource {
public DatabaseType dataType() default DatabaseType.test2;
}
2、我们需要在添加了这个注解的方法执行之前,先自动设置数据源,这里用到了切片的概念。新建DynamicDataSourceAspect.java
@Aspect
@Order(-10)
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(otherDataSource)")
public void changeDataSource(JoinPoint point, OtherDataSource otherDataSource){
DatabaseType dType = otherDataSource.dataType();
DatabaseContextHolder.setDatabaseType(dType);
}
}
添加完这两个类后,我们就可以使用注解的方式切换数据源了
public List<UserInfo> test() {
List<UserInfo> list= userInfoMapper.selectAll();
return list;
}
@OtherDataSource
public List<App> test2(){
List<App> list = appMapper.selectAll();
return list;
}
end