Java SpringBoot JPA

添加多数据源的配置

先在Spring Boot的配置文件application.properties中设置两个要链接的数据库配置,比如这样:

  1. spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
  2. spring.datasource.primary.username=root
  3. spring.datasource.primary.password=123456
  4. spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
  5. spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
  6. spring.datasource.secondary.username=root
  7. spring.datasource.secondary.password=123456
  8. spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
  9. # 日志打印执行的SQL
  10. spring.jpa.show-sql=true
  11. # Hibernate的DDL策略
  12. 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.,那么就是这个配置项的问题。

    初始化数据源与JPA配置

    完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的JdbcTemplate。
    由于JPA的配置要比JdbcTemplate的负责很多,所以将配置拆分一下来处理:

    单独建一个多数据源的配置类,比如下面这样:

    ```java @Configuration public class DataSourceConfiguration {

    @Primary @Bean @ConfigurationProperties(prefix = “spring.datasource.primary”) public DataSource primaryDataSource() {

    1. return DataSourceBuilder.create().build();

    }

    @Bean @ConfigurationProperties(prefix = “spring.datasource.secondary”) public DataSource secondaryDataSource() {

    1. return DataSourceBuilder.create().build();

    }

}

  1. 可以看到内容跟JdbcTemplate时候是一模一样的。通过`@ConfigurationProperties`可以知道这两个数据源分别加载了`spring.datasource.primary.*``spring.datasource.secondary.*`的配置。`@Primary`注解指定了主数据源,就是当不特别指定哪个数据源的时候,就会使用这个Bean真正差异部分在下面的JPA配置上。
  2. <a name="qBM6o"></a>
  3. ### 分别创建两个数据源的JPA配置。
  4. <a name="OskMC"></a>
  5. #### Primary数据源的JPA配置:
  6. ```java
  7. @Configuration
  8. @EnableTransactionManagement
  9. @EnableJpaRepositories(
  10. entityManagerFactoryRef="entityManagerFactoryPrimary",
  11. transactionManagerRef="transactionManagerPrimary",
  12. basePackages= { "com.fcant.repository.primary" }) //设置Repository所在位置
  13. public class PrimaryConfig {
  14. @Autowired
  15. @Qualifier("primaryDataSource")
  16. private DataSource primaryDataSource;
  17. @Autowired
  18. private JpaProperties jpaProperties;
  19. @Autowired
  20. private HibernateProperties hibernateProperties;
  21. private Map<String, Object> getVendorProperties() {
  22. return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
  23. }
  24. @Primary
  25. @Bean(name = "entityManagerPrimary")
  26. public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
  27. return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
  28. }
  29. @Primary
  30. @Bean(name = "entityManagerFactoryPrimary")
  31. public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
  32. return builder
  33. .dataSource(primaryDataSource)
  34. .packages("com.fcant.entity.primary") //设置实体类所在位置
  35. .persistenceUnit("primaryPersistenceUnit")
  36. .properties(getVendorProperties())
  37. .build();
  38. }
  39. @Primary
  40. @Bean(name = "transactionManagerPrimary")
  41. public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
  42. return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
  43. }
  44. }

Secondary数据源的JPA配置:

  1. @Configuration
  2. @EnableTransactionManagement
  3. @EnableJpaRepositories(
  4. entityManagerFactoryRef="entityManagerFactorySecondary",
  5. transactionManagerRef="transactionManagerSecondary",
  6. basePackages= { "com.fcant.repository.secondary" }) //设置Repository所在位置
  7. public class SecondaryConfig {
  8. @Autowired
  9. @Qualifier("secondaryDataSource")
  10. private DataSource secondaryDataSource;
  11. @Autowired
  12. private JpaProperties jpaProperties;
  13. @Autowired
  14. private HibernateProperties hibernateProperties;
  15. private Map<String, Object> getVendorProperties() {
  16. return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
  17. }
  18. @Bean(name = "entityManagerSecondary")
  19. public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
  20. return entityManagerFactorySecondary(builder).getObject().createEntityManager();
  21. }
  22. @Bean(name = "entityManagerFactorySecondary")
  23. public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
  24. return builder
  25. .dataSource(secondaryDataSource)
  26. .packages("com.fcant.entity.secondary") //设置实体类所在位置
  27. .persistenceUnit("secondaryPersistenceUnit")
  28. .properties(getVendorProperties())
  29. .build();
  30. }
  31. @Bean(name = "transactionManagerSecondary")
  32. PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
  33. return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
  34. }
  35. }

说明与注意:

  • 在使用JPA的时候,需要为不同的数据源创建不同的package来存放对应的Entity和Repository,以便于配置类的分区扫描
  • 类名上的注解@EnableJpaRepositories中指定Repository的所在位置
  • LocalContainerEntityManagerFactoryBean创建的时候,指定Entity所在的位置
  • 其他主要注意在互相注入时候,不同数据源不同配置的命名,基本就没有什么大问题了

    测试一下

    完成了上面之后,就可以写个测试类来尝试一下上面的多数据源配置是否正确了,比如下面这样: ```java @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class Chapter38ApplicationTests {

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

    @Test public void test() throws Exception {

    1. userRepository.save(new User("aaa", 10));
    2. userRepository.save(new User("bbb", 20));
    3. userRepository.save(new User("ccc", 30));
    4. userRepository.save(new User("ddd", 40));
    5. userRepository.save(new User("eee", 50));
    6. Assert.assertEquals(5, userRepository.findAll().size());
    7. messageRepository.save(new Message("o1", "aaaaaaaaaa"));
    8. messageRepository.save(new Message("o2", "bbbbbbbbbb"));
    9. messageRepository.save(new Message("o3", "cccccccccc"));
    10. Assert.assertEquals(3, messageRepository.findAll().size());

    }

} ``` 说明与注意:

  • 测试验证的逻辑很简单,就是通过不同的Repository往不同的数据源插入数据,然后查询一下总数是否是对的
  • 这里省略了Entity和Repository的细节