日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

druid 多数据源_Spring Boot + Mybatis 中 配置Druid多数据源并实现自由切换

發布時間:2024/4/19 javascript 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 druid 多数据源_Spring Boot + Mybatis 中 配置Druid多数据源并实现自由切换 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

概述

前面我們已經介紹過了對MyBatis、Druid的整合,接下來我們在之前的基礎上做擴展,實現對Druid多數據源的配置以及動態切換數據源。

問題:多數據源使用場景有哪些呢?
回答:在業務發展中,數據的不斷增長,會有讀寫分離的需求,以及按業務模塊分庫的需求,這樣我們的數據源會越來越多,在項目中就有了在各個數據源之間來回切換的場景。

實踐如何配置Druid多數據源并實現動態切換

1. 首先是啟動類的改造,在@SpringBootApplication注解后加上exclude = { DataSourceAutoConfiguration.class }

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })

2. yml配置文件,druid配置中,加入兩個數據庫,分別命名為master、slave

# MyBatis配置
mybatis:
# 搜索指定包別名
typeAliasesPackage: com.zhlab.demo.model
# 配置mapper的掃描,找到所有的mapper.xml映射文件
mapperLocations: classpath:mapper/*Mapper.xml
# 加載全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml


spring:
## 數據庫配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
master:
url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
slave:
enabled: true
url: jdbc:mysql://localhost:3306/demo2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
//...后邊省略和之前一樣

3. 創建com.zhlab.demo.db包,并創建DataSourceType.java枚舉類,存放所有數據源的名稱

package com.zhlab.demo.db;

/**
* @ClassName DataSourceType
* @Description //DataSourceType 數據源類型
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/11/2 0002 下午 2:41
**/
public enum DataSourceType
{
/**
* master
*/
MASTER,

/**
* slave
*/
SLAVE
}

4. 創建com.zhlab.demo.config.properties包,并創建DruidProperties.java類,用來獲取連接池屬性,并設置到數據源中

package com.zhlab.demo.config.properties;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
* @ClassName DruidProperties
* @Description //DruidProperties
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/11/2 0002 下午 2:44
**/
@Configuration
public class DruidProperties {
@Value("${spring.datasource.druid.initialSize}")
private int initialSize;

@Value("${spring.datasource.druid.minIdle}")
private int minIdle;

@Value("${spring.datasource.druid.maxActive}")
private int maxActive;

@Value("${spring.datasource.druid.maxWait}")
private int maxWait;

@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;

@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;

@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
private int maxEvictableIdleTimeMillis;

@Value("${spring.datasource.druid.validationQuery}")
private String validationQuery;

@Value("${spring.datasource.druid.testWhileIdle}")
private boolean testWhileIdle;

@Value("${spring.datasource.druid.testOnBorrow}")
private boolean testOnBorrow;

@Value("${spring.datasource.druid.testOnReturn}")
private boolean testOnReturn;

/**
* 連接池屬性設置
* */
public DruidDataSource dataSource(DruidDataSource datasource)
{
datasource.setInitialSize(initialSize);
datasource.setMaxActive(maxActive);
datasource.setMinIdle(minIdle);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}

5. 接著配置MybatisConfig.java和DruidConfig.java兩個配置類
MybatisConfig

package com.zhlab.demo.config;

import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
* @ClassName MybatisConfig
* @Description //MybatisConfig配置類
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/10/31 0031 上午 9:37
**/
@Configuration
@MapperScan("com.zhlab.demo.mapper") //mapper
public class MybatisConfig {

@Autowired
private Environment env;

@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

String mapperLocations = env.getProperty("mybatis.mapperLocations");
String configLocation = env.getProperty("mybatis.configLocation");
VFS.addImplClass(SpringBootVFS.class);
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage("com.zhlab.demo.model");
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
return sessionFactory.getObject();
}

}

DruidConfig

