Springboot多数据源配置之整合dynamic-datasource方式

yizhihongxing

Springboot多数据源配置之整合dynamic-datasource方式

在实际的应用开发中,我们往往需要连接多个数据库来存储不同的数据,而Springboot提供了多种方式来实现多数据源配置,其中一种方便易用的方式就是使用dynamic-datasource这个开源的库。

本文将介绍如何使用dynamic-datasource来配置Springboot的多数据源,并提供两个示例来演示如何在应用中使用它。

集成dynamic-datasource

  1. pom.xml中添加以下依赖:

xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>

  1. 配置application.yml文件,设置数据源信息:

yaml
dynamic-datasource:
datasource:
master:
url: jdbc:mysql://localhost:3306/master
username: root
password: root
slave:
url: jdbc:mysql://localhost:3306/slave
username: root
password: root

在上述示例中,我们配置了两个数据源,一个是master,另一个是slave

  1. 在Springboot主类上添加注解@EnableDynamicDataSource

  2. 在需要使用数据源的地方使用注解@DS来指定数据源名称。默认的数据源名称为master

```java
@Service
public class UserServiceImpl implements UserService {

   @Autowired
   private UserMapper userMapper;

   @DS("slave")
   @Override
   public List<User> listUsers() {
       return userMapper.listUsers();
   }

   // 省略其他方法

}
```

示例1:应用中使用单例的动态数据源

在这个示例中,我们将使用dynamic-datasource来配置一个单例的动态数据源,并演示如何在应用中使用它。

  1. 配置数据源信息

在application.yml文件中配置数据源信息,同时也配置了一个默认数据源:

```yaml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root

dynamic-datasource:
datasource:
db1:
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
db2:
url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
primary: db1
```

我们配置了三个数据源,一个是默认数据源spring.datasource,另外两个是db1db2

  1. 类和注解的配置

我们需要创建一个DynamicDataSourceConfig类,用于动态创建数据源。同时,使用注解@Import来将这个类导入进来。

java
@Configuration
@Import(DynamicDataSourceRegister.class)
public class DynamicDataSourceConfig {
}

动态数据源的注册类DynamicDataSourceRegister如下所示:

