赞
踩
此项目也是借助网上各种双数据源动态切换改编的(参考人人代码开源),暂没考虑数据库事务。
1、首先引入各种jar,springboot、mybatisplus等,配置pom文件如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.hualife</groupId>
- <artifactId>springboot</artifactId>
- <version>1.0-SNAPSHOT</version>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.7.5</version>
- </parent>
- <properties>
- <java.version>1.8</java.version>
- <mybatis.spring.boot.version>2.2.2</mybatis.spring.boot.version>
- <pagehelper.spring.boot.version>1.4.5</pagehelper.spring.boot.version>
- <druid.version>1.2.22</druid.version>
- <commons.io.version>2.11.0</commons.io.version>
- <commons.configuration.version>1.10</commons.configuration.version>
- <mysql.version>8.0.30</mysql.version>
- <druid.version>1.2.13</druid.version>
- <mybatisplus.version>3.5.2</mybatisplus.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>com.github.pagehelper</groupId>
- <artifactId>pagehelper-spring-boot-starter</artifactId>
- <version>${pagehelper.spring.boot.version}</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid</artifactId>
- <version>${druid.version}</version>
- </dependency>
- <!-- mysql驱动 -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>${mysql.version}</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid-spring-boot-starter</artifactId>
- <version>1.2.4</version>
- <exclusions>
- <exclusion>
- <artifactId>druid</artifactId>
- <groupId>com.alibaba</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>${mybatisplus.version}</version>
- <exclusions>
- <exclusion>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-generator</artifactId>
- </exclusion>
- <exclusion>
- <artifactId>jsqlparser</artifactId>
- <groupId>com.github.jsqlparser</groupId>
- </exclusion>
- <exclusion>
- <artifactId>mybatis</artifactId>
- <groupId>org.mybatis</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.16.22</version>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <version>2.7.5</version>
- </plugin>
- </plugins>
- </build>
- </project>

2、在yml中配置数据源信息如下(可多配置一些数据库连接池信息,自行百度吧有很多可以借鉴):
- server:
- port: 8080
- spring:
- datasource:
- dynamic:
- datasource:
- master:
- #MySQL配置
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/DB1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
- username: root
- password:
- initial-size: 2
- max-active: 20
- min-idle: 1
- slave:
- #MySQL配置
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/DB2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
- username: root
- password:
- initial-size: 2
- max-active: 20
- min-idle: 1
- mybatis-plus:
- configuration:
- log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- mapper-locations: classpath:mapper/**/*.xml

3、建数据库,建表(不会的百度吧),并且生成对应的Java实体及mapper及xml等(可使用开源版人人代码生成器)。
4、搭建springboot项目,项目结构如下图(除了config以外,其他的包应该都知道是啥):

