Spring Boot 2.x基礎(chǔ)教程:MyBatis的多數(shù)據(jù)源配置

前兩天亡资,我們已經(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

說明與注意

  1. 多數(shù)據(jù)源配置的時(shí)候迎捺,與單數(shù)據(jù)源不同點(diǎn)在于spring.datasource之后多設(shè)置一個(gè)數(shù)據(jù)源名稱primary和secondary來區(qū)分不同的數(shù)據(jù)源配置举畸,這個(gè)前綴將在后續(xù)初始化數(shù)據(jù)源的時(shí)候用到。
  2. 數(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)的問題岖瑰。
  3. 可以看到叛买,不論使用哪一種數(shù)據(jù)訪問框架,對于數(shù)據(jù)源的配置都是一樣的锭环。

初始化數(shù)據(jù)源與MyBatis配置

完成多數(shù)據(jù)源的配置信息之后聪全,就來創(chuàng)建個(gè)配置類來加載這些配置信息,初始化數(shù)據(jù)源辅辩,以及初始化每個(gè)數(shù)據(jù)源要用的MyBatis配置难礼。

這里我們繼續(xù)將數(shù)據(jù)源與框架配置做拆分處理:

  1. 單獨(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配置上。

  1. 分別創(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());
    }

}

說明與注意

  1. 配置類上使用@MapperScan注解來指定當(dāng)前數(shù)據(jù)源下定義的Entity和Mapper的包路徑键思;另外需要指定sqlSessionFactory和sqlSessionTemplate础爬,這兩個(gè)具體實(shí)現(xiàn)在該配置類中類中初始化。
  2. 配置類的構(gòu)造函數(shù)中吼鳞,通過@Qualifier注解來指定具體要用哪個(gè)數(shù)據(jù)源看蚜,其名字對應(yīng)在DataSourceConfiguration配置類中的數(shù)據(jù)源定義的函數(shù)名。
  3. 配置類中定義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)證思路:

  1. 往Primary數(shù)據(jù)源插入一條數(shù)據(jù)
  2. 從Primary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù),配置正確就可以查詢到
  3. 從Secondary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)击吱,配置正確應(yīng)該是查詢不到的
  4. 往Secondary數(shù)據(jù)源插入一條數(shù)據(jù)
  5. 從Primary數(shù)據(jù)源查詢剛才插入的數(shù)據(jù)淋淀,配置正確應(yīng)該是查詢不到的
  6. 從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目錄:

如果您覺得本文不錯(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界拦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末详炬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寞奸,更是在濱河造成了極大的恐慌,老刑警劉巖在跳,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枪萄,死亡現(xiàn)場離奇詭異,居然都是意外死亡猫妙,警方通過查閱死者的電腦和手機(jī)瓷翻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來割坠,“玉大人齐帚,你說我怎么就攤上這事”撕撸” “怎么了对妄?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長敢朱。 經(jīng)常有香客問我剪菱,道長,這世上最難降的妖魔是什么拴签? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任孝常,我火速辦了婚禮,結(jié)果婚禮上蚓哩,老公的妹妹穿的比我還像新娘构灸。我一直安慰自己,他們只是感情好岸梨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布喜颁。 她就那樣靜靜地躺著,像睡著了一般盛嘿。 火紅的嫁衣襯著肌膚如雪洛巢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天次兆,我揣著相機(jī)與錄音稿茉,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛漓库,可吹牛的內(nèi)容都是我干的恃慧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渺蒿,長吁一口氣:“原來是場噩夢啊……” “哼痢士!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茂装,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤怠蹂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后少态,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體城侧,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年彼妻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嫌佑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侨歉,死狀恐怖屋摇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幽邓,我是刑警寧澤炮温,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站牵舵,受9級(jí)特大地震影響茅特,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棋枕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一白修、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧重斑,春花似錦兵睛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至漾脂,卻和暖如春假颇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骨稿。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國打工笨鸡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姜钳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓形耗,卻偏偏與公主長得像哥桥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子激涤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355