专栏首页特特的专栏spring boot 配置 多数据源
原创

spring boot 配置 多数据源

1. 前言

在日常生活中,我们不可避免要在工程中配置多个数据源,下面我就给大家讲一下怎么在spring boot里面配置多数据源,并且在文章结尾给出一个github的demo,希望对大家有所帮助

2. application.yml 配置多个数据库

spring:
  datasource:
    write:
      url: jdbc:mysql://192.168.31.155:3306/test_1?characterEncoding=UTF-8&useSSL=false
      username: root
      password: root
    read:
      url: jdbc:mysql://192.168.31.155:3306/test_2?characterEncoding=UTF-8&useSSL=false
      username: root
      password: root

3. spring读取配置文件

package com.multi.datasource.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @create: 2021-01-14 14:56
 **/
@ConfigurationProperties(prefix = "spring.datasource.read")
@Data
public class DataSourceReadProperties {

    private String url;
    private String username;
    private String password;
}
package com.multi.datasource.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @create: 2021-01-14 14:56
 **/
@ConfigurationProperties(prefix = "spring.datasource.write")
@Data
public class DataSourceWriteProperties {

    private String url;
    private String username;
    private String password;
}

4. 数据源配置

package com.multi.datasource.config;

import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

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

/**
 * @create: 2021-01-14 14:55
 **/
@Configuration
public class DataSourceConfig {

    @Bean("dataSourceReadProperties")
    @ConditionalOnProperty(prefix = "spring.datasource.read", name = {"url", "username", "password"})
    public DataSourceReadProperties dataSourceReadProperties() {
        DataSourceReadProperties dataSourceReadProperties = new DataSourceReadProperties();
        return dataSourceReadProperties;
    }

    @Bean("dataSourceWriteProperties")
    @ConditionalOnProperty(prefix = "spring.datasource.write", name = {"url", "username", "password"})
    public DataSourceWriteProperties dataSourceWriteProperties() {
        DataSourceWriteProperties dataSourceWriteProperties = new DataSourceWriteProperties();
        return dataSourceWriteProperties;
    }

    @Bean("dataSourceRead")
    @ConditionalOnBean(name = "dataSourceReadProperties")
    public DataSource getDataSourceRead(@Qualifier("dataSourceReadProperties") DataSourceReadProperties dataSourceReadProperties) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(dataSourceReadProperties.getUrl());
        dataSource.setUsername(dataSourceReadProperties.getUsername());
        dataSource.setPassword(dataSourceReadProperties.getPassword());
        return dataSource;
    }

    @Bean("dataSourceWrite")
    @ConditionalOnBean(name = "dataSourceWriteProperties")
    public DataSource getDataSourceWrite(@Qualifier("dataSourceWriteProperties") DataSourceWriteProperties dataSourceWriteProperties) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(dataSourceWriteProperties.getUrl());
        dataSource.setUsername(dataSourceWriteProperties.getUsername());
        dataSource.setPassword(dataSourceWriteProperties.getPassword());
        return dataSource;
    }


    /**
     * 设置数据源路由,通过该类中的determineCurrentLookupKey决定使用哪个数据源
     */
    @Bean("routingDataSource")
    public AbstractRoutingDataSource routingDataSource(@Qualifier("dataSourceWrite") DataSource dataSourceWrite,
                                                       @Qualifier("dataSourceRead") DataSource dataSourceRead) {
        MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put(DbContextHolder.WRITE, dataSourceWrite);
        targetDataSources.put(DbContextHolder.READ, dataSourceRead);
        proxy.setDefaultTargetDataSource(dataSourceWrite);
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }

    /**
     * 多数据源需要自己设置sqlSessionFactory
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("routingDataSource") AbstractRoutingDataSource routingDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(routingDataSource);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // mybatis的XML的配置
        bean.setMapperLocations(resolver.getResources("classpath*:mapper/*Mapper.xml"));
        return bean.getObject();
    }

    /**
     * 设置事务,事务需要知道当前使用的是哪个数据源才能进行事务处理
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("routingDataSource") AbstractRoutingDataSource routingDataSource) {
        return new DataSourceTransactionManager(routingDataSource);
    }
}

5.多数据源切换配置

package com.multi.datasource.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * 这里切换读/写模式
 * 原理是利用ThreadLocal保存当前线程是否处于读模式(通过开始READ_ONLY注解在开始操作前设置模式为读模式,
 * 操作结束后清除该数据,避免内存泄漏,同时也为了后续在该线程进行写操作时任然为读模式
 *
 * @author zxliuyu
 */
@Slf4j
public class DbContextHolder {

    public static final String WRITE = "write";
    public static final String READ = "read";

    private static ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDbType(String dbType) {
        if (StringUtils.isBlank(dbType)) {
            log.error("DbContextHolder dbType is null");
        }
        contextHolder.set(dbType);
    }

