SpringBoot多数据源配置MySQL和PostgreSQL同时使用

avatar 2020年1月4日14:46:11 评论 59 views
广告也精彩

在企业级项目中,系统往往会越来越庞大,场景也各有不同,对数据库的要求也不尽相同。

比如我们有一个项目原来是用 Oracle 数据库,后台需要做一些地图相关的功能,需要地理运算,这个时候有一个比较好的选择就是 PostgreSQL,因为其支持一种空间运算的扩展,即 PostGis,所以我们地图项目基本都是用 PostgreSQL的。但是我们又有一些数据是在  Oracle 库里,在没有完全拆分之前,还是需要查询 Oracle 库的。

所以在早先时候,就必须要做到多数据源配置。

本文介绍一些具体实现。

这里以 Db1 (MySQL)和 Db2 (PostgreSQL) 演示配置并测试。

完整代码地址:https://github.com/saysky/sensboot

一、Maven 依赖

分别是 MySQLPostgreSQL 的依赖

  1. <!-- mysql-->
  2.   <dependency>
  3.       <groupId>mysql</groupId>
  4.       <artifactId>mysql-connector-java</artifactId>
  5.       <scope>runtime</scope>
  6.   </dependency>
  7.   <!-- postgrepsql-->
  8.   <dependency>
  9.       <groupId>org.postgresql</groupId>
  10.       <artifactId>postgresql</artifactId>
  11.       <scope>runtime</scope>
  12.   </dependency>

SpringBoot 版本是 2.0.5.RELEASE,完整代码可以访问上面 GitHub 地址

 

二、核心配置部分

1.application.yml

  1. server:
  2.   port: 8080
  3. # mybatis配置
  4. mybatis:
  5.   configuration:
  6.     map-underscore-to-camel-casetrue
  7.     cache-enabled: true
  8. db1:
  9.   driverClassName: com.mysql.jdbc.Driver
  10.   url: jdbc:mysql://127.0.0.1:3306/sensboot?characterEncoding=utf8&useSSL=false&allowMultiQueries=true
  11.   username: root
  12.   password: 123456
  13.   initialSize: 5
  14.   minIdle: 5
  15.   maxActive: 50
  16. db2:
  17.   driverClassName: org.postgresql.Driver
  18.   url: jdbc:postgresql://127.0.0.1:5432/sensboot
  19.   username: liuyanzhao
  20.   password:
  21.   platform: postgres
  22.   initialSize: 5
  23.   minIdle: 5
  24.   maxActive: 50

不需要其他额外数据源配置,数据库连接池等其他配置我们都可以通过在自定义配置类里设置

 

2.配置属性类

既然我们上面自定义了配置,那么我们就需要读取它.

注意 prefix 指的是 yml 文件配置的前缀,后面的部分要和类属性名保持一致

Db1DataSourceProperties.java

  1. package com.liuyanzhao.sens.config;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5. import java.io.Serializable;
  6. /**
  7.  * @author 言曌
  8.  * @date 2020-01-03 17:03
  9.  */
  10. @Component
  11. @Data
  12. @ConfigurationProperties(prefix = "db1")
  13. public class Db1DataSourceProperties implements Serializable {
  14.     private String driverClassName;
  15.     private String url;
  16.     private String username;
  17.     private String password;
  18.     private Integer initialSize;
  19.     private Integer minIdle;
  20.     private Integer maxActive;
  21. }

 

Db2DataSourceProperties.java

  1. package com.liuyanzhao.sens.config;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5. import java.io.Serializable;
  6. /**
  7.  * @author 言曌
  8.  * @date 2020-01-03 17:03
  9.  */
  10. @Component
  11. @Data
  12. @ConfigurationProperties(prefix = "db2")
  13. public class Db2DataSourceProperties implements Serializable {
  14.     private String driverClassName;
  15.     private String url;
  16.     private String username;
  17.     private String password;
  18.     private Integer initialSize;
  19.     private Integer minIdle;
  20.     private Integer maxActive;
  21.     private String platform;
  22. }

 

