参考网址:https://www.cnblogs.com/java-zhao/p/5413845.html
1.配置文件application.properties中增加其他数据源设置
#the first datasource
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:
mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc.username = root
jdbc.password = 123
#the second datasource
jdbc2.driverClassName = com.mysql.jdbc.Driver
jdbc2.url = jdbc:
mysql://xxx:3306/mytestdb2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
jdbc2.username = root
jdbc2.password = 123
2.建立databaseType枚举类,列举数据源的key
package com.xxx.firstboot.common.datasource;
/**
* 列出所有的数据源key(常用数据库名称来命名)
* 注意:
* 1)这里数据源与数据库是一对一的
* 2)DatabaseType中的变量名称就是数据库的名称
*/
public enum DatabaseType {
mytestdb,mytestdb2
}
3.DatabaseContextHolder构建DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
package com.xxx.firstboot.common.datasource;
/**
* 作用:
* 1、保存一个线程安全的DatabaseType容器
*/
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static void setDatabaseType(DatabaseType type){
contextHolder.set(type);
}
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
}
4.建立DynamicDataSource类,使用DatabaseContextHolder获取当前线程的DatabaseType
package com.xxx.firstboot.common.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源(需要继承AbstractRoutingDataSource)
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
5.配置MyBatisConfig,通过application.properties文件生成两个数据源,使用这两个数据源构造动态的dataSource
@Primary:指定在同一个接口有多个实现类可以注入的时候,默认选择哪一个
@Qualifier:指定名称的注入,当一个接口有多个实现类时使用
@Bean:生成的bean示例的名称
package com.xxx.firstboot.common;
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.context.annotation.Bean;
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 com.alibaba.druid.pool.DruidDataSourceFactory;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.common.datasource.DynamicDataSource;
/**
* springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
* 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
*/
@Configuration // 该注解类似于spring配置文件
@MapperScan(basePackages = "com.xxx.firstboot.mapper")
public class MyBatisConfig {
@Autowired
private Environment env;
/**
* 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
*/
@Bean
public DataSource myTestDbDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
props.put("url", env.getProperty("jdbc.url"));
props.put("username", env.getProperty("jdbc.username"));
props.put("password", env.getProperty("jdbc.password"));
return DruidDataSourceFactory.createDataSource(props);
}
@Bean
public DataSource myTestDb2DataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
props.put("url", env.getProperty("jdbc2.url"));
props.put("username", env.getProperty("jdbc2.username"));
props.put("password", env.getProperty("jdbc2.password"));
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,
@Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默认的datasource设置为myTestDbDataSource
return dataSource;
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
fb.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
6.使用实例
package com.xxx.firstboot.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.service.ShopService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/shop")
@Api("shopController相关api")
public class ShopController {
@Autowired
private ShopService service;
@ApiOperation("获取shop信息,测试多数据源")
@RequestMapping(value = "/getShop", method = RequestMethod.GET)
public Shop getShop(@RequestParam("id") int id) {
return service.getShop(id);
}
}
package com.xxx.firstboot.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xxx.firstboot.dao.ShopDao;
import com.xxx.firstboot.domain.Shop;
@Service
public class ShopService {
@Autowired
private ShopDao dao;
public Shop getShop(int id) {
return dao.getShop(id);
}
}
package com.xxx.firstboot.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.xxx.firstboot.common.datasource.DatabaseContextHolder;
import com.xxx.firstboot.common.datasource.DatabaseType;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.mapper.ShopMapper;
@Repository
public class ShopDao {
@Autowired
private ShopMapper mapper;
/**
* 获取shop
*/
public Shop getShop(int id) {
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
return mapper.getShop(id);
}
}
package com.xxx.firstboot.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import com.xxx.firstboot.domain.Shop;
public interface ShopMapper {
@Select("SELECT * FROM t_shop WHERE id = #{id}")
@Results(value = { @Result(id = true, column = "id", property = "id"),
@Result(column = "shop_name", property = "shopName") })
public Shop getShop(@Param("id") int id);
}
7.通过AOP实现数据源切换
package com.xxx.firstboot.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.xxx.firstboot.domain.Shop;
import com.xxx.firstboot.mapper.ShopMapper;
@Repository
public class ShopDao {
@Autowired
private ShopMapper mapper;
/**
* 获取shop
*/
public Shop getShop(int id) {
return mapper.getShop(id);
}
}
package com.xxx.firstboot.common.datasource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.xxx.firstboot.dao.ShopDao;
@Aspect
@Component
public class DataSourceAspect {
@Before("execution(* com.xxx.firstboot.dao.*.*(..))")
public void setDataSourceKey(JoinPoint point){
//连接点所属的类实例是ShopDao
if(point.getTarget() instanceof ShopDao){
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
}else{//连接点所属的类实例是UserDao(当然,这一步也可以不写,因为defaultTargertDataSource就是该类所用的mytestdb)
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb);
}
}
// @Around("execution(* com.xxx.firstboot.dao.*.*(..))")
// public Object setDataSourceKeyByAround(ProceedingJoinPoint point) throws Throwable{
// if(point.getTarget() instanceof ShopDao){
// DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
// }else{//连接点所属的类实例是UserDao(当然,这一步也可以不写,因为defaultTargertDataSource就是该类所用的mytestdb)
// DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb);
// }
// return point.proceed();//执行目标方法
// }
}
或者
package com.xxx.firstboot.common.datasource;
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 com.xxx.firstboot.dao.ShopDao;
@Aspect
@Component
public class DataSourceAspect {
/**
* 使用空方法定义切点表达式
*/
@Pointcut("execution(* com.xxx.firstboot.dao.*.*(..))")
public void declareJointPointExpression() {
}
/**
* 使用定义切点表达式的方法进行切点表达式的引入
*/
@Before("declareJointPointExpression()")
public void setDataSourceKey(JoinPoint point) {
// 连接点所属的类实例是ShopDao
if (point.getTarget() instanceof ShopDao) {
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
} else {// 连接点所属的类实例是UserDao(当然,这一步也可以不写,因为defaultTargertDataSource就是该类所用的mytestdb)
DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb);
}
}
}