1. 什么是多数据源?

所谓多数据源,就是一个Java EE项目中采用了不同数据库实例中的多个库,或者同一个数据库实例中多个不同的库。一般来说,采用MyCat等分布式数据库中间件是比较好的解决方案,这样可以把数据库读写分离、分库分表、备份等操作交给中间件去做,Java代码只需要专注于业务即可。不过,这并不意味着无法使用Java代码解决类似的问题,在Spring Framework中就可以配置多数据源,Spring Boot继承其衣钵,只不过配置方式有所变化。

2. 准备数据

2.1 创建数据库和表

1.创建数据库db1和表student
CREATE DATABASE db1;
CREATE TABLE `db1`.`student` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '名称',
`schoolId` int DEFAULT NULL COMMENT '学校ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';
INSERT INTO `db1`.`student`(`name`,`schoolId`)VALUES('小明',100);
2.创建数据库db2和表test2
CREATE DATABASE db2;
CREATE TABLE `db2`.`school` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '学校id',
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '学校名称',
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='学校表';
INSERT INTO `db2`.`school`(`id`,`name`,`address`)VALUES(100,'北京大学','北京');

2.2 创建实体类

1. 新建学生实体类

新建文件: StudentModel.java
文件位置: src/main/java/com/hui/javalearn/model/db1/StudentModel.java

代码如下:

package com.hui.javalearn.model.db1;
import lombok.Data;
@Data
public class StudentModel {
private Integer id;
private String name;
private Integer schoolId;
}
2. 新建学校实体类

新建文件: SchoolModel.java
文件位置: src/main/java/com/hui/javalearn/model/db2/SchoolModel.java

代码如下:

package com.hui.javalearn.model.db2;
import lombok.Data;
@Data
public class SchoolModel {
private Integer id;
private String name;
private String address;
}

2.3 创建学生Dao和Mapper文件

1.创建学生Dao

新建文件: StudentDao.java
文件位置: src/main/java/com/hui/javalearn/dao/db1/StudentDao.java

代码如下:

package com.hui.javalearn.dao.db1;
import com.hui.javalearn.model.db1.StudentModel;
public interface StudentDao {
StudentModel getStudent(Integer id);
}
2.创建学生Mapper

新建文件: StudentMapper.xml
文件位置: java-learn/src/main/resources/mapper/db1/StudentMapper.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.hui.javalearn.dao.db1.StudentDao">
<select id="getStudent" resultType="com.hui.javalearn.model.db1.StudentModel" parameterType="java.lang.Integer">
select * from student where id = #{id,jdbcType=INTEGER}
</select>
</mapper>

2.4 创建学校Dao和Mapper文件

1.创建学校Dao

新建文件: SchoolDao.java
文件位置: src/main/java/com/hui/javalearn/dao/db2/SchoolDao.java

代码如下:

package com.hui.javalearn.dao.db2;
import com.hui.javalearn.model.db2.SchoolModel;
public interface SchoolDao {
SchoolModel getSchoolById(Integer id);
}
2.创建学校Mapper

新建文件: SchoolMapper.xml
文件位置: java-learn/src/main/resources/mapper/db2/SchoolMapper.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.hui.javalearn.dao.db2.SchoolDao">
<select id="getSchoolById" parameterType="integer" resultType="com.hui.javalearn.model.db2.SchoolModel">
select * from school where id = #{id,jdbcType=INTEGER}
</select>
</mapper>

3.引入依赖

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>

4.配置数据库连接

修改配置文件: application.yml

文件位置: src/main/resources/application.yml

代码如下:

server:
port: 8080
spring:
datasource:
# 配置数据库连接
one:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
jdbc-url: jdbc:mysql://127.0.0.1:3306/db1?useSSL=false
two:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
jdbc-url: jdbc:mysql://127.0.0.1:3306/db2?useSSL=false

# sql语句输入到终端
logging:
level:
com.hui.javalearn.dao: debug

5.配置数据源

5.1 配置名为One数据源

新建文件: MybatisDataSourceOneConfig.java
文件位置: src/main/java/com/hui/javalearn/config/MybatisDataSourceOneConfig.java

代码如下:

package com.hui.javalearn.config;

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.Qualifier;
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 javax.sql.DataSource;

