前兩天亡资,我們已經(jīng)介紹了關(guān)于JdbcTemplate的多數(shù)據(jù)源配置以及Spring Data JPA的多數(shù)據(jù)源配置拿霉,接下來具體說說使用MyBatis時(shí)候的多數(shù)據(jù)源場景該如何配置曹体。
添加多數(shù)據(jù)源的配置
先在Spring Boot的配置文件application.properties中設(shè)置兩個(gè)你要鏈接的數(shù)據(jù)庫配置扳缕,比如這樣:
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
說明與注意:
- 多數(shù)據(jù)源配置的時(shí)候迎捺,與單數(shù)據(jù)源不同點(diǎn)在于spring.datasource之后多設(shè)置一個(gè)數(shù)據(jù)源名稱primary和secondary來區(qū)分不同的數(shù)據(jù)源配置举畸,這個(gè)前綴將在后續(xù)初始化數(shù)據(jù)源的時(shí)候用到。
- 數(shù)據(jù)源連接配置2.x和1.x的配置項(xiàng)是有區(qū)別的:2.x使用spring.datasource.secondary.jdbc-url凳枝,而1.x版本使用spring.datasource.secondary.url抄沮。如果你在配置的時(shí)候發(fā)生了這個(gè)報(bào)錯(cuò)java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是這個(gè)配置項(xiàng)的問題岖瑰。
- 可以看到叛买,不論使用哪一種數(shù)據(jù)訪問框架,對于數(shù)據(jù)源的配置都是一樣的锭环。
初始化數(shù)據(jù)源與MyBatis配置
完成多數(shù)據(jù)源的配置信息之后聪全,就來創(chuàng)建個(gè)配置類來加載這些配置信息,初始化數(shù)據(jù)源辅辩,以及初始化每個(gè)數(shù)據(jù)源要用的MyBatis配置难礼。
這里我們繼續(xù)將數(shù)據(jù)源與框架配置做拆分處理:
- 單獨(dú)建一個(gè)多數(shù)據(jù)源的配置類,比如下面這樣:
@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();
}
}
可以看到內(nèi)容跟JdbcTemplate玫锋、Spring Data JPA的時(shí)候是一模一樣的蛾茉。通過@ConfigurationProperties
可以知道這兩個(gè)數(shù)據(jù)源分別加載了spring.datasource.primary.*
和spring.datasource.secondary.*
的配置。@Primary
注解指定了主數(shù)據(jù)源撩鹿,就是當(dāng)我們不特別指定哪個(gè)數(shù)據(jù)源的時(shí)候谦炬,就會(huì)使用這個(gè)Bean真正差異部分在下面的JPA配置上。
- 分別創(chuàng)建兩個(gè)數(shù)據(jù)源的MyBatis配置。
Primary數(shù)據(jù)源的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數(shù)據(jù)源的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
注解來指定當(dāng)前數(shù)據(jù)源下定義的Entity和Mapper的包路徑键思;另外需要指定sqlSessionFactory和sqlSessionTemplate础爬,這兩個(gè)具體實(shí)現(xiàn)在該配置類中類中初始化。 - 配置類的構(gòu)造函數(shù)中吼鳞,通過
@Qualifier
注解來指定具體要用哪個(gè)數(shù)據(jù)源看蚜,其名字對應(yīng)在DataSourceConfiguration
配置類中的數(shù)據(jù)源定義的函數(shù)名。 - 配置類中定義SqlSessionFactory和SqlSessionTemplate的實(shí)現(xiàn)赔桌,注意具體使用的數(shù)據(jù)源正確(如果使用這里的演示代碼供炎,只要第二步?jīng)]問題就不需要修改)。
上一篇介紹JPA的時(shí)候疾党,因?yàn)橹敖榻BJPA的使用時(shí)候音诫,說過實(shí)體和Repository定義的方法,所以省略了 User 和 Repository的定義代碼雪位,但是還是有讀者問怎么沒有這個(gè)竭钝,其實(shí)都有說明,倉庫代碼里也都是有的茧泪。未避免再問這樣的問題蜓氨,所以這里就貼一下吧。
根據(jù)上面Primary數(shù)據(jù)源的定義队伟,在com.didispace.chapter39.p
包下穴吹,定義Primary數(shù)據(jù)源要用的實(shí)體和數(shù)據(jù)訪問對象,比如下面這樣:
@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();
}
根據(jù)上面Secondary數(shù)據(jù)源的定義嗜侮,在com.didispace.chapter39.s
包下港令,定義Secondary數(shù)據(jù)源要用的實(shí)體和數(shù)據(jù)訪問對象,比如下面這樣:
@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();
}
測試驗(yàn)證
完成了上面之后锈颗,我們就可以寫個(gè)測試類來嘗試一下上面的多數(shù)據(jù)源配置是否正確了顷霹,先來設(shè)計(jì)一下驗(yàn)證思路:
- 往Primary數(shù)據(jù)源插入一條數(shù)據(jù)
- 從Primary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù),配置正確就可以查詢到
- 從Secondary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)击吱,配置正確應(yīng)該是查詢不到的
- 往Secondary數(shù)據(jù)源插入一條數(shù)據(jù)
- 從Primary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)淋淀,配置正確應(yīng)該是查詢不到的
- 從Secondary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù),配置正確就可以查詢到
具體實(shí)現(xiàn)如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class Chapter39ApplicationTests {
@Autowired
private UserMapperPrimary userMapperPrimary;
@Autowired
private UserMapperSecondary userMapperSecondary;
@Before
public void setUp() {
// 清空測試表覆醇,保證每次結(jié)果一樣
userMapperPrimary.deleteAll();
userMapperSecondary.deleteAll();
}
@Test
public void test() throws Exception {
// 往Primary數(shù)據(jù)源插入一條數(shù)據(jù)
userMapperPrimary.insert("AAA", 20);
// 從Primary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)朵纷,配置正確就可以查詢到
UserPrimary userPrimary = userMapperPrimary.findByName("AAA");
Assert.assertEquals(20, userPrimary.getAge().intValue());
// 從Secondary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù),配置正確應(yīng)該是查詢不到的
UserSecondary userSecondary = userMapperSecondary.findByName("AAA");
Assert.assertNull(userSecondary);
// 往Secondary數(shù)據(jù)源插入一條數(shù)據(jù)
userMapperSecondary.insert("BBB", 20);
// 從Primary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)永脓,配置正確應(yīng)該是查詢不到的
userPrimary = userMapperPrimary.findByName("BBB");
Assert.assertNull(userPrimary);
// 從Secondary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)袍辞,配置正確就可以查詢到
userSecondary = userMapperSecondary.findByName("BBB");
Assert.assertEquals(20, userSecondary.getAge().intValue());
}
}
代碼示例
本文的相關(guān)例子可以查看下面?zhèn)}庫中的chapter3-9
目錄:
- Github:https://github.com/dyc87112/SpringBoot-Learning/
- Gitee:https://gitee.com/didispace/SpringBoot-Learning/
如果您覺得本文不錯(cuò),歡迎Star支持常摧,您的關(guān)注是我堅(jiān)持的動(dòng)力搅吁!
本文首發(fā):Spring Boot 2.x基礎(chǔ)教程:MyBatis的多數(shù)據(jù)源配置
威创,轉(zhuǎn)載請注明出處。
歡迎關(guān)注我的公眾號(hào):程序猿DD谎懦,獲得獨(dú)家整理的學(xué)習(xí)資源和日常干貨推送肚豺。
如果您對我的其他專題內(nèi)容感興趣,直達(dá)我的個(gè)人博客:didispace.com界拦。