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日

相关文章

  • JS实现的简单拖拽购物车功能示例【附源码下载】

    下面我就简单介绍一下 JS 实现的简单拖拽购物车功能示例的完整攻略。首先,该示例的功能主要就是实现物品拖拽,同时将拖拽到的物品添加到购物车中。 示例说明1:创建HTML页面 首先需要创建一个 HTML 页面,该页面内容如下: <!DOCTYPE html> <html> <head> <meta charset=&q…

    Java 2023年5月26日
    00
  • 详解Java中实现SHA1与MD5加密算法的基本方法

    当今网络环境中,安全性是非常重要的一个问题。密码的保护已经成为了一个必须面对的任务。SHA1和MD5是两种常见的加密算法,它们可以将密码字符串加密为一串看似随意的字符,从而实现密码的保护。在Java中,实现SHA1与MD5加密算法有以下基本方法: 1. 使用Java内置的MessageDigest类 MessageDigest是Java提供的安全类之一,它可…

    Java 2023年5月19日
    00
  • SpringMVC使用注解实现登录功能

    下面我将为您详细讲解如何使用注解实现SpringMVC的登录功能。 1. 创建SpringMVC项目 首先,我们需要使用Maven工具创建一个空的SpringMVC项目: <groupId>com.example</groupId> <artifactId>springmvcdemo</artifactId> …

    Java 2023年5月16日
    00
  • Java面向对象选择题总结归纳

    Java面向对象选择题总结归纳 1. 答题技巧 在 Java 面向对象的选择题中,需要重点关注四个方面: 继承 多态 接口 抽象类 在做选择题时,需要结合这四个方面来推断代码的输出结果,而且需要结合实际情况进行分析,不能片面理解。 2. 继承 Java 中可以通过继承实现类的复用。在做选择题时,需要注意以下几点: 子类继承了父类的所有方法和属性,但是并不会继…

    Java 2023年5月26日
    00
  • JSP加载JS文件不起作用的有效解决方法

    下面是关于“JSP加载JS文件不起作用的有效解决方法”的完整攻略: 问题背景 在JSP中使用JavaScript是非常常见的,但是有时候我们可能会遇到这样的问题:在JSP中引用的JS文件并没有起作用,也就是说JS代码没有被执行。这种情况下我们该怎么解决呢? 解决方法 具体的方法是在JSP文件中使用<script>标签引入JS文件时,要注意添加ty…

    Java 2023年6月15日
    00
  • java并发编程中ReentrantLock可重入读写锁

    ReentrantLock是Java并发编程中一种可重入的读写锁,它比Synchronized更加灵活,能够满足不同的场景需求。下面我们来详细讲解如何使用ReentrantLock读写锁。 1. ReentrantLock的基本使用 1.1 创建ReentrantLock对象 import java.util.concurrent.locks.Reentra…

    Java 2023年5月26日
    00
  • java编程实现求质数与因式分解代码分享

    下面是 “Java编程实现求质数与因式分解代码分享” 的完整攻略。 目录 介绍 求质数的代码实现 因式分解的代码实现 示例说明 总结 介绍 本文将介绍Java编程实现求质数与因式分解的代码。当我们需要判断一个数是不是质数时,我们可以使用质数的定义:只有1和该数本身能够整除它,它才是质数。因式分解是指将一个数分解成几个互质的整数乘积的形式。这里我们使用两种算法…

    Java 2023年5月19日
    00
  • Java下使用Oracle存储过程(详解)第3/3页

    下面是详细讲解“Java下使用Oracle存储过程(详解)第3/3页”的完整攻略。 1. 概述 这篇攻略主要介绍如何在Java中使用Oracle存储过程。存储过程是一组一起执行的SQL语句,可以接收参数并返回结果。在一些大型应用中,存储过程的使用可以提高数据库性能,减小网络传输压力,增加数据安全等等。 2. 实现步骤 步骤如下: (1)创建存储过程 首先在O…

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