    public static String getDbType() {
        return StringUtils.isBlank(contextHolder.get()) ? WRITE : contextHolder.get();
    }

    public static void clearDbType() {
        contextHolder.remove();
    }
}
package com.multi.datasource.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

@Slf4j
public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        String typeKey = DbContextHolder.getDbType();
        if (typeKey.equals(DbContextHolder.WRITE)) {
            log.info("dataSource is use write");
            return typeKey;
        }
        log.info("dataSource is use read");
        return DbContextHolder.READ;
    }
}

6. 通过注解指定数据源

package com.multi.datasource.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @create: 2021-01-14 17:22
 **/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}
package com.multi.datasource.config;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class ReadOnlyInterceptor implements Ordered {

    @Around("@annotation(readOnly)")
    public Object setRead(ProceedingJoinPoint joinPoint, ReadOnly readOnly) throws Throwable {
        try {
            DbContextHolder.setDbType(DbContextHolder.READ);
            return joinPoint.proceed();
        } finally {
            // 清楚DbType一方面为了避免内存泄漏,更重要的是避免对后续在本线程上执行的操作产生影响
            DbContextHolder.clearDbType();
            log.info("remove threadLocal");
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

7. github地址

https://github.com/constantRAIN/multi-data-source

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • Spring Boot配置多数据源

    如果尝试修改只读数据源则会抛出异常:java.sql.SQLException: Connection is read-only. Queries leadin...

    十毛
  • Spring Boot支持多Redis数据源

    十毛
  • Spring Boot MyBatis配置多数据源

    十毛
  • Spring boot 数据源未配置异常

    问题 在使Springboot自动生成的项目框架时如果选择了数据源,比如选择了mysql,生成项目之后,启动会报一下异常: Description: Cann...

    程序新视界
  • Spring Boot多数据源配置之JdbcTemplate

    多数据源配置也算是一个常见的开发需求,Spring和SpringBoot中,对此都有相应的解决方案,不过一般来说,如果有多数据源的需求,我还是建议首选分布式数据...

    江南一点雨
  • Spring Boot + Mybatis多数据源和动态数据源配置

    转载自 http://blog.csdn.net/neosmith/article/details/61202084

    allsmallpig
  • spring boot学习4 多环境配置

    在企业中,一个项目一般都有测试环境(test) 、开发环境(dev)、生产环境(pro)等等。在每个环境中,配置信息会不一样的。比如数据库、静态资源文件位置等都...

    凯哥Java
  • Spring Boot2.x 动态数据源配置

    基于 Spring Boot 2.x、Spring Data JPA、druid、mysql 的动态数据源配置Demo,适合用于数据库的读写分离等应用场景。通过...

    壹言
  • Spring boot配置多个Redis数据源操作实例

    平时都是使用本地环境的单Redis情况比较多,在集群环境的情况下连接多个Redis数据库是很正常的情况。

    程序员小强
  • Spring Boot之JdbcTemplate多数据源配置与使用

    之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源。在单数据源的情况下,Spring Boot的配置非常简单,只需要在a...

    大道七哥
  • Spring配置数据源

    applicationContext.xml加载jdbc.properties配置文件获得连接信息

    兮动人
  • Spring Boot 2.X(五):MyBatis 多数据源配置

    MyBatis 多数据源配置,最近在项目建设中,需要在原有系统上扩展一个新的业务模块,特意将数据库分库,以便减少复杂度。本文直接以简单的代码示例,如何对 MyB...

    朝雾轻寒
  • Spring Boot 1.0 && 2.0 + JPA 多数据源配置与使用

    mysql 对应的数据源配置中,定义了实体 Student 和对应的数据层接口 StudentRepository:

    happyJared
  • Spring Boot入门教程2-4、使用Spring Boot+MyBatis多数据源配置(xml配置版)

    本项目构建基于:https://ken.io/note/springboot-course-basic-curd-xml

    KenTalk
  • Spring Boot:集成Druid数据源

    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为...

    朝雨忆轻尘
  • Spring Boot集成数据源

    Spring Boot为创建数据库的数据源提供了非常好的支持。不需要编写任何额外的代码来在Spring Boot中创建数据源(DataSource)。只需添加依...

    黑洞代码
  • Spring Boot 1.0 && 2.0 + Mybatis 多数据源配置与使用

    mysql 对应的数据源配置中,定义了实体 Boy 和对应的数据层接口 BoyMapper:

    happyJared
  • Spring Boot 配置

    为了 Spring Boot 能够更好地生成配置元数据文件,我们可以在创建项目时添加 Spring Configuartion Processor 依赖,或者在...

    村雨遥
  • Spring Boot---(2)SpringBoot多环境配置和使用

    我们在开发Spring Boot应用时,通常同一套程序会被应用和安装到几个不同的环境,比如:开发、测试、生产等。其中每个环境的数据库地址、服务器端口等等配置都会...

    IT云清

扫码关注腾讯云开发者

领取腾讯云代金券