5、在config目录下创建Java类,CurrDataSource.java(注解类,可用于项目中区分数据源),DataSourceAspect.java(切面类,根据切面选择不同的数据源),DataSourceFactory.java(数据源工厂,包含数据库连接池信息),DataSourceProperties.java(数据源信息封装的实体类),DBTypeName.java(数据源名称常量),DynamicContextHolder.java(数据源上下文切换用的,实际是队列),
DynamicDataSourceProperties.java(双数据源信息对应的map,具体看下文中的代码吧),MyDynamicDataSourceConfig.java(动态数据源配置类),MyRoutingDataSource.java(数据源路由类)
6、数据源切换的注解:
- import java.lang.annotation.*;
- /**
- * 多数据源注解
- * 自己项目中添加
- */
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- public @interface CurrDataSource {
- String value() default "master";
- }
7、aop切面类:
- import lombok.extern.slf4j.Slf4j;
- import org.aspectj.lang.ProceedingJoinPoint;
- 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.Ordered;
- import org.springframework.core.annotation.AnnotationUtils;
- import org.springframework.core.annotation.Order;
- import org.springframework.stereotype.Component;
- import java.lang.reflect.Method;
- /**
- * 多数据源,切面处理类
- */
- @Aspect
- @Component
- @Order(Ordered.HIGHEST_PRECEDENCE)
- @Slf4j
- public class DataSourceAspect {
-
- @Pointcut("@annotation(com.hualife.modules.config.CurrDataSource) " //方法包含注解
- +"|| @within(com.hualife.modules.config.CurrDataSource)" //类包含注解
- +"|| within(com.baomidou.mybatisplus.core.mapper.BaseMapper+)")
- // 切BaseMapper及其子类,因为在mapper加注解但调用父类的方法切不到,故加了within,这样就可以切到父类了
- //其实只要within表达式就行,因为不管是service的impl还是mapper最终调的都是mapper的方法
- public void dSPointCut() {
- }
-
- @Around("dSPointCut()")
- public Object around(ProceedingJoinPoint point) throws Throwable {
- MethodSignature signature = (MethodSignature) point.getSignature();
- Class targetClass = point.getTarget().getClass();
- Method method = signature.getMethod();
- log.info("执行数据库操作的类是:{},函数是:{}", targetClass.getName(), method.getName());
- // CurrDataSource targetDataSource = (CurrDataSource) targetClass.getAnnotation(CurrDataSource.class);
- CurrDataSource targetDataSource = AnnotationUtils.findAnnotation(targetClass, CurrDataSource.class);//获取目标类上注解
- CurrDataSource methodDataSource = method.getAnnotation(CurrDataSource.class);//获取目标函数上注解
- if (targetDataSource != null || methodDataSource != null) {
- log.info(targetDataSource + "===========" + methodDataSource);
- String value;
- if (methodDataSource != null) {
- value = methodDataSource.value();
- } else {
- value = targetDataSource.value();
- }
- DynamicContextHolder.push(value);
- } else {
- log.info("执行数据库操作的类及其函数上没有@CurrDataSource,而这个执行数据库操作的函数属于BaseMapper及其子类下的函数,故调用master");
- DynamicContextHolder.push(DBTypeName.MASTER);
- }
- try {
- return point.proceed();
- } finally {
- DynamicContextHolder.poll();
- log.info("clean datasource");
- }
- }
- }

8、数据源工厂:
- import com.alibaba.druid.pool.DruidDataSource;
- import java.sql.SQLException;
- /**
- * DruidDataSource
- */
- public class DataSourceFactory {
- public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) {
- DruidDataSource druidDataSource = new DruidDataSource();
- druidDataSource.setDriverClassName(properties.getDriverClassName());
- druidDataSource.setUrl(properties.getUrl());
- druidDataSource.setUsername(properties.getUsername());
- druidDataSource.setPassword(properties.getPassword());
-
- druidDataSource.setInitialSize(properties.getInitialSize());
- druidDataSource.setMaxActive(properties.getMaxActive());
- druidDataSource.setMinIdle(properties.getMinIdle());
- druidDataSource.setMaxWait(properties.getMaxWait());
- druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
- druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
- druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());
- druidDataSource.setValidationQuery(properties.getValidationQuery());
- druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());
- druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
- druidDataSource.setTestOnReturn(properties.isTestOnReturn());
- druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
- druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());
- druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements());
- try {
- // druidDataSource.setFilters(properties.getFilters());
- druidDataSource.init();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return druidDataSource;
- }
- }

9、多数据源属性类:
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- /**
- * 多数据源属性,yml
- */
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class DataSourceProperties {
- private String driverClassName;
- private String url;
- private String username;
- private String password;
- /**
- * Druid默认参数
- */
- private int initialSize = 2;
- private int maxActive = 10;
- private int minIdle = -1;
- private long maxWait = 60 * 1000L;
- private long timeBetweenEvictionRunsMillis = 60 * 1000L;
- private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;
- private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7;
- private String validationQuery = "select 1";
- private int validationQueryTimeout = -1;
- private boolean testOnBorrow = false;
- private boolean testOnReturn = false;
- private boolean testWhileIdle = true;
- private boolean poolPreparedStatements = false;
- private int maxOpenPreparedStatements = -1;
- private boolean sharePreparedStatements = false;
- private String filters = "stat,wall";
- }

