在企业级项目中,系统往往会越来越庞大,场景也各有不同,对数据库的要求也不尽相同。
比如我们有一个项目原来是用 Oracle 数据库,后台需要做一些地图相关的功能,需要地理运算,这个时候有一个比较好的选择就是 PostgreSQL,因为其支持一种空间运算的扩展,即 PostGis,所以我们地图项目基本都是用 PostgreSQL的。但是我们又有一些数据是在 Oracle 库里,在没有完全拆分之前,还是需要查询 Oracle 库的。
所以在早先时候,就必须要做到多数据源配置。
本文介绍一些具体实现。
这里以 Db1 (MySQL)和 Db2 (PostgreSQL) 演示配置并测试。
完整代码地址:https://github.com/saysky/sensboot
分别是 MySQL 和 PostgreSQL 的依赖
SpringBoot 版本是 2.0.5.RELEASE,完整代码可以访问上面 GitHub 地址
1.application.yml
不需要其他额外数据源配置,数据库连接池等其他配置我们都可以通过在自定义配置类里设置
2.配置属性类
既然我们上面自定义了配置,那么我们就需要读取它.
注意 prefix 指的是 yml 文件配置的前缀,后面的部分要和类属性名保持一致
Db1DataSourceProperties.java
Db2DataSourceProperties.java
3.数据源配置
Db1DataSourceConfig.java
Db2DataSourceConfig.java
这两个配置文件是相似的,只是指定不同的 mapper 扫描位置和 xml 文件位置。同时,我们自定义了 sqlSessionTemplate 和 transactionManager 的 bean 名称。需要用的时候指定对应的即可,下面会介绍事务的时候说明。
我们这里还是用 sensboot 那个项目测试。
在 mapper 包下创建 db1 和 db2,用于存放不同数据源的 mapper 类。
在 resources/mapper 下也创建对应的 db1 和 db2,用于存放不同数据源的 mapper xml 文件。
本地主要以 Db1UserMapper 和 Db2UserMapper 测试。
数据库两表结构也是一样的,对应实体类都是 User。
完整代码大家可以自己去 GitHub 上克隆。
这里贴一下 Db1UserMapper.xml 代码
其和 Db2UserMapper.xml 代码除了包名和类名不同都一样
然后贴一下对应的 xml,两个 xml 除了 namespace 写的不一样,几乎一致。
创建表的 sql 这里就不贴了吧
我们往两个数据库分别插入一条 id 为1的数据,内容不同
然后通过单元测试调用 db1UserMapper.findById(1) 和 db2UserMapper.findById(1)
分别都能返回对应结果,说明配置成功
db1 的测试类 如下
我们通过一个给测试类加上事务的注解,并制定 manager。
具体代码是往数据库插入一条数据,然后强制抛出一个异常,看是否回滚
经过测试发现,db1 的能正常回滚,控制台并打印回滚信息
然后测试 db2,也是一样的,不会插入数据
注意:transactionManager = "db1TransactionManager" 不能写错,如果这里写反了
如 db2UserMapper.insert() 的事务 manager 指定为 db1TransactionManager,事务将无法回滚
比如我们有一个项目原来是用 Oracle 数据库,后台需要做一些地图相关的功能,需要地理运算,这个时候有一个比较好的选择就是 PostgreSQL,因为其支持一种空间运算的扩展,即 PostGis,所以我们地图项目基本都是用 PostgreSQL的。但是我们又有一些数据是在 Oracle 库里,在没有完全拆分之前,还是需要查询 Oracle 库的。
所以在早先时候,就必须要做到多数据源配置。
本文介绍一些具体实现。
这里以 Db1 (MySQL)和 Db2 (PostgreSQL) 演示配置并测试。
完整代码地址:https://github.com/saysky/sensboot
一、Maven 依赖
分别是 MySQL 和 PostgreSQL 的依赖
- <!-- mysql-->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <scope>runtime</scope>
- </dependency>
- <!-- postgrepsql-->
- <dependency>
- <groupId>org.postgresql</groupId>
- <artifactId>postgresql</artifactId>
- <scope>runtime</scope>
- </dependency>
SpringBoot 版本是 2.0.5.RELEASE,完整代码可以访问上面 GitHub 地址
二、核心配置部分
1.application.yml
- server:
- port: 8080
- # mybatis配置
- mybatis:
- configuration:
- map-underscore-to-camel-case: true
- cache-enabled: true
- db1:
- driverClassName: com.mysql.jdbc.Driver
- url: jdbc:mysql://127.0.0.1:3306/sensboot?characterEncoding=utf8&useSSL=false&allowMultiQueries=true
- username: root
- password: 123456
- initialSize: 5
- minIdle: 5
- maxActive: 50
- db2:
- driverClassName: org.postgresql.Driver
- url: jdbc:postgresql://127.0.0.1:5432/sensboot
- username: liuyanzhao
- password:
- platform: postgres
- initialSize: 5
- minIdle: 5
- maxActive: 50
不需要其他额外数据源配置,数据库连接池等其他配置我们都可以通过在自定义配置类里设置
2.配置属性类
既然我们上面自定义了配置,那么我们就需要读取它.
注意 prefix 指的是 yml 文件配置的前缀,后面的部分要和类属性名保持一致
Db1DataSourceProperties.java
- package com.liuyanzhao.sens.config;
- import lombok.Data;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.stereotype.Component;
- import java.io.Serializable;
- /**
- * @author 言曌
- * @date 2020-01-03 17:03
- */
- @Component
- @Data
- @ConfigurationProperties(prefix = "db1")
- public class Db1DataSourceProperties implements Serializable {
- private String driverClassName;
- private String url;
- private String username;
- private String password;
- private Integer initialSize;
- private Integer minIdle;
- private Integer maxActive;
- }
Db2DataSourceProperties.java
- package com.liuyanzhao.sens.config;
- import lombok.Data;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.stereotype.Component;
- import java.io.Serializable;
- /**
- * @author 言曌
- * @date 2020-01-03 17:03
- */
- @Component
- @Data
- @ConfigurationProperties(prefix = "db2")
- public class Db2DataSourceProperties implements Serializable {
- private String driverClassName;
- private String url;
- private String username;
- private String password;
- private Integer initialSize;
- private Integer minIdle;
- private Integer maxActive;
- private String platform;
- }
3.数据源配置
Db1DataSourceConfig.java
- package com.liuyanzhao.sens.config;
- import com.alibaba.druid.pool.DruidDataSource;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.mybatis.spring.SqlSessionFactoryBean;
- import org.mybatis.spring.SqlSessionTemplate;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.io.Resource;
- import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
- import org.springframework.core.io.support.ResourcePatternResolver;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
- import javax.sql.DataSource;
- /**
- * @author 言曌
- * @date 2020-01-03 17:03
- */
- @Configuration
- @MapperScan(basePackages = "com.liuyanzhao.sens.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
- public class Db1DataSourceConfig {
- @Autowired
- private Db1DataSourceProperties db1DataSourceProperties;
- @Bean(name = "db1DataSource")
- public DataSource dataSource() {
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName(db1DataSourceProperties.getDriverClassName());
- dataSource.setUrl(db1DataSourceProperties.getUrl());
- dataSource.setUsername(db1DataSourceProperties.getUsername());
- dataSource.setPassword(db1DataSourceProperties.getPassword());
- dataSource.setInitialSize(db1DataSourceProperties.getInitialSize());
- dataSource.setMinIdle(db1DataSourceProperties.getMinIdle());
- dataSource.setMaxActive(db1DataSourceProperties.getMaxActive());
- return dataSource;
- }
- @Bean(name = "db1SqlSessionFactory")
- public SqlSessionFactory sqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource,
- @Qualifier("camelCaseConfiguration") org.apache.ibatis.session.Configuration configuration) throws Exception {
- SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
- sqlSessionFactoryBean.setDataSource(dataSource);
- sqlSessionFactoryBean.setConfiguration(configuration);
- ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
- Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/db1/*Mapper.xml");
- sqlSessionFactoryBean.setMapperLocations(resources);
- return sqlSessionFactoryBean.getObject();
- }
- @Bean(name = "db1SqlSessionTemplate")
- public SqlSessionTemplate sqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
- return new SqlSessionTemplate(sqlSessionFactory);
- }
- @Bean(name = "db1TransactionManager")
- public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
- return new DataSourceTransactionManager(dataSource);
- }
- @Bean(name = "camelCaseConfiguration")
- @ConfigurationProperties(prefix = "mybatis.configuration")
- public org.apache.ibatis.session.Configuration configuration() {
- return new org.apache.ibatis.session.Configuration();
- }
- }
Db2DataSourceConfig.java
- package com.liuyanzhao.sens.config;
- import com.alibaba.druid.pool.DruidDataSource;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.mybatis.spring.SqlSessionFactoryBean;
- import org.mybatis.spring.SqlSessionTemplate;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Primary;
- import org.springframework.core.io.Resource;
- import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
- import org.springframework.core.io.support.ResourcePatternResolver;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
- import javax.sql.DataSource;
- /**
- * @author 言曌
- * @date 2020-01-03 17:03
- */
- @Configuration
- @MapperScan(basePackages = "com.liuyanzhao.sens.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory")
- public class Db2DataSourceConfig {
- @Autowired
- private Db2DataSourceProperties db2DataSourceProperties;
- @Primary
- @Bean(name = "db2DataSource")
- public DataSource dataSource() {
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName(db2DataSourceProperties.getDriverClassName());
- dataSource.setUrl(db2DataSourceProperties.getUrl());
- dataSource.setUsername(db2DataSourceProperties.getUsername());
- dataSource.setPassword(db2DataSourceProperties.getPassword());
- dataSource.setInitialSize(db2DataSourceProperties.getInitialSize());
- dataSource.setMinIdle(db2DataSourceProperties.getMinIdle());
- dataSource.setMaxActive(db2DataSourceProperties.getMaxActive());
- return dataSource;
- }
- @Bean(name = "db2SqlSessionFactory")
- public SqlSessionFactory sqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource,
- @Qualifier("camelCaseConfiguration") org.apache.ibatis.session.Configuration configuration) throws Exception {
- SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
- sqlSessionFactoryBean.setDataSource(dataSource);
- sqlSessionFactoryBean.setConfiguration(configuration);
- ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
- Resource[] resources = resourcePatternResolver.getResources("classpath*:mapper/db2/*Mapper.xml");
- sqlSessionFactoryBean.setMapperLocations(resources);
- return sqlSessionFactoryBean.getObject();
- }
- @Bean(name = "db2SqlSessionTemplate")
- public SqlSessionTemplate sqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
- return new SqlSessionTemplate(sqlSessionFactory);
- }
- @Bean(name = "db2TransactionManager")
- public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
- return new DataSourceTransactionManager(dataSource);
- }
- }
这两个配置文件是相似的,只是指定不同的 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 代码除了包名和类名不同都一样
- package com.liuyanzhao.sens.mapper.db1;
- import com.liuyanzhao.sens.entity.User;
- /**
- * Db1 数据源的 mapper 测试
- * @author 言曌
- * @date 2020-01-03 17:39
- */
- public interface Db1UserMapper {
- /**
- * 根据ID获得用户
- * @param id
- * @return
- */
- User findById(Long id);
- /**
- * 添加
- * @param user
- * @return
- */
- Integer insert(User user);
- }
然后贴一下对应的 xml,两个 xml 除了 namespace 写的不一样,几乎一致。
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.liuyanzhao.sens.mapper.db2.Db2UserMapper">
- <resultMap id="BaseResultMap" type="com.liuyanzhao.sens.entity.User">
- <id column="id" property="id"/>
- <result column="username" property="username"/>
- <result column="nickname" property="nickname"/>
- <result column="password" property="password"/>
- <result column="email" property="email"/>
- <result column="avatar" property="avatar"/>
- <result column="status" property="status"/>
- <result column="created_time" property="createdTime"/>
- <result column="created_by" property="createdBy"/>
- <result column="updated_time" property="updatedTime"/>
- <result column="updated_by" property="updatedBy"/>
- </resultMap>
- <sql id="all_columns">
- id, username, nickname, password, email, avatar, status,
- created_time, created_by, updated_time, updated_by
- </sql>
- <insert id="insert">
- INSERT INTO t_user
- (
- username, nickname, password, email,
- avatar, status, created_time, created_by,
- updated_time, updated_by
- )
- VALUES
- (
- #{username}, #{nickname}, #{password}, #{email},
- #{avatar}, #{status}, #{createdTime}, #{createdBy},
- #{updatedTime}, #{updatedBy}
- )
- </insert>
- <select id="findById" resultMap="BaseResultMap">
- SELECT
- <include refid="all_columns"/>
- FROM
- t_user
- WHERE id = #{id}
- </select>
- </mapper>
创建表的 sql 这里就不贴了吧
四、测试数据源连接和查询
我们往两个数据库分别插入一条 id 为1的数据,内容不同
然后通过单元测试调用 db1UserMapper.findById(1) 和 db2UserMapper.findById(1)
分别都能返回对应结果,说明配置成功
db1 的测试类 如下
- package com.liuyanzhao.sens.mapper.db1;
- import com.liuyanzhao.sens.entity.User;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
- /**
- * @author 言曌
- * @date 2020-01-03 17:48
- */
- @SpringBootTest
- @RunWith(SpringRunner.class)
- public class Db1UserMapperTest {
- @Autowired
- private Db1UserMapper db1UserMapper;
- @Test
- public void findById() {
- User user = db1UserMapper.findById(1L);
- System.out.println(user);
- }
- }
五、测试事务
我们通过一个给测试类加上事务的注解,并制定 manager。
具体代码是往数据库插入一条数据,然后强制抛出一个异常,看是否回滚
- package com.liuyanzhao.sens.mapper.db1;
- import com.liuyanzhao.sens.entity.User;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
- import org.springframework.transaction.annotation.Transactional;
- import java.util.Date;
- /**
- * @author 言曌
- * @date 2020-01-03 17:48
- */
- @SpringBootTest
- @RunWith(SpringRunner.class)
- public class Db1UserMapperTest {
- @Autowired
- private Db1UserMapper db1UserMapper;
- /**
- * 测试事务
- * 如果没有插入 saysky123,说明事务有效
- */
- @Test
- @Transactional(rollbackFor = Exception.class, transactionManager = "db1TransactionManager")
- public void insert() {
- User user = new User("saysky123", "saysky123", "987654", "forest", "", 1, new Date(), "system", new Date(), "system");
- db1UserMapper.insert(user);
- System.out.println(1 / 0);
- }
- }
经过测试发现,db1 的能正常回滚,控制台并打印回滚信息
然后测试 db2,也是一样的,不会插入数据
注意:transactionManager = "db1TransactionManager" 不能写错,如果这里写反了
如 db2UserMapper.insert() 的事务 manager 指定为 db1TransactionManager,事务将无法回滚
您可以选择一种方式赞助本站
支付宝扫一扫赞助
微信钱包扫描赞助
赏