动态数据源
在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库。又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决。接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理。
实现案例
本教程案例基于 Spring Boot + Mybatis + MySQL 实现。
数据库设计
首先需要安装好MySQL数据库,新建数据库 example,创建example表,用来测试数据源,SQL脚本如下:
CREATE TABLE `example` ( `pk` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `message` varchar(100) NOT NULL, `create_time` datetime NOT NULL COMMENT '创建时间', `modify_time` datetime DEFAULT NULL COMMENT '生效时间', PRIMARY KEY (`pk`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='测试用例表'
添加依赖
添加Spring Boot,Spring Aop,Mybatis,MySQL相关依赖。
pom.xml
org.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-testtest org.mybatis.spring.boot mybatis-spring-boot-starter1.3.1 org.springframework.boot spring-boot-starter-aopmysql mysql-connector-java5.1.8
自定义配置文件
新建自定义配置文件resource/config/mysql/db.properties,添加数据源:
#数据库设置 spring.datasource.example.jdbc-url=jdbc:mysql://localhost:3306/example?characterEncoding=UTF-8 spring.datasource.example.username=root spring.datasource.example.password=123456 spring.datasource.example.driver-class-name=com.mysql.jdbc.Driver
启动类
启动类添加 exclude = {DataSourceAutoConfiguration.class}, 以禁用数据源默认自动配置。
数据源默认自动配置会读取 spring.datasource.* 的属性创建数据源,所以要禁用以进行定制。
DynamicDatasourceApplication.java:
package com.main.example.dynamic.datasource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
public class DynamicDatasourceApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicDatasourceApplication.class, args);
}
}
数据源配置类
创建一个数据源配置类,主要做以下几件事情:
1. 配置 dao,model(bean),xml mapper文件的扫描路径。
2. 注入数据源配置属性,创建数据源。
3. 创建一个动态数据源,装入数据源。
4. 将动态数据源设置到SQL会话工厂和事务管理器。
如此,当进行数据库操作时,就会通过我们创建的动态数据源去获取要操作的数据源了。
DbSourceConfig.java:
package com.main.example.config.dao;
import com.main.example.common.DataEnum;
import com.main.example.common.DynamicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
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.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
//数据库配置统一在config/mysql/db.properties中
@Configuration
@PropertySource(value = "classpath:config/mysql/db.properties")
public class DbSourceConfig {
private String typeAliasesPackage = "com.main.example.bean.**.*";
@Bean(name = "exampleDataSource")
@ConfigurationProperties(prefix = "spring.datasource.example")
public DataSource exampleDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//配置多数据源
Map
创建一个AOP切面,拦截带 @DataSource 注解的方法,在方法执行前切换至目标数据源,执行完成后恢复到默认数据源。
DynamicDataSourceAspect.java:
package com.main.example.config.dao;
import com.main.example.common.DbSourceContext;
import org.apache.log4j.Logger;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Order(-1) // 该切面应当先于 @Transactional 执行
@Component
public class DynamicDataSourceAspect {
private static Logger logger = Logger.getLogger(DynamicDataSourceAspect.class);
//@Before("@annotation(dbSource)") 注解在对应方法,拦截有@DbSource的方法
//注解在类对象,拦截有@DbSource类下所有的方法
@Before("@within(dbSource)")
public void switchDataSource(JoinPoint point, DbSource dbSource) {
// 切换数据源
DbSourceContext.setDbSource(dbSource.value());
}
//注解在类对象,拦截有@DbSource类下所有的方法
@After("@within(dbSource)")
public void restoreDataSource(JoinPoint point, DbSource dbSource) {
// 将数据源置为默认数据源
DbSourceContext.clearDbSource();
}
}
到这里,动态数据源相关的处理代码就完成了。
编写用户业务代码
接下来编写用户查询业务代码,用来进行测试,Dao层只需添加一个查询接口即可。
ExampleDao.java:
package com.main.example.dao;
import com.main.example.common.DataEnum;
import com.main.example.config.dao.DbSource;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Component("exampleDao")
//切换数据源注解,以DataEnum.DbSource中的值为准
@DbSource("example")
public class ExampleDao extends Daobase {
private static final String MAPPER_NAME_SPACE = "com.main.example.dao.ExampleMapper";
public List selectAllMessages() {
return selectList(MAPPER_NAME_SPACE, "selectAllMessages");
}
}
Controler代码:
TestExampleDao.java:
package com.main.example.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class TestExampleDao {
@Autowired
ExampleDao exampleDao;
@RequestMapping(value = "/test/example")
public List selectAllMessages() {
try {
List ldata = exampleDao.selectAllMessages();
if(ldata == null){System.out.println("*********it is null.***********");return null;}
for(String d : ldata) {
System.out.println(d);
}
return ldata;
}catch(Exception e) {
e.printStackTrace();
}
return new ArrayList<>();
}
}
ExampleMapper.xml代码:
测试效果
启动系统,访问 http://localhost:80/test/example">http://localhost:80/test/example,分别测试两个接口,成功返回数据。
可能遇到的问题
1.报错:java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName
原因:
spring boot从1.X升级到2.X版本之后,一些配置及用法有了变化,如果不小心就会碰到“jdbcUrl is required with driverClassName.”的错误
解决方法:
在1.0 配置数据源的过程中主要是写成:spring.datasource.url 和spring.datasource.driverClassName。
而在2.0升级之后需要变更成:spring.datasource.jdbc-url和spring.datasource.driver-class-name即可解决!
2.自定义配置文件
自定义配置文件需要在指定配置类上加上@PropertySource标签,例如:
@PropertySource(value = "classpath:config/mysql/db.properties")
若是作用于配置类中的方法,则在方法上加上@ConfigurationProperties,例如:
@ConfigurationProperties(prefix = "spring.datasource.example")
配置项前缀为spring.datasource.example
若是作用于配置类上,则在类上加上@ConfigurationProperties(同上),并且在启动类上加上@EnableConfigurationProperties(XXX.class)
3.多数据源
需要在启动类上取消自动装载数据源,如:
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
以上所述是小编给大家介绍的Spring Boot + Mybatis 实现动态数据源详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对考高分网网站的支持!