10、数据库名字常量:
- /**
- * 数据库名字常量
- */
- public class DBTypeName {
- public static final String MASTER = "master";
- public static final String SLAVE = "slave";
- }
11、多数据源上下文 ,用来放置数据源名字:
- import java.util.ArrayDeque;
- import java.util.Deque;
- /**
- * 多数据源上下文
- * 用来放置数据源
- */
- public class DynamicContextHolder {
- private static final ThreadLocal<Deque<String>> HOLDER = new ThreadLocal<Deque<String>>() {
- @Override
- protected Deque<String> initialValue() {
- return new ArrayDeque<String>();
- }
- };
- /**
- * 获得当前线程数据源
- * @return 数据源名称
- */
- public static String peek() {
- return HOLDER.get().peek();
- }
-
- /**
- * 设置当前线程数据源
- * @param dataSource 数据源名称
- */
- public static void push(String dataSource) {
- HOLDER.get().push(dataSource);
- }
- /**
- * 清空当前线程数据源
- */
- public static void poll() {
- Deque<String> deque = HOLDER.get();
- deque.poll();
- if (deque.isEmpty()) {
- HOLDER.remove();
- }
- }
- }

12、多数据源属性类(把yml中多数据源信息对应成map):
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import java.util.LinkedHashMap;
- import java.util.Map;
- /**
- * 多数据源属性
- */
- @ConfigurationProperties(prefix = "spring.datasource.dynamic")
- public class DynamicDataSourceProperties {
- private Map<String, DataSourceProperties> datasource = new LinkedHashMap<>();
- public Map<String, DataSourceProperties> getDatasource() {
- return datasource;
- }
- public void setDatasource(Map<String, DataSourceProperties> datasource) {
- this.datasource = datasource;
- }
- }

13、多数据源动态切换配置类:
- import com.alibaba.druid.pool.DruidDataSource;
- import com.baomidou.mybatisplus.core.MybatisConfiguration;
- import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.apache.ibatis.type.JdbcType;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.boot.context.properties.EnableConfigurationProperties;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * 配置多数据源
- */
- @Slf4j
- @Configuration
- @EnableConfigurationProperties(DynamicDataSourceProperties.class)
- public class MyDynamicDataSourceConfig {
- @Autowired
- private DynamicDataSourceProperties dynamicDataSourceProperties;
-
- @Bean(name = "dataSourceProperties")
- @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
- public DataSourceProperties dataSourceProperties() {
- return new DataSourceProperties();
- }
-
- @Bean
- public MyRoutingDataSource dynamicDataSource(@Qualifier("dataSourceProperties") DataSourceProperties dataSourceProperties) {
- MyRoutingDataSource dynamicDataSource = new MyRoutingDataSource();
- Map<String, DataSourceProperties> dataSourcePropertiesMap = dynamicDataSourceProperties.getDatasource();
- Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
- dataSourcePropertiesMap.forEach((k, v) -> {
- DruidDataSource druidDataSource = DataSourceFactory.buildDruidDataSource(v);
- targetDataSources.put(k, druidDataSource);
- });
- dynamicDataSource.setTargetDataSources(targetDataSources);
- //默认数据源
- DruidDataSource defaultDataSource = DataSourceFactory.buildDruidDataSource(dataSourceProperties);
- dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
- return dynamicDataSource;
- }
-
- @Bean("sqlSessionFactory")
- public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceProperties") DataSourceProperties dataSourceProperties) throws Exception {
- MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
- sqlSessionFactory.setDataSource(dynamicDataSource(dataSourceProperties));
- MybatisConfiguration configuration = new MybatisConfiguration();
- configuration.setJdbcTypeForNull(JdbcType.NULL);
- configuration.setMapUnderscoreToCamelCase(true);
- configuration.setCacheEnabled(false);
- sqlSessionFactory.setConfiguration(configuration);
- return sqlSessionFactory.getObject();
- }
- }

14、多数据源路由:
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
- /**
- * 多数据源
- */
- @Slf4j
- public class MyRoutingDataSource extends AbstractRoutingDataSource {
- @Override
- protected Object determineCurrentLookupKey() {
- String dataSource = DynamicContextHolder.peek();
- log.info("使用数据源:{}", dataSource);
- return dataSource;
- }
- }
15、多数据源路由理解:参考下图(借用别人的图)銆怉bstractRoutingDataSource銆戝垎鏋� - zzsuje - 鍗氬鍥�

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。