所谓多数据源,就是一个 Java EE 项目中采用了不同数据库实例中的多个库,或者同一个数据库实例中多个不同的库 。一般来说,采用 MyCa 等分 式数据库中间件是比较好的解决方案 ,这样可以把数据库读写分离、分库分表、备份等操作交给中间件去做, Java 代码只需要专注于业务即可。不过,这并不意味着无法使用 Java 代码解决类似的问题, Spring Framework 中就可以配置多数据源, Spring Boot 继承其衣钵,只不过配置方式有所变化。
一、JdbcTemplate 多数据源
JdbcTemplate 多数据源的配置是较简单的。因为 JdbcTemplate 对应一个 DataSource,开发者只需要手动提供多个 DataSource ,再手动配置 JdbcTemplate 即可。具体步骤如下。
1.1 创建数据库
创建两个数据库: test1、test2。两个库中都创建 user 表,再各预设一条数据,脚本如下:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL,
`age` tinyint(3) NULL DEFAULT NULL COMMENT '年龄',
`name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '名字',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `user` VALUES (1, 20, 'wg1');
INSERT INTO `user` VALUES (1, 20, '张三');
1.2 创建项目,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
注意这里添加的数据库连接池依赖是 druid spring-boot-starter 。 druid-spring-boot-starter 可以帮助开发者在 Spring Boot 项目中轻松集成 Druid 数据库连接池和监控。
1.3 配置数据库连接
# 数据源1
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.url=jdbc:mysql://localhost:3306/test
spring.datasource.one.username=root
spring.datasource.one.password=root123456
# 数据源2
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.url=jdbc:mysql://localhost:3306/test1
spring.datasource.two.username=root
spring.datasource.two.password=root123456
1.4 配置数据源
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
//ConfigurationProperties注解表示使用不同前缀的配置文件来创建不同的 DataSource 实例
@ConfigurationProperties("spring.datasource.one")
DataSource dataSourceOne(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.two")
DataSource dataSourceTwo(){
return DruidDataSourceBuilder.create().build();
}
}
1.5 配置 JdbcTemplate
只要引入了 spring-jdbc 的依赖 那么开发者没有提供 JdbcTernplate 实例时, Spring Boot 默认会提供 JdbcTernplate 实例。现在配置多数据源时, 由开发者自己提供 JdbcTernplate 例,代码如下:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class JdbcTemplateConfig {
@Bean
JdbcTemplate jdbcTemplateOne(@Qualifier("dataSourceOne") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
JdbcTemplate jdbcTemplateTwo(@Qualifier("dataSourceTwo") DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
JdbcTernplateConfig 中提供了两个 JdbcTernplate 实例,每个 JdbcTernplate 实例都需要提供 DataSource ,由于 Spring 容器中有两个 DataSource 实例,因此需要通过方法名查找。@Qualifier
表示查找不同名 DataSource 实例注入进来。
1.6 创建 BookController
@RestController
public class UserController {
@Resource(name = "jdbcTemplateOne")
JdbcTemplate jdbcTemplateOne;
@Autowired
@Qualifier("jdbcTemplateTwo")
JdbcTemplate jdbcTemplateTwo;
@GetMapping("userTest")
public void userTest() {
List<User> query1 = jdbcTemplateOne.query("select * from user", new BeanPropertyRowMapper<>(User.class));
List<User> query2 = jdbcTemplateTwo.query("select * from user", new BeanPropertyRowMapper<>(User.class));
System.out.println("query1--->" + query1);
System.out.println("query2--->" + query2);
}
}
在 Controller 中注入两个不同的 JdbcTemplate 有两种方式: 种是使用 @Resource 注解,并指明 name 属性 ,即按 name 进行装配,此时会根据实例名查找相应的实例注入;另一种是使用 @Autowired 注解结合 @Qualifier 注解,效果等同于使用@Resource 注解。
1.7 测试
二、MyBatis 多数据源
2.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
为了使 Mapper 映射文件不被过滤掉, 在 pom.xml 中做如下配置:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
2.2 配置数据源
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.one")
DataSource dataSourceOne(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.two")
DataSource dataSourceTwo(){
return DruidDataSourceBuilder.create().build();
}
}
2.3 创建 MyBatis 配置
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 javax.sql.DataSource;
@Configuration
@MapperScan(value = "com.weigang.mybatis.mapper1", sqlSessionFactoryRef = "sqlSessionFactoryBean1")
public class MybatisConfigOne {
@Autowired
@Qualifier(value = "dataSourceOne")
DataSource dataSourceOne;
@Bean
SqlSessionFactory sqlSessionFactoryBean1() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSourceOne);
return factoryBean.getObject();
}
@Bean
SqlSessionTemplate sqlSessionTemplate1() throws Exception {
return new SqlSessionTemplate(sqlSessionFactoryBean1());
}
}
@Configuration
@MapperScan(value = "com.weigang.mybatis.mapper2", sqlSessionFactoryRef = "sqlSessionFactoryBean2")
public class MybatisConfigTwo {
@Autowired
@Qualifier(value = "dataSourceTwo")
DataSource dataSourceTwo;
@Bean
SqlSessionFactory sqlSessionFactoryBean2() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSourceTwo);
return factoryBean.getObject();
}
@Bean
SqlSessionTemplate sqlSessionTemplate2() throws Exception {
return new SqlSessionTemplate(sqlSessionFactoryBean2());
}
}
代码解释:
- @MapperScan 注解中指定 Mapper 接口所在的位直, 同时指定 SqlSessionFactory 的实例名,则该位置下的 Mapper 将使用 Sq!SessionFactory 实例。
- 提供 SqlSessionFactory 实例, 直接接创建出来,同时 DataSource 的实例设置给 SqlSessionFactory,这里创建的 SqlSessionFactory 实例也就是@MapperScan 注解中 sqlSessionFactoryRef 指定的实例。
- 提供一个 SqlSessionTemplate 实例。 这是一个线程安全类 ,主要用来管理 MyBatis 中的 SqlSession 操作。
2.4 创建 Mapper
分别创建两个 Mapper
@Mapper
public interface HrUserMapper1 {
//获取用户列表
List<HrUser> selectAllUser();
}
@Mapper
public interface HrUserMapper2 {
//获取用户列表
List<HrUser> selectAllUser();
}
<?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.weigang.mybatis.mapper1.HrUserMapper1">
<select id="selectAllUser" resultType="com.weigang.mybatis.entity.HrUser">
select * from user
</select>
</mapper>
<?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.weigang.mybatis.mapper2.HrUserMapper2">
<select id="selectAllUser" resultType="com.weigang.mybatis.entity.HrUser">
select * from user
</select>
</mapper>
2.5 创建 Controller
@RestController
@RequestMapping("/user")
public class HrUserController
{
@Resource
private HrUserMapper1 hrUserMapper1;
@Resource
private HrUserMapper2 hrUserMapper2;
@GetMapping(value = "/getUsers")
public String getUsers(){
List<HrUser> user1 = hrUserMapper1.selectAllUser();
List<HrUser> user2 = hrUserMapper2.selectAllUser();
System.out.println("user1-->" + user1);
System.out.println("user2-->" + user2);
return "1111111111";//http://localhost:8080/user/getUsers
}
}