最近学习springboot,简单写了个Spring Boot + Mybatis多数据源和动态数据源配置,可以根据配置名字来指定需要哪个数据源,如果不指定就用默认的数据源.
一.项目引入Mybatis依赖包:
<!-- Spring Boot Mybatis 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
二.配置文件配置数据源:
#主数据源,默认的
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.username=wtadmin
spring.datasource.password=123456
# 更多数据源
custom.datasource.names=ds1,knetqe
custom.datasource.ds1.driver-class-name=oracle.jdbc.driver.OracleDriver
custom.datasource.ds1.url=jdbc:oracle:thin:@localhost:1521:orcl
custom.datasource.ds1.username=wcadmin
custom.datasource.ds1.password=123456
custom.datasource.knetqe.driver-class-name=oracle.jdbc.driver.OracleDriver
custom.datasource.knetqe.url=jdbc:oracle:thin:@localhost:1521:orcl
custom.datasource.knetqe.username=knetqe
custom.datasource.knetqe.password=123456
三.java类
1.DynamicDataSource:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
2.DynamicDataSourceAspect
/**
* 切换数据源Advice
*
* @author lihuaju
* @create 2018年1月7日
*/
@Aspect
@Order(-1)// 保证该AOP在@Transactional之前执行
@Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(tds)")
// @Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSource tds) throws Throwable {
String dsId = tds.name();
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
logger.error("数据源[{}]不存在,使用默认数据源 > {}", tds.name(), point.getSignature());
} else {
logger.debug("Use DataSource : {} > {}", tds.name(), point.getSignature());
DynamicDataSourceContextHolder.setDataSourceType(tds.name());
}
}
@After("@annotation(tds)")
// @Before("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSource tds) {
logger.debug("Revert DataSource : {} > {}", tds.name(), point.getSignature());
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
3.DynamicDataSourceContextHolder:
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static List<String> dataSourceIds = new ArrayList<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
/**
* 判断指定DataSrouce当前是否存在
*
* @param dataSourceId
* @return
* @author
* @create 2018年1月14日
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
4.DynamicDataSourceRegister
/**
* 动态数据源注册<br/>
* 启动动态数据源请在启动类中(如SpringBootApplication)
* 添加 @Import(DynamicDataSourceRegister.class)
*
* @author lihuaju
* @create 2018年1月14日
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
private ConversionService conversionService = new DefaultConversionService();
private PropertyValues dataSourcePropertyValues;
// 如配置文件中未指定数据源类型,使用该默认值
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
// private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
// private static final Object DATASOURCE_TYPE_DEFAULT = "spring.datasource";
// 数据源
private DataSource defaultDataSource;
private Map<String, DataSource> customDataSources = new HashMap<>();
/**
* 加载多数据源配置
*/
@Override
public void setEnvironment(Environment env) {
initDefaultDataSource(env);
initCustomDataSources(env);
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
// 将主数据源添加到更多数据源中
targetDataSources.put("dataSource", defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
// 添加更多数据源
targetDataSources.putAll(customDataSources);
for (String key : customDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
}
// 创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
logger.info("Dynamic DataSource Registry");
}
/**
* 创建DataSource
*
* @param type
* @param driverClassName
* @param url
* @param username
* @param password
* @return
* @author LIHUAJU
* @create 2018年1月7日
*/
@SuppressWarnings("unchecked")
public DataSource buildDataSource(Map<String, Object> dsMap) {
try {
Object type = dsMap.get("type");
if (type == null)
type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
Class<? extends DataSource> dataSourceType;
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dsMap.get("driver-class-name").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString();
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
.username(username).password(password).type(dataSourceType);
return factory.build();
/*********************************修改原来的*************************************/
// String driverClassName = dsMap.get("driver-class-name").toString();
// String url = dsMap.get("url").toString();
// String username = dsMap.get("username").toString();
// String password = dsMap.get("password").toString();
// DriverManagerDataSource dataSource = new DriverManagerDataSource();
// dataSource.setDriverClassName(driverClassName);
// dataSource.setUrl(url);
// dataSource.setUsername(username);
// dataSource.setPassword(password);
// return dataSource;
/**********************************************************************/
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 初始化主数据源
*
* @author LIHUAJU
* @create 2018年1月7日
*/
private void initDefaultDataSource(Environment env) {
// 读取主数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
defaultDataSource = buildDataSource(dsMap);
dataBinder(defaultDataSource, env);
}
/**
* 为DataSource绑定更多数据
*
* @param dataSource
* @param env
* @author LIHUAJU
* @create 2018年1月7日
*/
private void dataBinder(DataSource dataSource, Environment env){
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
//dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
dataBinder.setConversionService(conversionService);
dataBinder.setIgnoreNestedProperties(false);//false
dataBinder.setIgnoreInvalidFields(false);//false
dataBinder.setIgnoreUnknownFields(true);//true
if(dataSourcePropertyValues == null){
Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
Map<String, Object> values = new HashMap<>(rpr);
// 排除已经设置的属性
values.remove("type");
values.remove("driver-class-name");
values.remove("url");
values.remove("username");
values.remove("password");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
}
/**
* 初始化更多数据源
*
* @author LIHUAJU
* @create 2018年1月7日
*/
private void initCustomDataSources(Environment env) {
// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
DataSource ds = buildDataSource(dsMap);
customDataSources.put(dsPrefix, ds);
dataBinder(ds, env);
}
}
}
5.TargetDataSource
/**
* 在方法上使用,用于指定使用哪个数据源
*
* @author lihuaju
* @create 2018年1月7日
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String name();
}
四.serviceimpl代码示例
/**
* 不指定数据源使用默认数据源
*
* @return
* @author lihuaju
* @create 2018年1月14日
*/
public List<Student> getList(){
String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT1";
return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){
@Override
public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
Student stu = new Student();
stu.setId(rs.getInt("ID"));
stu.setAge(rs.getInt("AGE"));
stu.setName(rs.getString("NAME"));
stu.setSumScore(rs.getString("SCORE_SUM"));
stu.setAvgScore(rs.getString("SCORE_AVG"));
return stu;
}
});
}
/**
* 指定数据源使用默认数据源
*
* @return
* @author lihuaju
* @create 2018年1月14日
*/
@TargetDataSource(name="ds1")
@Transactional(propagation=Propagation.REQUIRED,rollbackFor= {Exception.class})
public String insertDataToDs1(){
String sql = "insert into STUDENT (ID,NAME,SCORE_SUM,SCORE_AVG, AGE ) values (3,'wtadmin3',90,40,15)";
jdbcTemplate.update(sql);
String sql1 = "insert into STUDENT (ID,NAME,SCORE_SUM,SCORE_AVG, AGE ) values (4,'wtadmin4',90,40,15)";
jdbcTemplate.update(sql1);
return "success";
}
/**
* 指定数据源
*
* @return
* @author lihuaju
* @create 2018年1月14日
*/
@TargetDataSource(name="knetqe")
@Transactional(propagation=Propagation.REQUIRED,rollbackFor= {Exception.class})
public String insertDataToDefault(){
String sql = "insert into STUDENT (ID,NAME,SCORE_SUM,SCORE_AVG, AGE ) values (1,'knetqe1',91,41,11)";
jdbcTemplate.update(sql);
String sql1 = "insert into STUDENT (ID,NAME,SCORE_SUM,SCORE_AVG, AGE ) values (2,'knetqe2',92,42,12)";
jdbcTemplate.update(sql1);
return "success";
}
这样就很方便的配置使用哪个数据源了,欢迎大家共同学习和交流,不足之处还请大家指出,谢谢.