```java
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

   private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);

   private ResourceLoader resourceLoader;

   private PropertyValues dataSourcePropertyValues;

   // 默认数据源
   private DataSource defaultDataSource;

   private Map<String, DataSource> customDataSources = new HashMap<>();

   @Override
   public void setResourceLoader(ResourceLoader resourceLoader) {
       this.resourceLoader = resourceLoader;
   }

   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

       // 注册默认数据源
       Map<String, Object> dsMap = new HashMap<>();
       dsMap.put("type", DataSource.class);
       dsMap.put("driverClassName", "com.mysql.jdbc.Driver");
       dsMap.put("url", "jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8");
       dsMap.put("username", "root");
       dsMap.put("password", "root");
       defaultDataSource = buildDataSource(dsMap);
       dataBinder(defaultDataSource);

       // 注册其它数据源
       Map<String, Object> dataSourceMap = (Map<String, Object>) YamlUtils.load("datasource.yml");

       // 这里也可以使用JdbcUtils.execute()来获取数据源信息,而不是从YAML配置文件中获取
       for (String key : dataSourceMap.keySet()) {
           Map<String, Object> ds = (Map<String, Object>) dataSourceMap.get(key);
           DataSource dataSource = buildDataSource(ds);
           customDataSources.put(key, dataSource);
           dataBinder(dataSource);
       }

       // 注册动态数据源
       Map<String, Object> targetDataSources = new HashMap<>();
       targetDataSources.put("dataSource", defaultDataSource);
       targetDataSources.putAll(customDataSources);

       // 添加数据源
       DynamicDataSourceContextHolder.dataSourceIds.addAll(customDataSources.keySet());

       RootBeanDefinition beanDefinition = new RootBeanDefinition(DynamicDataSource.class);

       // 给bean添加属性
       beanDefinition.getPropertyValues().addPropertyValue("defaultTargetDataSource", defaultDataSource);
       beanDefinition.getPropertyValues().addPropertyValue("targetDataSources", targetDataSources);

       registry.registerBeanDefinition("dataSource", beanDefinition);

       logger.info("Dynamic DataSource Registry");
   }

   /**
    * 创建数据源
    *
    * @param dsMap
    * @return
    */
   private DataSource buildDataSource(Map<String, Object> dsMap) {
       Object type = dsMap.get("type");
       if (type == null) {
           type = DruidDataSource.class;
       }
       Class<? extends DataSource> dataSourceType;
       try {
           dataSourceType = (Class<? extends DataSource>) type;
       } catch (Exception e) {
           throw new IllegalArgumentException("Invalid dataSource type", e);
       }
       String driverClassName = dsMap.get("driverClassName").toString();
       String url = dsMap.get("url").toString();
       String username = dsMap.get("username").toString();
       String password = dsMap.get("password").toString();
       DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password);
       return factory.type(dataSourceType).build();
   }

   /**
    * 为数据源绑定更多属性
    *
    * @param dataSource
    */
   private void dataBinder(DataSource dataSource) {
       if (dataSource instanceof DruidDataSource) {
           WallFilter wallFilter = new WallFilter();
           wallFilter.setConfig(wallConfig());
           List<Filter> filters = new ArrayList<>();
           filters.add(wallFilter);
           ((DruidDataSource) dataSource).setProxyFilters(filters);
       }
   }

   /**
    * 加载wallConfig
    *
    * @return
    */
   private WallConfig wallConfig() {
       WallConfig wallConfig = new WallConfig();
       wallConfig.setMultiStatementAllow(true);
       wallConfig.setNoneBaseStatementAllow(true);
       return wallConfig;
   }

}
```

动态数据源类DynamicDataSource如下所示:

```java
public class DynamicDataSource extends AbstractRoutingDataSource {

   private static Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

   @Override
   protected Object determineCurrentLookupKey() {
       logger.debug("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
       return DynamicDataSourceContextHolder.getDataSourceKey();
   }

}
```

  1. Service层代码中使用所需的数据源

现在,我们可以在Service层的代码中使用所需的数据源。在使用的方法上添加注解@DS来指定使用哪个数据源。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @DS("db1")
    @Override
    public List<User> listUsersFromDb1() {
        return userMapper.listUsers();
    }

    @DS("db2")
    @Override
    public List<User> listUsersFromDb2() {
        return userMapper.listUsers();
    }

    // 省略其他方法
}

这个示例中,当我们调用listUsersFromDb1()方法时,它会使用db1数据源来查询数据;而当我们调用listUsersFromDb2()方法时,它会使用db2数据源来查询数据。

示例2:应用中使用多例的动态数据源

在这个示例中,我们将使用dynamic-datasource来配置一个多例的动态数据源,并演示如何在应用中使用它。

  1. 配置数据源信息

在application.yml文件中配置数据源信息:

yaml
dynamic-datasource:
datasource:
db1:
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
db2:
url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
primary: db1

我们配置了两个数据源,db1db2。默认的数据源为db1

  1. 注册动态数据源,使其成为多例对象

为了使用多例的数据源,我们需要将动态数据源的注册类DynamicDataSourceRegister中的@Configuration注解移除。因此,我们需要将它与DynamicDataSourceConfig分开。

DynamicDataSourceConfig类:

```java
@Configuration
public class DynamicDataSourceConfig {

   @Bean
   @ConfigurationProperties(prefix = "dynamic-datasource")
   public DataSourceProperties dataSourceProperties() {
       return new DataSourceProperties();
   }

   @Bean
   public DataSource dataSource() {
       return dataSourceProperties().initializeDataSourceBuilder().build();
   }

   @Bean
   public JdbcTemplate jdbcTemplate() {
       return new JdbcTemplate(dataSource());
   }

}
```

