下面是“使用SpringBoot+Druid双数据源动态配置操作”的完整攻略及两条示例。
一、概述
在实际的项目开发中,经常会遇到同时操作多个不同的数据库的情况,比如读写分离、多租户等。使用SpringBoot+Druid双数据源动态配置操作,可以有效地解决这些问题。
二、配置SpringBoot+Druid
1. 引入相关依赖
在 pom.xml
文件中加入以下依赖:
<!-- SpringBoot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- MySQL驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Druid连接池依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- MyBatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
2. 配置Druid连接池
在 application.yml
文件中配置Druid连接池:
spring:
datasource:
# 主数据源
druid:
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
initial-size: 1
max-active: 20
min-idle: 1
max-wait: 60000
time-between-eviction-runs-millis: 10000
min-evictable-idle-time-millis: 300000
# 辅助数据源
druid2:
url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
initial-size: 1
max-active: 20
min-idle: 1
max-wait: 60000
time-between-eviction-runs-millis: 10000
min-evictable-idle-time-millis: 300000
# Druid配置
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
login-username: druid
login-password: druid
filter:
stat:
log-slow-sql: true
slow-sql-millis: 2000
merge-sql: true
wall:
enabled: true
configs:
# 允许一次执行多条语句
multi-statement-allow: true
# MyBatis配置
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.model
3. 配置Druid监控
可以通过如下配置,开启Druid的监控:
@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// IP白名单设置
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// IP黑名单设置
servletRegistrationBean.addInitParameter("deny", "192.168.0.1");
// 控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "druid");
servletRegistrationBean.addInitParameter("loginPassword", "druid");
// 是否能够重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean statFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// IP白名单设置
filterRegistrationBean.addInitParameter("allow", "127.0.0.1");
// IP黑名单设置,如果allow与deny同时存在时,deny优先于allow
filterRegistrationBean.addInitParameter("deny", "192.168.0.1");
// 监控路径
filterRegistrationBean.addUrlPatterns("/*");
// 排除过滤的请求
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
三、动态配置多数据源
1. 定义数据源枚举类
public enum DataSourceEnum {
ds1("主数据源"),
ds2("辅助数据源");
private String desc;
DataSourceEnum(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
2. 定义动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceKey();
}
}
3. 定义数据源上下文
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void set(String dbType) {
contextHolder.set(dbType);
}
public static String getDataSourceKey() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
4. 配置动态数据源
@Configuration
public class DataSourceConfig {
@Bean(name = "dataSource")
public DynamicDataSource dataSource() {
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceEnum.ds1.name(), ds1());
targetDataSources.put(DataSourceEnum.ds2.name(), ds2());
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(ds1());
return dataSource;
}
@Bean(name = "ds1")
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource ds1() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "ds2")
@ConfigurationProperties(prefix = "spring.datasource.druid2")
public DataSource ds2() {
return DruidDataSourceBuilder.create().build();
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sessionFactory.getObject();
}
}
5. 定义切换数据源的注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSwitch {
String value() default DataSourceEnum.ds1;
}
6. 定义切面切换数据源
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.example.demo.datasource.DataSourceSwitch)")
public void dataSourceSwitch() {
}
@Before("dataSourceSwitch() && @annotation(dataSourceSwitch)")
public void before(JoinPoint point, DataSourceSwitch dataSourceSwitch) {
String dataSourceKey = dataSourceSwitch.value().name();
if (!DynamicDataSourceContextHolder.containsDataSource(dataSourceKey)) {
System.err.println("数据源 [{}] 不存在,使用默认数据源 > {}" + dataSourceSwitch.value() + point.getSignature());
} else {
System.out.println("Use DataSource :{} > {}" + dataSourceSwitch.value() + point.getSignature());
DynamicDataSourceContextHolder.setDataSourceKey(dataSourceKey);
}
}
@After("dataSourceSwitch()")
public void restoreDataSource(JoinPoint point) {
System.out.println("Revert DataSource : {} > {}"+ DynamicDataSourceContextHolder.getDataSourceKey() + point.getSignature());
DynamicDataSourceContextHolder.clearDataSourceKey();
}
}
四、使用示例
1. 业务层接口
public interface UserService {
List<User> list();
@DataSourceSwitch(DataSourceEnum.ds2)
List<User> listByDb2();
}
2. 业务层实现
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> list() {
return userMapper.selectAll();
}
@Override
public List<User> listByDb2() {
return userMapper.selectAll();
}
}
3. 控制层
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public List<User> list() {
return userService.list();
}
@GetMapping("/listByDb2")
public List<User> listByDb2() {
return userService.listByDb2();
}
}
五、总结
通过上面的配置,我们可以轻松地实现动态切换数据源的功能,使用起来非常方便。当然,在实际的项目中,可能会有更为复杂的数据源操作,需要我们根据实际情况进行不同的配置和处理,但是,这里的代码可以提供我们一个很好的起点和思路。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用springboot+druid双数据源动态配置操作 - Python技术站