SpringBoot+MyBatis-Plus+MySQL多数据源的使用

前言

近期的工作中协助同事解决一个SpringBoot+MyBatis-Plus+MySQL多数据源的问题,借此记录一下。

业务背景:从一个数据库中获取某个字段为空的的所有记录,将这些记录集作为一个接口的请求参数调用接口将接口返回值再插入到对应的数据库中

从业务上来说,这个场景只需要单纯的使用多数据源即可。并不牵涉到主从数据库的概念。

本片文章通过一个简单的案例来描述一下多数据源的配置及使用。

实现的功能:查询数据库1中的一条数据,将其插入到数据库2中

准备SQL

-- 创建数据库db1,db2
CREATE DATABASE `db1` DEFAULT CHARACTER SET utf8;
CREATE DATABASE `db2` DEFAULT CHARACTER SET utf8;

-- 在db1库和db2库中分别创建user表
use db1;
CREATE TABLE `user` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `pwd` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

use db2;
CREATE TABLE `user` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `pwd` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

项目结构

在这里插入图片描述

实现步骤

1.引入项目所需依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.48</version>
</dependency>

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.4.2</version>
</dependency>

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.4</version>
</dependency>

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

2.yml配置

单纯的多数据源配置如下所示:

server:
  port: 8085
spring:
  datasource:
    db1:
      druid:
        driver-class-name: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/db1?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=true
        username: root
        password: root
        filters: wall,mergeStat
        initial-size: 5
        min-idle: 5
        max-active: 20
        max-wait: 60000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        validation-query: select 1 from dual
        test-while-idle: true
        pool-prepared-statements: true
        max-pool-prepared-statement-per-connection-size: 20
    db2:
      druid:
        driver-class-name: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/db2?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=true
        username: root
        password: root
        filters: wall,mergeStat
        initial-size: 5
        min-idle: 5
        max-active: 20
        max-wait: 60000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        validation-query: select 1 from dual
        test-while-idle: true
        pool-prepared-statements: true
        max-pool-prepared-statement-per-connection-size: 20

# xml文件映射路径
mybatis:
  db1:
    mapper-locations: classpath:mapper/db1/*.xml
  db2:
    mapper-locations: classpath:mapper/db2/*.xml

3.编写配置类,加载数据源及相关配置。

加载db1数据库

package com.scholartang.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Author ScholarTang
 * @Date 2021/1/25 7:38 下午
 * @Desc 数据库1配置类
 */
@Configuration
@MapperScan(basePackages = "com.scholartang.mapper.db1", sqlSessionTemplateRef  = "db1SqlSessionTemplate")
public class DataSourceDb1Config {
    
    

    /**
     * 获取映射文件所在的路径
     */
    @Value("${mybatis.db1.mapper-locations}")
    private String db1tMapperPath;

    /**
     * 数据源加载
     * @return
     */
    @Bean(name = "db1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1.druid")
    public DataSource test1DataSource() {
    
    
        return DataSourceBuilder.create().build();
    }

    /**
     * 注入SqlSessionFactory,指定数据源和映射文件路径
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name = "db1SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
    
    
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(db1tMapperPath);
        bean.setMapperLocations(resources);
        return bean.getObject();
    }

    /**
     * 注入DataSourceTransactionManager事物管理器
     * @param dataSource
     * @return
     */
    @Bean(name = "db1TransactionManager")
    public DataSourceTransactionManager testTransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
    
    
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     *
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "db1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
    
    
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

加载db2数据库

package com.scholartang.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Author ScholarTang
 * @Date 2021/1/25 7:38 下午
 * @Desc 数据库1配置类
 */
@Configuration
@MapperScan(basePackages = "com.scholartang.mapper.db2", sqlSessionTemplateRef  = "db2SqlSessionTemplate")
public class DataSourceDb2Config {
    
    

    /**
     * 获取映射文件所在的路径
     */
    @Value("${mybatis.db2.mapper-locations}")
    private String db2tMapperPath;

    /**
     * 数据源加载
     * @return
     */
    @Bean(name = "db2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db2.druid")
    public DataSource test1DataSource() {
    
    
        return DataSourceBuilder.create().build();
    }