DynamicDataSourceRegister类:

```java
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

   private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);

   private ResourceLoader resourceLoader;

   private Environment environment;

   private PropertyValues dataSourcePropertyValues;

   // 默认数据源
   private DataSource defaultDataSource;

   private Map<String, DataSource> customDataSources = new HashMap<>();

   @Override
   public void setResourceLoader(ResourceLoader resourceLoader) {
       this.resourceLoader = resourceLoader;
   }

   @Override
   public void setEnvironment(Environment environment) {
       this.environment = environment;
   }

   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

       // 注册默认数据源
       Map<String, Object> dsMap = new HashMap<>();
       dsMap.put("type", DruidDataSource.class);
       dsMap.put("driverClassName", "com.mysql.jdbc.Driver");
       dsMap.put("url", "jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8");
       dsMap.put("username", "root");
       dsMap.put("password", "root");
       defaultDataSource = buildDataSource(dsMap);

       // 注册其它数据源
       Map<Object, Object> dataSourceMap = new HashMap<>();

       RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "dynamic-datasource.datasource.");

       String[] dsNames = propertyResolver.getProperty("names", String[].class);
       Arrays.stream(dsNames)
               .forEach(dsName -> {
                   Map<String, Object> subProperties = propertyResolver.getSubProperties(dsName + ".");
                   DataSource dataSource = DataSourceBuilder.create()
                           .driverClassName(subProperties.get("driver-class-name").toString())
                           .url(subProperties.get("url").toString())
                           .username(subProperties.get("username").toString())
                           .password(subProperties.get("password").toString())
                           .build();
                   customDataSources.put(dsName, dataSource);
                   dataSourceMap.put(dsName, dataSource);
               });

       // 添加数据源
       DynamicDataSourceContextHolder.dataSourceIds.addAll(customDataSources.keySet());

       // 创建动态数据源
       GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
       beanDefinition.setBeanClass(DynamicRoutingDataSource.class);
       beanDefinition.setSynthetic(true);

       // 给bean添加属性
       MutablePropertyValues mpv = beanDefinition.getPropertyValues();
       mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
       mpv.addPropertyValue("targetDataSources", dataSourceMap);
       registry.registerBeanDefinition("dataSource", beanDefinition);

       logger.info("Dynamic DataSource Registry");
   }

   /**
    * 创建数据源
    *
    * @param dsMap
    * @return
    */
   private DataSource buildDataSource(Map<String, Object> dsMap) {
       Object type = dsMap.get("type");
       if (type == null) {
           type = DruidDataSource.class;
       }
       Class<? extends DataSource> dataSourceType;
       try {
           dataSourceType = (Class<? extends DataSource>) type;
       } catch (Exception e) {
           throw new IllegalArgumentException("Invalid dataSource type", e);
       }
       String driverClassName = dsMap.get("driverClassName").toString();
       String url = dsMap.get("url").toString();
       String username = dsMap.get("username").toString();
       String password = dsMap.get("password").toString();
       DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password);
       return factory.type(dataSourceType).build();
   }

}
```

DynamicRoutingDataSource类:

```java
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

   @Override
   protected DataSource determineTargetDataSource() {
       return super.determineTargetDataSource();
   }

   @Override
   protected Object determineCurrentLookupKey() {
       return DynamicDataSourceContextHolder.getDataSourceKey();
   }

}
```

  1. Service层代码中使用所需的数据源

与示例1相似,我们可以在Service层的代码中使用所需的数据源。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @DS("db1")
    @Override
    public List<User> listUsersFromDb1() {
        String sql = "SELECT * FROM users";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
    }

    @DS("db2")
    @Override
    public List<User> listUsersFromDb2() {
        String sql = "SELECT * FROM users";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
    }

    // 省略其他方法
}

