共计 6066 个字符,预计需要花费 16 分钟才能阅读完成。
前两天,我们已经介绍了关于 JdbcTemplate 的多数据源配置以及 Spring Data JPA 的多数据源配置,接下来具体说说使用 MyBatis 时候的多数据源场景该如何配置。
添加多数据源的配置
先在 Spring Boot 的配置文件 application.properties 中设置两个你要链接的数据库配置,比如这样:
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1 | |
spring.datasource.primary.username=root | |
spring.datasource.primary.password=123456 | |
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver | |
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2 | |
spring.datasource.secondary.username=root | |
spring.datasource.secondary.password=123456 | |
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver |
说明与注意:
- 多数据源配置的时候,与单数据源不同点在于 spring.datasource 之后多设置一个数据源名称 primary 和 secondary 来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。
- 数据源连接配置 2.x 和 1.x 的配置项是有区别的:2.x 使用 spring.datasource.secondary.jdbc-url,而 1.x 版本使用 spring.datasource.secondary.url。如果你在配置的时候发生了这个报错 java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是这个配置项的问题。
- 可以看到,不论使用哪一种数据访问框架,对于数据源的配置都是一样的。
初始化数据源与 MyBatis 配置
完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的 MyBatis 配置。
这里我们继续将数据源与框架配置做拆分处理:
- 单独建一个多数据源的配置类,比如下面这样:
@Configuration | |
public class DataSourceConfiguration { | |
@Primary | |
@Bean | |
@ConfigurationProperties(prefix = "spring.datasource.primary") | |
public DataSource primaryDataSource() {return DataSourceBuilder.create().build();} | |
@Bean | |
@ConfigurationProperties(prefix = "spring.datasource.secondary") | |
public DataSource secondaryDataSource() {return DataSourceBuilder.create().build();} | |
} |
可以看到内容跟 JdbcTemplate、Spring Data JPA 的时候是一模一样的。通过 @ConfigurationProperties
可以知道这两个数据源分别加载了 spring.datasource.primary.*
和spring.datasource.secondary.*
的配置。@Primary
注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个 Bean 真正差异部分在下面的 JPA 配置上。
- 分别创建两个数据源的 MyBatis 配置。
Primary 数据源的 JPA 配置:
@Configuration | |
@MapperScan( | |
basePackages = "com.didispace.chapter39.p", | |
sqlSessionFactoryRef = "sqlSessionFactoryPrimary", | |
sqlSessionTemplateRef = "sqlSessionTemplatePrimary") | |
public class PrimaryConfig { | |
private DataSource primaryDataSource; | |
public PrimaryConfig(@Qualifier("primaryDataSource") DataSource primaryDataSource) {this.primaryDataSource = primaryDataSource;} | |
@Bean | |
public SqlSessionFactory sqlSessionFactoryPrimary() throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); | |
bean.setDataSource(primaryDataSource); | |
return bean.getObject();} | |
@Bean | |
public SqlSessionTemplate sqlSessionTemplatePrimary() throws Exception {return new SqlSessionTemplate(sqlSessionFactoryPrimary()); | |
} | |
} |
Secondary 数据源的 JPA 配置:
@Configuration | |
@MapperScan( | |
basePackages = "com.didispace.chapter39.s", | |
sqlSessionFactoryRef = "sqlSessionFactorySecondary", | |
sqlSessionTemplateRef = "sqlSessionTemplateSecondary") | |
public class SecondaryConfig { | |
private DataSource secondaryDataSource; | |
public SecondaryConfig(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {this.secondaryDataSource = secondaryDataSource;} | |
@Bean | |
public SqlSessionFactory sqlSessionFactorySecondary() throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); | |
bean.setDataSource(secondaryDataSource); | |
return bean.getObject();} | |
@Bean | |
public SqlSessionTemplate sqlSessionTemplateSecondary() throws Exception {return new SqlSessionTemplate(sqlSessionFactorySecondary()); | |
} | |
} |
说明与注意:
- 配置类上使用
@MapperScan
注解来指定当前数据源下定义的 Entity 和 Mapper 的包路径;另外需要指定 sqlSessionFactory 和 sqlSessionTemplate,这两个具体实现在该配置类中类中初始化。 - 配置类的构造函数中,通过
@Qualifier
注解来指定具体要用哪个数据源,其名字对应在DataSourceConfiguration
配置类中的数据源定义的函数名。 - 配置类中定义 SqlSessionFactory 和 SqlSessionTemplate 的实现,注意具体使用的数据源正确(如果使用这里的演示代码,只要第二步没问题就不需要修改)。
上一篇介绍 JPA 的时候,因为之前介绍 JPA 的使用时候,说过实体和 Repository 定义的方法,所以省略了 User 和 Repository 的定义代码,但是还是有读者问怎么没有这个,其实都有说明,仓库代码里也都是有的。未避免再问这样的问题,所以这里就贴一下吧。
根据上面 Primary 数据源的定义,在 com.didispace.chapter39.p
包下,定义 Primary 数据源要用的实体和数据访问对象,比如下面这样:
@Data | |
@NoArgsConstructor | |
public class UserPrimary { | |
private Long id; | |
private String name; | |
private Integer age; | |
public UserPrimary(String name, Integer age) { | |
this.name = name; | |
this.age = age; | |
} | |
} | |
public interface UserMapperPrimary {@Select("SELECT * FROM USER WHERE NAME = #{name}") | |
UserPrimary findByName(@Param("name") String name); | |
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})") | |
int insert(@Param("name") String name, @Param("age") Integer age); | |
@Delete("DELETE FROM USER") | |
int deleteAll();} |
根据上面 Secondary 数据源的定义,在 com.didispace.chapter39.s
包下,定义 Secondary 数据源要用的实体和数据访问对象,比如下面这样:
@Data | |
@NoArgsConstructor | |
public class UserSecondary { | |
private Long id; | |
private String name; | |
private Integer age; | |
public UserSecondary(String name, Integer age) { | |
this.name = name; | |
this.age = age; | |
} | |
} | |
public interface UserMapperSecondary {@Select("SELECT * FROM USER WHERE NAME = #{name}") | |
UserSecondary findByName(@Param("name") String name); | |
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})") | |
int insert(@Param("name") String name, @Param("age") Integer age); | |
@Delete("DELETE FROM USER") | |
int deleteAll();} |
测试验证
完成了上面之后,我们就可以写个测试类来尝试一下上面的多数据源配置是否正确了,先来设计一下验证思路:
- 往 Primary 数据源插入一条数据
- 从 Primary 数据源查询刚才插入的数据,配置正确就可以查询到
- 从 Secondary 数据源查询刚才插入的数据,配置正确应该是查询不到的
- 往 Secondary 数据源插入一条数据
- 从 Primary 数据源查询刚才插入的数据,配置正确应该是查询不到的
- 从 Secondary 数据源查询刚才插入的数据,配置正确就可以查询到
具体实现如下:
@Slf4j | |
@RunWith(SpringRunner.class) | |
@SpringBootTest | |
@Transactional | |
public class Chapter39ApplicationTests { | |
@Autowired | |
private UserMapperPrimary userMapperPrimary; | |
@Autowired | |
private UserMapperSecondary userMapperSecondary; | |
@Before | |
public void setUp() { | |
// 清空测试表,保证每次结果一样 | |
userMapperPrimary.deleteAll(); | |
userMapperSecondary.deleteAll();} | |
@Test | |
public void test() throws Exception { | |
// 往 Primary 数据源插入一条数据 | |
userMapperPrimary.insert("AAA", 20); | |
// 从 Primary 数据源查询刚才插入的数据,配置正确就可以查询到 | |
UserPrimary userPrimary = userMapperPrimary.findByName("AAA"); | |
Assert.assertEquals(20, userPrimary.getAge().intValue()); | |
// 从 Secondary 数据源查询刚才插入的数据,配置正确应该是查询不到的 | |
UserSecondary userSecondary = userMapperSecondary.findByName("AAA"); | |
Assert.assertNull(userSecondary); | |
// 往 Secondary 数据源插入一条数据 | |
userMapperSecondary.insert("BBB", 20); | |
// 从 Primary 数据源查询刚才插入的数据,配置正确应该是查询不到的 | |
userPrimary = userMapperPrimary.findByName("BBB"); | |
Assert.assertNull(userPrimary); | |
// 从 Secondary 数据源查询刚才插入的数据,配置正确就可以查询到 | |
userSecondary = userMapperSecondary.findByName("BBB"); | |
Assert.assertEquals(20, userSecondary.getAge().intValue()); | |
} | |
} |
代码示例
本文的相关例子可以查看下面仓库中的 chapter3-9
目录:
- Github:https://github.com/dyc87112/SpringBoot-Learning/
- Gitee:https://gitee.com/didispace/SpringBoot-Learning/
如果您觉得本文不错,欢迎 Star 支持,您的关注是我坚持的动力!
本文首发:[Spring Boot 2.x 基础教程:MyBatis 的多数据源配置
](http://blog.didispace.com/spr…,转载请注明出处。
欢迎关注我的公众号:程序猿 DD,获得独家整理的学习资源和日常干货推送。
如果您对我的其他专题内容感兴趣,直达我的个人博客:didispace.com。