    /**
     * 注入SqlSessionFactory,指定数据源和映射文件路径
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean(name = "db2SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
    
    
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(db2tMapperPath);
        bean.setMapperLocations(resources);
        return bean.getObject();
    }

    /**
     * 注入DataSourceTransactionManager事物管理器
     * @param dataSource
     * @return
     */
    @Bean(name = "db2TransactionManager")
    public DataSourceTransactionManager testTransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
    
    
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     *
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "db2SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
    
    
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

4.编写数据表对应的实体类(我这里db1、db2用的是完全相同的两张表所以共用同一个JavaBean)

package com.scholartang.model.po;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @Author ScholarTang
 * @Date 2021/1/26 5:21 下午
 * @Desc 用户表对应的实体类
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@TableName(value = "user")
public class User {
    
    

    @TableId(value = "id",type = IdType.AUTO)
    private Integer uid;

    @TableField(value = "username")
    private String username;

    @TableField(value = "pwd")
    private String pwd;
}

5.编写Mapper接口提供数据层支持(上边加载数据源的时候配置类不同数据源对应的mapper所在位置,所有用的时候mapper会被指向性的到某个库)

db1库对应的mapper层

package com.scholartang.mapper.db1;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.scholartang.model.po.User;
import org.apache.ibatis.annotations.Param;

/**
 * @Author ScholarTang
 * @Date 2021/1/26 5:23 下午
 * @Desc 数据库db1-提供用户数据层支持
 */
public interface UserToDb1Mapper extends BaseMapper<User> {
    
    
    User selectUserById(@Param("id") int id);
}

db2库对应的mapper层

package com.scholartang.mapper.db2;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.scholartang.model.po.User;

/**
 * @Author ScholarTang
 * @Date 2021/1/26 5:23 下午
 * @Desc 数据库db2-提供用户数据层支持
 */
public interface UserToDb2Mapper extends BaseMapper<User> {
    
    
}

5.编写mapper对应的xml

UserToDb1Mapper.xml

<?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.scholartang.mapper.db1.UserToDb1Mapper">
    <!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="com.scholartang.model.po.User" id="user">
        <result property="uid" column="uid"/>
        <result property="username" column="username"/>
        <result property="pwd" column="pwd"/>
    </resultMap>

    <select id="mySelectById" resultType="com.scholartang.model.po.User">
        select * from `user` where id = 1;
    </select>

    <select id="selectUserById" resultMap="user">
        select * from user where uid = #{id}
    </select>
</mapper>

UserToDb2Mapper.xml

<?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.scholartang.mapper.db2.UserToDb2Mapper">
    <!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="com.scholartang.model.po.User" id="user">
        <result property="uid" column="uid"/>
        <result property="username" column="username"/>
        <result property="pwd" column="pwd"/>
    </resultMap>
</mapper>

6.编写服务层

本文作为简单实现,只提供了一个供处理层调研的service类,在该类中通过注入mapper层的接口调用接口中的方法来实现交互。

package com.scholartang.service;

import com.scholartang.mapper.db1.UserToDb1Mapper;
import com.scholartang.mapper.db2.UserToDb2Mapper;
import com.scholartang.model.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author ScholarTang
 * @Date 2021/1/26 5:37 下午
 * @Desc 提供测试服务
 */

@Service
public class MyTestService {
    
    

    @Autowired
    private UserToDb1Mapper userToDb1Mapper;

    @Autowired
    private UserToDb2Mapper userToDb2Mapper;

    public void syncDb1ToDb2() {
    
    
        User user = userToDb1Mapper.selectUserById(1);
        saveData(user);
    }

    @Transactional(value = "db2TransactionManager")
    public void saveData(User user) {
    
    
        user.setUid(null);
        userToDb2Mapper.insert(user);
    }
}

7.编写测试类,测试

package com.scholartang;

import com.scholartang.service.MyTestService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MultipleDataSourcesDemoApplicationTests {
    
    
    @Autowired
    private MyTestService service;
  
    @Test
    void contextLoads() {
    
    
        service.syncDb1ToDb2();
    }
}

8.结果

测试满足预期,从db1库中的user表查询出一条数据,并将该数据插入到了db2库中的user表中。

9.补充

开始的时候我在加载数据源是,数据源的url并没有指明是否进行SSL连接出现了事物问题。但是我加上后事物失效问题得到了解决。对此我还不大能想明白这个问题,望浏览的大佬多多指点。

猜你喜欢

转载自blog.csdn.net/ScholarTang/article/details/113186195