乐趣区

Spring-Boot-2x基础教程Spring-Data-JPA的多数据源配置

上一篇我们介绍了在使用 JdbcTemplate 来做数据访问时候的多数据源配置实现。接下来我们继续学习如何在使用 Spring Data JPA 的时候,完成多数据源的配置和使用。

添加多数据源的配置

先在 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 之后多设置一个数据源名称 primarysecondary来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。
  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 的负责很多,所以我们将配置拆分一下来处理:

  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 所在的位置
  • 其他主要注意在互相注入时候,不同数据源不同配置的命名,基本就没有什么大问题了

测试一下

完成了上面之后,我们就可以写个测试类来尝试一下上面的多数据源配置是否正确了,比如下面这样:

@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 的细节,读者可以在下方代码示例中下载完整例子对照查看

代码示例

本文的相关例子可以查看下面仓库中的 chapter3-8 目录:

  • Github:https://github.com/dyc87112/S…
  • Gitee:https://gitee.com/didispace/S…

如果您觉得本文不错,欢迎 Star 支持,您的关注是我坚持的动力!

相关阅读

  • Spring Boot 1.x 基础教程:多数据源配置

本文首发:Spring Boot 2.x 基础教程:Spring Data JPA 的多数据源配置,转载请注明出处。
欢迎关注我的公众号:程序猿 DD,获得独家整理的学习资源和日常干货推送。
如果您对我的其他专题内容感兴趣,直达我的个人博客:didispace.com。

退出移动版