package com.zhlab.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.zhlab.demo.config.properties.DruidProperties;
import com.zhlab.demo.db.DataSourceType;
import com.zhlab.demo.db.datasource.DynamicDataSource;
import com.zhlab.demo.utils.SpringUtil;
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 java.util.HashMap;
import java.util.Map;

/**
* @ClassName DruidConfig
* @Description //DruidConfig配置類
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/11/2 0002 下午 2:08
**/
@Configuration
public class DruidConfig {

/**
* master數據源
* */
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}

/**
* slave數據源
* */
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}

@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource) {

Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);

//設置備用
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}

/**
* 設置數據源
*
* @param targetDataSources 備選數據源集合
* @param sourceName 數據源名稱
* @param beanName bean名稱
*/
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
try {
DataSource dataSource = SpringUtil.getBean(beanName);
targetDataSources.put(sourceName, dataSource);
}
catch (Exception e) {
e.printStackTrace();
}
}
}

6. 配置信息完成后,需要加入動態數據源支持,創建com.zhlab.demo.db.datasource包,并創建
DynamicDataSource類,繼承AbstractRoutingDataSource,這個抽象類有兩個成員變量需要我們了解一下

  • targetDataSources:保存了key和數據庫連接的映射關系

  • defaultTargetDataSource:表示默認的數據庫連接

package com.zhlab.demo.db.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;

/**
* @ClassName DynamicDataSource
* @Description //DynamicDataSource
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/11/2 0002 下午 2:22
**/
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 DynamicDataSourceHelper.getDataSourceType();
}
}

上述類中,重寫了determineCurrentLookupKey()函數,我們看一下它在抽象類中是如何被使用的

protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = this.determineCurrentLookupKey();
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}

if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}

所以,我們需要在determineCurrentLookupKey()方法中返回一個數據源的標志即可,即

@Override
protected Object determineCurrentLookupKey()
{
return DynamicDataSourceHelper.getDataSourceType();
}

7. 我們還需要創建一個自定義的DynamicDataSourceHelper類,來操作數據源的設置、獲取和清除

package com.zhlab.demo.db.datasource;


/**
* @ClassName DynamicDataSource
* @Description //數據源切換處理
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/11/2 0002 下午 2:22
**/
public class DynamicDataSourceHelper
{

private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

/**
* 設置數據源的變量
*/
public static void setDataSourceType(String dsType) { CONTEXT_HOLDER.set(dsType); }

/**
* 獲得數據源的變量
*/
public static String getDataSourceType() { return CONTEXT_HOLDER.get(); }

/**
* 清空數據源變量
*/
public static void clearDataSourceType() {CONTEXT_HOLDER.remove();}
}

8. 實現動態切換,通過注解來簡化業務層的數據源切換,創建com.zhlab.demo.db.annotation包,并創建注解DataSource.java

package com.zhlab.demo.db.annotation;

import com.zhlab.demo.db.DataSourceType;

import java.lang.annotation.*;

/**
* 自定義多數據源切換注解
* 在這里切換數據源名稱
* */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited
public @interface DataSource
{
DataSourceType value() default DataSourceType.MASTER;
}

9. 使用AOP,切入DataSource注解,實現數據源切換,創建com.zhlab.demo.db.aspect包,并創建DynamicDataSourceAspect.java

package com.zhlab.demo.db.aspect;

import com.zhlab.demo.db.annotation.DataSource;

import com.zhlab.demo.db.datasource.DynamicDataSourceHelper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
* @ClassName DynamicDataSourceAspect
* @Description //數據源動態切換AOP
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/11/2 0002 下午 3:16
**/
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {

/**
* 選擇切入點為DataSource注解
* */
@Pointcut("@annotation(com.zhlab.demo.db.annotation.DataSource)"
+ "|| @within(com.zhlab.demo.db.annotation.DataSource)")
public void dsPointCut() { }

@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
DataSource dataSource = getDataSource(point);
if (dataSource != null) {
DynamicDataSourceHelper.setDataSourceType(dataSource.value().name());
}

try {
return point.proceed();
}
finally {
// 銷毀數據源 在執行方法之后
DynamicDataSourceHelper.clearDataSourceType();
}
}

