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

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日

相关文章

  • Java如何读写Properties配置文件(Properties类)

    下面我将详细讲解“Java如何读写Properties配置文件(Properties类)”的完整攻略。 什么是Properties配置文件 Properties文件是Java中一种非常常用的配置文件格式,它采用Key-Value的形式存储数据,是一种轻量级的配置文件。Properties文件一般用于存储应用程序配置信息,如数据库连接信息、系统配置信息等。 P…

    Java 2023年6月15日
    00
  • Java对zip,rar,7z文件带密码解压实例详解

    Java对zip,rar,7z文件带密码解压实例详解 在Java中,可以通过使用第三方库来实现对压缩文件的解压操作。其中,针对带有密码保护的压缩文件,需要借助专门的工具才能解压。本文将介绍如何使用Java对zip、rar、7z文件带密码进行解压的详细攻略。 I. 依赖库 首先,需要引入以下依赖库: <dependency> <groupId…

    Java 2023年5月20日
    00
  • 深入了解Java中循环结构的使用

    深入了解 Java 中循环结构的使用 循环结构是编程中非常重要的一个概念,Java 中包含多种不同类型的循环结构。掌握这些循环结构可以帮助我们更高效地进行编程。下面是深入了解 Java 中循环结构的完整攻略。 for 循环 for 循环是最常用的循环结构之一。它允许我们按照在代码中指定的条件来重复执行特定的代码块。for 循环的语法如下: for (init…

    Java 2023年5月26日
    00
  • Java实现字符串切割的方法详解

    Java实现字符串切割的方法详解 在Java开发中,经常需要将一个字符串按照特定规则进行切割,切割后的字符串可以使用来进行各种操作。本文就 Java 实现字符串切割的方法进行详细的讲解 1、使用 split() 方法 Java内置的String类中,提供了 split() 方法,该方法可以实现对字符串按照特定规则进行切割,返回一个字符串数组。下面是使用 sp…

    Java 2023年5月26日
    00
  • Java时间复杂度、空间复杂度的深入详解

    Java时间复杂度、空间复杂度的深入详解 什么是时间复杂度? 时间复杂度是对一个算法运行时间的度量,通常用大O符号表示。 常见的时间复杂度有: O(1):常数复杂度,运行时间和数据规模无关,如单次循环、赋值等; O(logn):对数复杂度,如二分查找; O(n):线性复杂度,与数据规模成正比,如遍历一次数组; O(n^2):平方复杂度,与数据规模的平方成正比…

    Java 2023年5月19日
    00
  • Spring MVC Controller返回值及异常的统一处理方法

    下面我将为你详细讲解“Spring MVC Controller返回值及异常的统一处理方法”的完整攻略。 一、Controller返回值的处理 在Spring MVC框架中,Controller负责处理客户端的HTTP请求并响应相应的结果给客户端。当客户端请求到达Controller之后,Controller需要根据业务逻辑处理数据,并根据结果返回响应结果给…

    Java 2023年5月27日
    00
  • Java中ShardingSphere 数据分片的实现

    非常感谢您对“Java中ShardingSphere 数据分片的实现”的关注。下面是大致的攻略: 1. 什么是ShardingSphere ShardingSphere是一个开源的分布式数据库中间件解决方案,提供数据库分片、分布式事务、数据治理等功能。它由Apache ShardingSphere孵化经过一年多的孵化过程,于2021年2月正式成为Apache…

    Java 2023年5月20日
    00
  • 什么是Java多线程,如何实现

    什么是Java多线程? 多线程是指在一个程序中同时运行多个线程,并行执行多个任务的技术。Java是一种多线程编程语言,提供了丰富的多线程API,使得开发者可以轻松地创建多线程应用程序。 在Java中,每个线程都是一种独立的执行路径,每个线程都会独立地执行自己的代码和内存空间,并且可以互不干扰的访问其它线程中的数据。 如何实现Java多线程? Java提供了两…

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