@Configuration

@MapperScan(basePackages = "com.hui.javalearn.dao.db1", sqlSessionFactoryRef = "testOneSqlSessionFactory")
public class MybatisDataSourceOneConfig {
/**
* 设置one的数据库连接配置
*/
@Bean(name = "dbOne")
@ConfigurationProperties(prefix = "spring.datasource.one")
public DataSource testOneDataSource(){
return DataSourceBuilder.create().build();
}

/**
* 创建SqlSessionFactory的实例,这里创建的实例就是sqlSessionFactoryRef参数指定的实例
* @param dbOneDataSource
* throws Exception
*/
@Bean(name = "testOneSqlSessionFactory")
public SqlSessionFactory testOneSqlSessionFactory(@Qualifier("dbOne") DataSource dbOneDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dbOneDataSource);
Resource[] mapperXmlResources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/db1/*.xml");
sqlSessionFactoryBean.setMapperLocations(mapperXmlResources);
return sqlSessionFactoryBean.getObject();
}

/**
* 提供一个SqlSessionTemplate实例。这是一个线程安全类,主要用来管理MyBatis中的SqlSession操作
* @param sqlSessionFactory
* return SqlSessionTemplate
*/
@Bean
public SqlSessionTemplate testOneSqlSessionTemplate(
@Qualifier("testOneSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
}

5.2 配置名为Two数据源

新建文件: MybatisDataSourceTwoConfig.java
文件位置: src/main/java/com/hui/javalearn/config/MybatisDataSourceTwoConfig.java

代码如下:

package com.hui.javalearn.config;
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.Qualifier;
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 javax.sql.DataSource;
@Configuration
// 指定扫描文件
@MapperScan(basePackages = "com.hui.javalearn.dao.db2", sqlSessionFactoryRef = "testTwoSqlSessionFactory")
public class MybatisDataSourceTwoConfig {
/**
* 设置db-one的数据库连接配置
*/
@Bean(name = "dbTwo")
@ConfigurationProperties(prefix = "spring.datasource.two")
public DataSource testTwoDataSource(){
return DataSourceBuilder.create().build();
}

/**
* 创建SqlSessionFactory的实例,这里创建的实例就是sqlSessionFactoryRef参数指定的实例
* @param dbOneDataSource
* throws Exception
*/
@Bean(name = "testTwoSqlSessionFactory")
public SqlSessionFactory testTwoSqlSessionFactory(@Qualifier("dbTwo") DataSource dbOneDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dbOneDataSource);
Resource[] mapperXmlResources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/db2/*.xml");
sqlSessionFactoryBean.setMapperLocations(mapperXmlResources);
return sqlSessionFactoryBean.getObject();
}

/**
* 提供一个SqlSessionTemplate实例。这是一个线程安全类,主要用来管理MyBatis中的SqlSession操作
* @param sqlSessionFactory
* return SqlSessionTemplate
*/
@Bean
public SqlSessionTemplate testTwoSqlSessionTemplate(
@Qualifier("testTwoSqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
}

6.文件位置总览

image-20200929174956101

7.测试

新建测试文件: TestMultiDataSource.java

文件位置: src/test/java/com/hui/javalearn/TestMultiDataSource.java

package com.hui.javalearn;

import com.hui.javalearn.dao.db1.StudentDao;
import com.hui.javalearn.dao.db2.SchoolDao;
import com.hui.javalearn.model.db1.StudentModel;
import com.hui.javalearn.model.db2.SchoolModel;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
public class TestMultiDataSource {
@Resource
private StudentDao testOneDao;
@Resource
private SchoolDao schoolDao;

@Test
void run(){
// 查询学生id为1的学生信息和对应的学校信息
StudentModel studentModel = testOneDao.getStudent(1);
Integer schoolId = studentModel.getSchoolId();
SchoolModel schoolModel = schoolDao.getSchoolById(schoolId);
String res = "学生ID: " + studentModel.getId()
+ " 名字: " + studentModel.getName()
+ " 学校: " + schoolModel.getName()
+ " 学校地址: " + schoolModel.getAddress();
System.out.println(res);
}
}
// 输出: 学生ID: 1 名字: 小明 学校: 北京大学 学校地址: 北京