天天看點

pring Boot 2.x基礎教程:Spring Data JPA的多資料源配置

上一篇

我們介紹了在使用JdbcTemplate來做資料通路時候的多資料源配置實作。接下來我們繼續學習如何在使用Spring Data JPA的時候,完成多資料源的配置和使用。

https://blog.didispace.com/spring-boot-learning-21-3-8/#%E6%B7%BB%E5%8A%A0%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90%E7%9A%84%E9%85%8D%E7%BD%AE 添加多資料源的配置

先在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

# 日志列印執行的SQL
spring.jpa.show-sql=true
# Hibernate的DDL政策
spring.jpa.hibernate.ddl-auto=create-drop      

這裡除了JPA自身相關的配置之外,與JdbcTemplate配置時候的資料源配置完全是一緻的

說明與注意:

  1. 多資料源配置的時候,與單資料源不同點在于

    spring.datasource

    之後多設定一個資料源名稱

    primary

    secondary

    來區分不同的資料源配置,這個字首将在後續初始化資料源的時候用到。
  2. 資料源連接配接配置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.

    ,那麼就是這個配置項的問題。

https://blog.didispace.com/spring-boot-learning-21-3-8/#%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E6%8D%AE%E6%BA%90%E4%B8%8EJPA%E9%85%8D%E7%BD%AE 初始化資料源與JPA配置

完成多資料源的配置資訊之後,就來建立個配置類來加載這些配置資訊,初始化資料源,以及初始化每個資料源要用的JPA配置。

由于JPA的配置要比JdbcTemplate的複雜很多,是以我們将配置拆分一下來處理:

  1. 單獨建一個多資料源的配置類,比如下面這樣:
@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時候是一模一樣的。通過

@ConfigurationProperties

可以知道這兩個資料源分别加載了

spring.datasource.primary.*

spring.datasource.secondary.*

的配置。

@Primary

注解指定了主資料源,就是當我們不特别指定哪個資料源的時候,就會使用這個Bean真正差異部分在下面的JPA配置上。

  1. 分别建立兩個資料源的JPA配置。

Primary資料源的JPA配置:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactoryPrimary",
        transactionManagerRef="transactionManagerPrimary",
        basePackages= { "com.didispace.chapter38.p" }) //設定Repository所在位置
public class PrimaryConfig {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired
    private JpaProperties jpaProperties;
    @Autowired
    private HibernateProperties hibernateProperties;

    private Map<String, Object> getVendorProperties() {
        return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
    }

    @Primary
    @Bean(name = "entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }

    @Primary
    @Bean(name = "entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(primaryDataSource)
                .packages("com.didispace.chapter38.p") //設定實體類所在位置
                .persistenceUnit("primaryPersistenceUnit")
                .properties(getVendorProperties())
                .build();
    }

    @Primary
    @Bean(name = "transactionManagerPrimary")
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }

}      

Secondary資料源的JPA配置:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactorySecondary",
        transactionManagerRef="transactionManagerSecondary",
        basePackages= { "com.didispace.chapter38.s" }) //設定Repository所在位置
public class SecondaryConfig {

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    @Autowired
    private JpaProperties jpaProperties;
    @Autowired
    private HibernateProperties hibernateProperties;

    private Map<String, Object> getVendorProperties() {
        return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
    }

    @Bean(name = "entityManagerSecondary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactorySecondary(builder).getObject().createEntityManager();
    }

    @Bean(name = "entityManagerFactorySecondary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(secondaryDataSource)
                .packages("com.didispace.chapter38.s") //設定實體類所在位置
                .persistenceUnit("secondaryPersistenceUnit")
                .properties(getVendorProperties())
                .build();
    }

    @Bean(name = "transactionManagerSecondary")
    PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
    }

}      
  • 在使用JPA的時候,需要為不同的資料源建立不同的package來存放對應的Entity和Repository,以便于配置類的分區掃描
  • 類名上的注解

    @EnableJpaRepositories

    中指定Repository的所在位置
  • LocalContainerEntityManagerFactoryBean

    建立的時候,指定Entity所在的位置
  • 其他主要注意在互相注入時候,不同資料源不同配置的命名,基本就沒有什麼大問題了

https://blog.didispace.com/spring-boot-learning-21-3-8/#%E6%B5%8B%E8%AF%95%E4%B8%80%E4%B8%8B 測試一下

完成了上面之後,我們就可以寫個測試類來嘗試一下上面的多資料源配置是否正确了,比如下面這樣:

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter38ApplicationTests {

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private MessageRepository messageRepository;

    @Test
    public void test() throws Exception {
        userRepository.save(new User("aaa", 10));
        userRepository.save(new User("bbb", 20));
        userRepository.save(new User("ccc", 30));
        userRepository.save(new User("ddd", 40));
        userRepository.save(new User("eee", 50));

        Assert.assertEquals(5, userRepository.findAll().size());

        messageRepository.save(new Message("o1", "aaaaaaaaaa"));
        messageRepository.save(new Message("o2", "bbbbbbbbbb"));
        messageRepository.save(new Message("o3", "cccccccccc"));

        Assert.assertEquals(3, messageRepository.findAll().size());
    }

}      
  • 測試驗證的邏輯很簡單,就是通過不同的Repository往不同的資料源插入資料,然後查詢一下總數是否是對的
  • 這裡省略了Entity和Repository的細節,讀者可以在下方代碼示例中下載下傳完整例子對照檢視

https://blog.didispace.com/spring-boot-learning-21-3-8/#%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B 代碼示例

本文的相關例子可以檢視下面倉庫中的

chapter3-8

目錄:

如果您覺得本文不錯,歡迎Star支援,您的關注是我堅持的動力!

https://blog.didispace.com/spring-boot-learning-21-3-8/#%E7%9B%B8%E5%85%B3%E9%98%85%E8%AF%BB 相關閱讀