/**
* 獲取需要切換的數據源
*/
public DataSource getDataSource(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
Class<? extends Object> targetClass = point.getTarget().getClass();
DataSource targetDataSource = targetClass.getAnnotation(DataSource.class);
if (targetDataSource != null) {
return targetDataSource;
} else {
Method method = signature.getMethod();
DataSource dataSource = method.getAnnotation(DataSource.class);
return dataSource;
}
}
}

10. 完成以上的步驟后,可以在DAO層、Service層的方法中切換數據源了

package com.zhlab.demo.mapper;

import com.zhlab.demo.db.DataSourceType;
import com.zhlab.demo.db.annotation.DataSource;
import com.zhlab.demo.model.SysAdminUser;
import java.util.List;

public interface SysAdminUserMapper {
int insert(SysAdminUser record);

/**
* 查詢所有用戶
* */
List<SysAdminUser> selectAll();

//切換數據源后,查詢所有用戶
@DataSource(value = DataSourceType.SLAVE)
List<SysAdminUser> selectAll2();
}

SysAdminUserService.java

package com.zhlab.demo.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhlab.demo.db.DataSourceType;
import com.zhlab.demo.db.annotation.DataSource;
import com.zhlab.demo.mapper.SysAdminUserMapper;
import com.zhlab.demo.model.SysAdminUser;
import com.zhlab.demo.utils.PageUtil;
import com.zhlab.demo.utils.page.PageRequest;
import com.zhlab.demo.utils.page.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* @ClassName SysAdminUserService
* @Description //SysAdminUserService
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/10/31 0031 上午 9:45
**/
@Service
public class SysAdminUserService {

@Autowired
SysAdminUserMapper sysAdminUserMapper;

/**
* 查詢所有用戶
* */
public List<SysAdminUser> findAll(){
return sysAdminUserMapper.selectAll();
}

public List<SysAdminUser> findAll2(){
return sysAdminUserMapper.selectAll2();
}
}

UserController.java

package com.zhlab.demo.controller;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhlab.demo.model.SysAdminUser;
import com.zhlab.demo.service.SysAdminUserService;
import com.zhlab.demo.utils.PageUtil;
import com.zhlab.demo.utils.page.PageRequest;
import com.zhlab.demo.utils.page.PageResult;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
* @ClassName UserController
* @Description //用戶接口層
* @Author singleZhang
* @Email 405780096@qq.com
* @Date 2020/10/31 0031 上午 9:43
**/
@RestController
@RequestMapping("/user")
public class UserController {

@Autowired
SysAdminUserService sysAdminUserService;

/* 方法注解 */
@ApiOperation(value = "方法名:用戶列表", notes = "獲取用戶列表")
@GetMapping("/list")
public List<SysAdminUser> list(){
List<SysAdminUser> list = sysAdminUserService.findAll();

return list;
}

/* 方法注解 */
@ApiOperation(value = "方法名:用戶列表2", notes = "切換數據源獲取用戶列表")
@GetMapping("/list2")
public List<SysAdminUser> list2(){
List<SysAdminUser> list = sysAdminUserService.findAll2();

return list;
}

}

demo2數據庫中的數據需要和demo數據庫中的數據不同,形成對比

demo2 數據表內容:

demo 數據表內容:

10. 啟動項目,打開http://localhost:8080/swagger-ui.html查看接口


demo數據庫,查詢所有用戶:/user/list


demo2數據庫,查詢所有用戶:/user/list2

實現成功,完成自定義切換數據源。

總結

Druid多數據源以及動態切換的使用場景其實在很多項目中是很常見的,需要大家掌握,以后接觸到分布式系統的時候在這基礎上會擴展得更多,需要持續深入研究。

項目地址

https://gitee.com/kaixinshow/springboot-note

總結

以上是生活随笔為你收集整理的druid 多数据源_Spring Boot + Mybatis 中 配置Druid多数据源并实现自由切换的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。