Spring Boot 2.x basic tutorial: Spring Data JPA multiple data source configuration

In the previous article, we introduced the implementation of multiple data source configuration when using JdbcTemplate for data access. Next, we continue to learn how to complete the configuration and use of multiple data sources when using Spring Data JPA.

Add the configuration of multiple data sources

First application.propertiesset up two database configurations that you want to link in the Spring Boot configuration file , such as this:

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

Except for the configuration related to JPA itself, the data source configuration is completely consistent with the JdbcTemplate configuration.

Description and note :

  1. When configuring multiple data sources, the difference from single data source is that spring.datasourceafter setting up a data source name primaryand secondarydistinguishing different data source configurations, this prefix will be used in subsequent initialization of data sources.
  2. Data source connection configuration 2.x and 1.x configuration items are different: 2.x use spring.datasource.secondary.jdbc-url, and 1.x version use spring.datasource.secondary.url. If this error occurs during configuration java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName., then it is a problem with this configuration item.

Initialize the data source and JPA configuration

After completing the configuration information for multiple data sources, create a configuration class to load the configuration information, initialize the data source, and initialize the JdbcTemplate used by each data source.

Since the configuration of JPA is more responsible than that of JdbcTemplate, we will split the configuration to deal with:

  1. Create a separate configuration class for multiple data sources, such as the following:
@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();
    }

}

You can see that the content is exactly the same as the JdbcTemplate. It @ConfigurationPropertiescan be known that these two data sources have loaded the configuration of spring.datasource.primary.*and respectively spring.datasource.secondary.*. @PrimaryThe annotation specifies the main data source, that is, when we do not specify which data source, the real difference of this Bean will be used in the following JPA configuration.

  1. Create the JPA configuration of the two data sources respectively.

JPA configuration of Primary data source:

@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());
    }

}

JPA configuration of Secondary data source:

@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());
    }

}

Description and note :

  • When using JPA, you need to create different packages for different data sources to store the corresponding Entity and Repository, so as to facilitate the partition scan of the configuration class
  • @EnableJpaRepositoriesSpecify the location of the Repository in the annotation on the class name
  • LocalContainerEntityManagerFactoryBeanWhen creating, specify the location of Entity
  • The other main attention is that when injecting each other, there is basically no big problem with the naming of different data sources and different configurations.

have a test

After completing the above, we can write a test class to try whether the above multiple data source configuration is correct, such as the following:

@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());
    }

}

Description and note :

  • The logic of the test verification is very simple, that is, insert data into different data sources through different Repository, and then query whether the total number is correct
  • The details of Entity and Repository are omitted here, readers can download the complete example in the code sample below to view it

Code example

For related examples in this article, you can view the chapter3-8directories in the following warehouse :

  • Github:https://github.com/dyc87112/SpringBoot-Learning/
  • Gitee:https://gitee.com/didispace/SpringBoot-Learning/

If you think this article is good, welcome Star support, your attention is my motivation for persistence!

Related Reading

Guess you like

Origin blog.csdn.net/dyc87112/article/details/106919883