3.数据源配置

Db1DataSourceConfig.java

  1. package com.liuyanzhao.sens.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.apache.ibatis.session.SqlSessionFactory;
  4. import org.mybatis.spring.SqlSessionFactoryBean;
  5. import org.mybatis.spring.SqlSessionTemplate;
  6. import org.mybatis.spring.annotation.MapperScan;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.beans.factory.annotation.Qualifier;
  9. import org.springframework.boot.context.properties.ConfigurationProperties;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.core.io.Resource;
  13. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  14. import org.springframework.core.io.support.ResourcePatternResolver;
  15. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  16. import javax.sql.DataSource;
  17. /**
  18.  * @author 言曌
  19.  * @date 2020-01-03 17:03
  20.  */
  21. @Configuration
  22. @MapperScan(basePackages = "com.liuyanzhao.sens.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
  23. public class Db1DataSourceConfig {
  24.     @Autowired
  25.     private Db1DataSourceProperties db1DataSourceProperties;
  26.     @Bean(name = "db1DataSource")
  27.     public DataSource dataSource() {
  28.         DruidDataSource dataSource = new DruidDataSource();
  29.         dataSource.setDriverClassName(db1DataSourceProperties.getDriverClassName());
  30.         dataSource.setUrl(db1DataSourceProperties.getUrl());
  31.         dataSource.setUsername(db1DataSourceProperties.getUsername());
  32.         dataSource.setPassword(db1DataSourceProperties.getPassword());
  33.         dataSource.setInitialSize(db1DataSourceProperties.getInitialSize());
  34.         dataSource.setMinIdle(db1DataSourceProperties.getMinIdle());
  35.         dataSource.setMaxActive(db1DataSourceProperties.getMaxActive());
  36.         return dataSource;
  37.     }
  38.     @Bean(name = "db1SqlSessionFactory")
  39.     public SqlSessionFactory sqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource,
  40.                                                @Qualifier("camelCaseConfiguration") org.apache.ibatis.session.Configuration configuration) throws Exception {
  41.         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  42.         sqlSessionFactoryBean.setDataSource(dataSource);
  43.         sqlSessionFactoryBean.setConfiguration(configuration);
  44.         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
  45.         Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/db1/*Mapper.xml");
  46.         sqlSessionFactoryBean.setMapperLocations(resources);
  47.         return sqlSessionFactoryBean.getObject();
  48.     }
  49.     @Bean(name = "db1SqlSessionTemplate")
  50.     public SqlSessionTemplate sqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
  51.         return new SqlSessionTemplate(sqlSessionFactory);
  52.     }
  53.     @Bean(name = "db1TransactionManager")
  54.     public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
  55.         return new DataSourceTransactionManager(dataSource);
  56.     }
  57.     @Bean(name = "camelCaseConfiguration")
  58.     @ConfigurationProperties(prefix = "mybatis.configuration")
  59.     public org.apache.ibatis.session.Configuration configuration() {
  60.         return new org.apache.ibatis.session.Configuration();
  61.     }
  62. }

 

Db2DataSourceConfig.java

  1. package com.liuyanzhao.sens.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.apache.ibatis.session.SqlSessionFactory;
  4. import org.mybatis.spring.SqlSessionFactoryBean;
  5. import org.mybatis.spring.SqlSessionTemplate;
  6. import org.mybatis.spring.annotation.MapperScan;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.beans.factory.annotation.Qualifier;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.context.annotation.Primary;
  12. import org.springframework.core.io.Resource;
  13. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  14. import org.springframework.core.io.support.ResourcePatternResolver;
  15. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  16. import javax.sql.DataSource;
  17. /**
  18.  * @author 言曌
  19.  * @date 2020-01-03 17:03
  20.  */
  21. @Configuration
  22. @MapperScan(basePackages = "com.liuyanzhao.sens.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory")
  23. public class Db2DataSourceConfig {
  24.     @Autowired
  25.     private Db2DataSourceProperties db2DataSourceProperties;
  26.     @Primary
  27.     @Bean(name = "db2DataSource")
  28.     public DataSource dataSource() {
  29.         DruidDataSource dataSource = new DruidDataSource();
  30.         dataSource.setDriverClassName(db2DataSourceProperties.getDriverClassName());
  31.         dataSource.setUrl(db2DataSourceProperties.getUrl());
  32.         dataSource.setUsername(db2DataSourceProperties.getUsername());
  33.         dataSource.setPassword(db2DataSourceProperties.getPassword());
  34.         dataSource.setInitialSize(db2DataSourceProperties.getInitialSize());
  35.         dataSource.setMinIdle(db2DataSourceProperties.getMinIdle());
  36.         dataSource.setMaxActive(db2DataSourceProperties.getMaxActive());
  37.         return dataSource;
  38.     }
  39.     @Bean(name = "db2SqlSessionFactory")
  40.     public SqlSessionFactory sqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource,
  41.                                                @Qualifier("camelCaseConfiguration") org.apache.ibatis.session.Configuration configuration) throws Exception {
  42.         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  43.         sqlSessionFactoryBean.setDataSource(dataSource);
  44.         sqlSessionFactoryBean.setConfiguration(configuration);
  45.         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
  46.         Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/db2/*Mapper.xml");
  47.         sqlSessionFactoryBean.setMapperLocations(resources);
  48.         return sqlSessionFactoryBean.getObject();
  49.     }
  50.     @Bean(name = "db2SqlSessionTemplate")
  51.     public SqlSessionTemplate sqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
  52.         return new SqlSessionTemplate(sqlSessionFactory);
  53.     }
  54.     @Bean(name = "db2TransactionManager")
  55.     public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
  56.         return new DataSourceTransactionManager(dataSource);
  57.     }
  58. }

这两个配置文件是相似的,只是指定不同的  mapper 扫描位置和 xml 文件位置。同时,我们自定义了 sqlSessionTemplate 和 transactionManager  的 bean 名称。需要用的时候指定对应的即可,下面会介绍事务的时候说明。

 

三、分别创建 mapper,准备测试

我们这里还是用 sensboot 那个项目测试。

在 mapper 包下创建 db1 和 db2,用于存放不同数据源的 mapper 类。

在 resources/mapper 下也创建对应的 db1 和 db2,用于存放不同数据源的 mapper xml 文件。

本地主要以 Db1UserMapper 和 Db2UserMapper 测试。

数据库两表结构也是一样的,对应实体类都是 User。

完整代码大家可以自己去 GitHub 上克隆。

这里贴一下 Db1UserMapper.xml 代码

其和 Db2UserMapper.xml 代码除了包名和类名不同都一样

  1. package com.liuyanzhao.sens.mapper.db1;
  2. import com.liuyanzhao.sens.entity.User;
  3. /**
  4.  * Db1 数据源的 mapper 测试
  5.  * @author 言曌
  6.  * @date 2020-01-03 17:39
  7.  */
  8. public interface Db1UserMapper {
  9.     /**
  10.      * 根据ID获得用户
  11.      * @param id
  12.      * @return
  13.      */
  14.     User findById(Long id);
  15.     /**
  16.      * 添加
  17.      * @param user
  18.      * @return
  19.      */
  20.     Integer insert(User user);
  21. }

 

然后贴一下对应的 xml,两个 xml 除了 namespace 写的不一样,几乎一致。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.liuyanzhao.sens.mapper.db2.Db2UserMapper">
  4.     <resultMap id="BaseResultMap" type="com.liuyanzhao.sens.entity.User">
  5.         <id column="id" property="id"/>
  6.         <result column="username" property="username"/>
  7.         <result column="nickname" property="nickname"/>
  8.         <result column="password" property="password"/>
  9.         <result column="email" property="email"/>
  10.         <result column="avatar" property="avatar"/>
  11.         <result column="status" property="status"/>
  12.         <result column="created_time" property="createdTime"/>
  13.         <result column="created_by" property="createdBy"/>
  14.         <result column="updated_time" property="updatedTime"/>
  15.         <result column="updated_by" property="updatedBy"/>
  16.     </resultMap>
  17.     <sql id="all_columns">
  18.         id, username, nickname, password, email, avatar, status,
  19.         created_time, created_by, updated_time, updated_by
  20.     </sql>
  21.     <insert id="insert">
  22.         INSERT INTO t_user
  23.         (
  24.         username, nickname, password, email,
  25.         avatar, status, created_time, created_by,
  26.         updated_time, updated_by
  27.         )
  28.         VALUES
  29.         (
  30.         #{username}, #{nickname}, #{password}, #{email},
  31.         #{avatar},  #{status},  #{createdTime},  #{createdBy},
  32.         #{updatedTime},  #{updatedBy}
  33.         )
  34.     </insert>
  35.     <select id="findById" resultMap="BaseResultMap">
  36.         SELECT
  37.         <include refid="all_columns"/>
  38.         FROM
  39.         t_user
  40.         WHERE id = #{id}
  41.     </select>
  42. </mapper>

 

创建表的 sql 这里就不贴了吧

四、测试数据源连接和查询

我们往两个数据库分别插入一条 id 为1的数据,内容不同

然后通过单元测试调用 db1UserMapper.findById(1) 和 db2UserMapper.findById(1)

分别都能返回对应结果,说明配置成功

db1 的测试类 如下

  1. package com.liuyanzhao.sens.mapper.db1;
  2. import com.liuyanzhao.sens.entity.User;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. import org.springframework.test.context.junit4.SpringRunner;
  8. /**
  9.  * @author 言曌
  10.  * @date 2020-01-03 17:48
  11.  */
  12. @SpringBootTest
  13. @RunWith(SpringRunner.class)
  14. public class Db1UserMapperTest {
  15.     @Autowired
  16.     private Db1UserMapper db1UserMapper;
  17.     @Test
  18.     public void findById() {
  19.         User user = db1UserMapper.findById(1L);
  20.         System.out.println(user);
  21.     }
  22. }

 

五、测试事务

我们通过一个给测试类加上事务的注解,并制定 manager。

具体代码是往数据库插入一条数据,然后强制抛出一个异常,看是否回滚

  1. package com.liuyanzhao.sens.mapper.db1;
  2. import com.liuyanzhao.sens.entity.User;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. import org.springframework.test.context.junit4.SpringRunner;
  8. import org.springframework.transaction.annotation.Transactional;
  9. import java.util.Date;
  10. /**
  11.  * @author 言曌
  12.  * @date 2020-01-03 17:48
  13.  */
  14. @SpringBootTest
  15. @RunWith(SpringRunner.class)
  16. public class Db1UserMapperTest {
  17.     @Autowired
  18.     private Db1UserMapper db1UserMapper;
  19.     /**
  20.      * 测试事务
  21.      * 如果没有插入 saysky123,说明事务有效
  22.      */
  23.     @Test
  24.     @Transactional(rollbackFor = Exception.class, transactionManager = "db1TransactionManager")
  25.     public void insert() {
  26.         User user = new User("saysky123""saysky123""987654""forest"""1new Date(), "system"new Date(), "system");
  27.         db1UserMapper.insert(user);
  28.         System.out.println(1 / 0);
  29.     }
  30. }

经过测试发现,db1 的能正常回滚,控制台并打印回滚信息

然后测试 db2,也是一样的,不会插入数据

注意:transactionManager = "db1TransactionManager" 不能写错,如果这里写反了

如 db2UserMapper.insert() 的事务 manager 指定为 db1TransactionManager,事务将无法回滚

 

 

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: