使用springboot+mybatis-plus+(Druid/Hikari)實現(xiàn)多數(shù)據(jù)源配置
操作步驟:
- 引入相應(yīng)的maven坐標(biāo)
- 編寫mybatis配置,集成mybatis或mybatis-plus(如果已集成可跳過)
- 編寫數(shù)據(jù)源配置類
- 編寫注解,并通過aop進行增強(編寫數(shù)據(jù)源切換代碼)
- 類或方法中使用注解,對數(shù)據(jù)源進行切換
第一步:引入需要相應(yīng)maven坐標(biāo)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- aop注解實現(xiàn)aspectjweaver依賴 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- mysql-plus 依賴 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<!-- Mysql驅(qū)動包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--阿里數(shù)據(jù)庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
第二步:編寫mybatis-plus配置集成
server:
port: 8000
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
master:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
username: root
password: 123456
slaver:
# 從數(shù)據(jù)源開關(guān)/默認關(guān)閉
enabled: false
url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
username: root
password: 123456
druid:
# 初始連接數(shù)
initial-size: 5
# 最小連接池數(shù)量
min-idle: 10
# 最大連接池數(shù)量
max-active: 20
# 獲取連接等待超時時間
max-wait: 60000
mybatis-plus:
mapper-locations: classpath*:mapper/*Mapper.xml
logging:
level:
com.example.demo: debug
以下是集成mp相關(guān)的代碼,不需要可直接跳過?。?!(以user表為例)
userMapper
package com.example.demo.datesource.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.datesource.domain.User;
import java.util.List;
public interface UserService extends IService<User> {
List<User> getUserList();
List<User> getUserListByXml();
List<User> testSlave();
}
userMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.datesource.mapper.UserMapper">
<select id="getUserListByXml" resultType="com.example.demo.datesource.domain.User">
select * from user
</select>
</mapper>
userService
package com.example.demo.datesource.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.datesource.domain.User;
import java.util.List;
public interface UserService extends IService<User> {
List<User> getUserList();
List<User> getUserListByXml();
List<User> testSlave();
}
userServiceImpl
package com.example.demo.datesource.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.datesource.annotation.UseDataSource;
import com.example.demo.datesource.domain.User;
import com.example.demo.datesource.enums.DataSourceType;
import com.example.demo.datesource.mapper.UserMapper;
import com.example.demo.datesource.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> getUserList() {
return list();
}
@Override
public List<User> getUserListByXml(){
return userMapper.getUserListByXml();
}
@Override
public List<User> testSlave() {
return list();
}
}
User
package com.example.demo.datesource.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
private String sex;
private int age;
}
mybatis-plus 集成完畢!
第三步:編寫數(shù)據(jù)源的配置類(例:Druid、Hikari)
一、@ConfigurationProperties要和配置文件中的配置對應(yīng)上
(1)Druid:
package com.example.demo.datesource.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.example.demo.datesource.config.properties.DruidPropertiesConfig;
import com.example.demo.datesource.datasource.DynamicDataSource;
import com.example.demo.datesource.enums.DataSourceType;
import com.example.demo.datesource.utils.SpringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import javax.swing.*;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource(DruidPropertiesConfig propertiesConfig){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return propertiesConfig.setDataSourceProperties(dataSource);
}
@Bean
@ConfigurationProperties("spring.datasource.slaver")
@ConditionalOnProperty(prefix = "spring.datasource.slaver", name = "enabled", havingValue = "true")
public DataSource slaverDataSource(DruidPropertiesConfig propertiesConfig){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return propertiesConfig.setDataSourceProperties(dataSource);
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource){
Map<Object,Object> targetDataSource = new HashMap<>();
targetDataSource.put(DataSourceType.MASTER.name(),masterDataSource);
setDataSource(targetDataSource,DataSourceType.SLAVE.name(),"slaverDataSource");
return new DynamicDataSource(masterDataSource,targetDataSource);
}
public void setDataSource(Map<Object,Object> targetDataSource,String sourceName,String beanName){
try {
DataSource dataSource = SpringUtils.getBean(beanName);
targetDataSource.put(sourceName,dataSource);
}catch (Exception e){
}
}
}
增加DruidPropertiesConfig類,用于給數(shù)據(jù)源配置上配置文件對應(yīng)的屬性
package com.example.demo.datesource.config.properties;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DruidPropertiesConfig {
@Value("${spring.datasource.druid.initial-size}")
private int initialSize;
@Value("${spring.datasource.druid.min-idle}")
private int minIdle;
@Value("${spring.datasource.druid.max-active}")
private int maxActive;
@Value("${spring.datasource.druid.max-wait}")
private int maxWait;
public DruidDataSource setDataSourceProperties(DruidDataSource dataSource){
dataSource.setInitialSize(this.initialSize);
dataSource.setMinIdle(this.minIdle);
dataSource.setMaxActive(this.maxActive);
dataSource.setMaxWait(this.maxWait);
return dataSource;
}
}
(2)Hikari:配置上基本和druid相同,只需把配置類改為如下且配置文件的“url”改為“jdbc-url”即可
二、增加SpringUtils類,用于讀取spring中的bean
package com.example.demo.datesource.utils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class SpringUtils implements BeanFactoryPostProcessor {
private static ConfigurableListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
SpringUtils.beanFactory = configurableListableBeanFactory;
}
public static <T> T getBean(String name){
return (T) beanFactory.getBean(name);
}
}
三、添加DynamicDataSource類
(1)此類是用來配置動態(tài)數(shù)據(jù)源的關(guān)鍵。該類繼承spring的抽象類AbstractRoutingDataSource,通過這個類可以實現(xiàn)動態(tài)數(shù)據(jù)源切換。其中維護著默認數(shù)據(jù)源(defaultTargetDataSource)和數(shù)據(jù)源列表(targetDataSources),通過afterPropertiesSet()方法對數(shù)據(jù)源列表進行解析以及設(shè)置數(shù)據(jù)源。
程序每次對數(shù)據(jù)庫發(fā)起連接時,都會訪問到AbstractRoutingDataSource的getConnection()方法,此方法會調(diào)用determineCurrentLookupKey的相應(yīng)實現(xiàn),此處實現(xiàn)為獲取線程變量。
package com.example.demo.datesource.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
(2)添加DynamicDataSourceContextHolder類,此類用于切換數(shù)據(jù)源,根據(jù)ThreadLocal做多線程數(shù)據(jù)隔離,每一次切換都能保證不影響其他線程的正常運行
package com.example.demo.datesource.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DynamicDataSourceContextHolder {
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 設(shè)置數(shù)據(jù)源的變量
*/
public static void setDataSourceType(String dsType){
log.info("切換到{}數(shù)據(jù)源",dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 獲得數(shù)據(jù)源的變量
*/
public static String getDataSourceType()
{
return CONTEXT_HOLDER.get();
}
/**
* 清空數(shù)據(jù)源變量,此方法可調(diào)動gc對線程進行清除
*/
public static void clearDataSourceType()
{
CONTEXT_HOLDER.remove();
}
}
AbstractRoutingDataSource切換線程變量來切換數(shù)據(jù)源源碼。
此處就是獲取之前在DynamicDataSourceContextHolder中set到線程中的數(shù)據(jù)源名,通過數(shù)據(jù)源名獲取維護的數(shù)據(jù)源列表中對應(yīng)的數(shù)據(jù)源
第四步:使用
大致思路:定義一個切換數(shù)據(jù)源注解,通過注解aop的形式對數(shù)據(jù)源進行切換,在類或方法中使用注解,設(shè)置對應(yīng)的數(shù)據(jù)源名稱以此來達到數(shù)據(jù)源切換
一、定義一個切換數(shù)據(jù)源注解
package com.example.demo.datesource.annotation;
import com.example.demo.datesource.enums.DataSourceType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UseDataSource {
public DataSourceType dataSource() default DataSourceType.MASTER;
}
二、編寫aop代碼
@within:此注解可以作用于類中的所有方法,如果@UseDataSource放到類上,此類下的所有的方法都會被當(dāng)做切點攔截
@Order(1):提升組件注冊到spring中的優(yōu)先級
point.proceed():原來執(zhí)行的程序,也就是原請求,想要做環(huán)繞增強就再這周圍寫就可以了
package com.example.demo.datesource.aspectj;
import com.example.demo.datesource.annotation.UseDataSource;
import com.example.demo.datesource.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.Objects;
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.example.demo.datesource.annotation.UseDataSource) || @within(com.example.demo.datesource.annotation.UseDataSource)")
public void pointCut(){}
@Around("pointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
UseDataSource useDataSource = getDataSource(point);
if(Objects.nonNull(useDataSource)){
DynamicDataSourceContextHolder.setDataSourceType(useDataSource.dataSource().name());
}
try {
return point.proceed();
}finally {
//清空threadlocalMap的entry。避免內(nèi)存泄漏
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
public UseDataSource getDataSource(ProceedingJoinPoint point){
MethodSignature signature = (MethodSignature) point.getSignature();
UseDataSource annotation = AnnotationUtils.findAnnotation(signature.getMethod(), UseDataSource.class);
if(Objects.nonNull(annotation)){
return annotation;
}
return AnnotationUtils.findAnnotation(signature.getDeclaringType(),UseDataSource.class);
}
}
三、編寫注解到類或方法上
運行結(jié)果:
至此,數(shù)據(jù)源切換成功?。?!
另外,如果需要增加新的數(shù)據(jù)源需要的操作:
1、啟動配置文件中增加新的數(shù)據(jù)源參數(shù)
2、數(shù)據(jù)源配置類中,新增配置源bean,并且把數(shù)據(jù)源set到targetDataSource中
3、DataSourceType枚舉類中,添加新數(shù)據(jù)源的枚舉
如圖所示:文章來源:http://www.zghlxwxcb.cn/news/detail-635538.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-635538.html
到了這里,關(guān)于springboot實現(xiàn)多數(shù)據(jù)源配置(Druid/Hikari)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!