此示例中的方法也类似于示例1。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Springboot多数据源配置之整合dynamic-datasource方式 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 如何在IntelliJ IDEA 2018上配置Tomcat并运行第一个JavaWeb项目

    请参考以下步骤来配置Tomcat并运行第一个JavaWeb项目: 1. 下载和安装Tomcat 首先,下载Tomcat并解压缩到本地。 推荐使用Tomcat 8.0版本。 打开Tomcat/bin目录,双击startup.bat文件启动Tomcat服务器。 2. 在IntelliJ IDEA 2018中配置Tomcat服务器 打开IntelliJ IDEA,…

    Java 2023年5月19日
    00
  • 详解SpringMVC的拦截器链实现及拦截器链配置

    详解SpringMVC的拦截器链实现及拦截器链配置 在SpringMVC中,拦截器是一个非常重要的组件,它可以帮助我们在请求到达控制器之前或之后执行一些操作。本文将详细介绍SpringMVC的拦截器链实现及拦截器链配置,并提供两个示例说明。 拦截器链实现 在SpringMVC中,拦截器链是由HandlerInterceptor接口实现的。拦截器链中的每个拦截…

    Java 2023年5月17日
    00
  • JAVA中的日期时间类用法总结

    JAVA中的日期时间类用法总结 一、介绍 JAVA中的日期时间类可以用来处理日期、时间等与时间有关的业务。JAVA中内置了多个日期时间类,比较常用的有: Date类:这个类已经被替代了,不推荐使用。 Calendar类:是一个抽象类,提供了一组可以操纵日期、时间与之对应的字段的方法,同时还提供了其他的一些常用模块方法。 SimpleDateFormat类:可…

    Java 2023年5月20日
    00
  • 图解Eclipse j2ee开发环境的搭建过程

    图解Eclipse J2EE开发环境的搭建过程 简介 本教程介绍如何使用Eclipse IDE搭建J2EE开发环境。J2EE是Java 2 Enterprise Edition的缩写,是Java平台上使用最广泛的企业级应用开发技术之一。 步骤 第一步:安装Java JDK 确定已经安装Java JDK,否则需要先下载并安装Java JDK。可访问官方网站Ja…

    Java 2023年5月26日
    00
  • Java Date与String的相互转换详解

    Java Date与String的相互转换详解 在Java开发中,我们经常需要对日期时间进行处理和转换,而Java中常用的日期时间类型有两个:Date和String。本篇攻略将详细讲解Java Date和String的相互转换方法。 Date类型转换为String类型 将Date类型转换为String类型,常用的有以下两种方式: 方法一:使用SimpleDa…

    Java 2023年5月20日
    00
  • MyBatis后端对数据库进行增删改查等操作实例

    下面是MyBatis后端对数据库进行增删改查等操作实例的详细攻略: 1. 准备工作 在进行MyBatis操作之前,我们需要准备好以下内容: 数据库:我们需要在本地或远程服务器上搭建好相应的数据库,并在其中创建好表格。 MyBatis环境:我们需要使用Maven或Gradle等工具引入MyBatis相关依赖,并在项目中配置好MyBatis的相关信息,如数据库连…

    Java 2023年5月19日
    00
  • SpringBoot错误处理机制以及自定义异常处理详解

    Spring Boot错误处理机制以及自定义异常处理详解 1. Spring Boot错误处理机制 Spring Boot的错误处理机制主要是基于Spring MVC框架提供的异常处理机制进行封装扩展的,并通过@ControllerAdvice注解标注的类的统一异常处理方法对异常进行处理。 Spring Boot提供了两种常见的异常处理方式: 1.1 @Ex…

    Java 2023年5月27日
    00
  • SpringBoot整合Spring Security过滤器链加载执行流程源码分析(最新推荐)

    下面我来详细讲解一下 SpringBoot 整合 Spring Security 过滤器链加载执行流程源码分析的完整攻略。 1. 概述 Spring Security 是基于 Spring 框架实现的安全框架。它的作用是保护系统的安全性,可以对用户进行身份认证和权限控制。Spring Security 是一个强大而灵活的安全框架,它提供了多种安全特性,包括用…

    Java 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部