本文是 Spring Boot 整合数据持久化方案的最后一篇,主要和大伙来聊聊 Spring Boot 整合 Jpa 多数据源问题。在 Spring Boot 整合JbdcTemplate 多数据源、Spring Boot 整合 MyBatis 多数据源以及 Spring Boot 整合 Jpa 多数据源这三个知识点中,整合 Jpa 多数据源算是最复杂的一种,也是很多人在配置时最容易出错的一种。本文大伙就跟着松哥的教程,一步一步整合 Jpa 多数据源。

# 工程创建

首先是创建一个 Spring Boot 工程,创建时添加基本的 Web、Jpa 以及 MySQL 依赖,如下:

创建完成后,添加 Druid 依赖,这里和前文的要求一样,要使用专为 Spring Boot 打造的 Druid,大伙可能发现了,如果整合多数据源一定要使用这个依赖,因为这个依赖中才有 DruidDataSourceBuilder,最后还要记得锁定数据库依赖的版本,因为可能大部分人用的还是 5.x 的 MySQL 而不是 8.x。完整依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.28</version>
    <scope>runtime</scope>
</dependency>

如此之后,工程就创建成功了。

# 基本配置

在基本配置中,我们首先来配置多数据源基本信息以及 DataSource,首先在 application.properties 中添加如下配置信息:

#  数据源一
spring.datasource.one.username=root
spring.datasource.one.password=root
spring.datasource.one.url=jdbc:mysql:///test01?useUnicode=true&characterEncoding=UTF-8
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource

#  数据源二
spring.datasource.two.username=root
spring.datasource.two.password=root
spring.datasource.two.url=jdbc:mysql:///test02?useUnicode=true&characterEncoding=UTF-8
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource

# Jpa配置
spring.jpa.properties.database=mysql
spring.jpa.properties.show-sql=true
spring.jpa.properties.database-platform=mysql
spring.jpa.properties.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect

这里 Jpa 的配置和上文相比 key 中多了 properties,多数据源的配置和前文一致,然后接下来配置两个 DataSource,如下:

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.one")
    @Primary
    DataSource dsOne() {
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.two")
    DataSource dsTwo() {
        return DruidDataSourceBuilder.create().build();
    }
}

这里的配置和前文的多数据源配置基本一致,但是注意多了一个在 Spring 中使用较少的注解 @Primary,这个注解一定不能少,否则在项目启动时会出错,@Primary 表示当某一个类存在多个实例时,优先使用哪个实例。

好了,这样,DataSource 就有了。

# 多数据源配置

接下来配置 Jpa 的基本信息,这里两个数据源,我分别在两个类中来配置,先来看第一个配置:

@Configuration
@EnableJpaRepositories(basePackages = "org.javaboy.jpa.dao",entityManagerFactoryRef = "localContainerEntityManagerFactoryBeanOne",transactionManagerRef = "platformTransactionManagerOne")
public class JpaConfigOne {
    @Autowired
    @Qualifier(value = "dsOne")
    DataSource dsOne;
    @Autowired
    JpaProperties jpaProperties;
    @Bean
    @Primary
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBeanOne(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(dsOne)
                .packages("org.javaboy.jpa.model")
                .properties(jpaProperties.getProperties())
                .persistenceUnit("pu1")
                .build();
    }
    @Bean
    PlatformTransactionManager platformTransactionManagerOne(EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean factoryBeanOne = localContainerEntityManagerFactoryBeanOne(builder);
        return new JpaTransactionManager(factoryBeanOne.getObject());
    }
}

首先这里注入 dsOne,再注入 JpaProperties,JpaProperties 是系统提供的一个实例,里边的数据就是我们在 application.properties 中配置的 jpa 相关的配置。然后我们提供两个 Bean,分别是 LocalContainerEntityManagerFactoryBean 和 PlatformTransactionManager 事务管理器,不同于 MyBatis 和 JdbcTemplate,在 Jpa 中,事务一定要配置。在提供 LocalContainerEntityManagerFactoryBean 的时候,需要指定 packages,这里的 packages 指定的包就是这个数据源对应的实体类所在的位置,另外在这里配置类上通过 @EnableJpaRepositories 注解指定 dao 所在的位置,以及 LocalContainerEntityManagerFactoryBean 和 PlatformTransactionManager 分别对应的引用的名字。

好了,这样第一个就配置好了,第二个基本和这个类似,主要有几个不同点:

  • dao 的位置不同
  • persistenceUnit 不同
  • 相关 bean 的名称不同

注意实体类可以共用。

代码如下:

@Configuration
@EnableJpaRepositories(basePackages = "org.javaboy.jpa.dao2",entityManagerFactoryRef = "localContainerEntityManagerFactoryBeanTwo",transactionManagerRef = "platformTransactionManagerTwo")
public class JpaConfigTwo {
    @Autowired
    @Qualifier(value = "dsTwo")
    DataSource dsTwo;
    @Autowired
    JpaProperties jpaProperties;
    @Bean
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBeanTwo(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(dsTwo)
                .packages("org.javaboy.jpa.model")
                .properties(jpaProperties.getProperties())
                .persistenceUnit("pu2")
                .build();
    }
    @Bean
    PlatformTransactionManager platformTransactionManagerTwo(EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean factoryBeanTwo = localContainerEntityManagerFactoryBeanTwo(builder);
        return new JpaTransactionManager(factoryBeanTwo.getObject());
    }
}

接下来,在对应位置分别提供相关的实体类和 dao 即可,数据源一的 dao 如下:

package org.javaboy.jpa.dao;
public interface UserDao extends JpaRepository<User,Integer> {
    List<User> getUserByAddressEqualsAndIdLessThanEqual(String address, Integer id);
    @Query(value = "select * from t_user where id=(select max(id) from t_user)",nativeQuery = true)
    User maxIdUser();
}

数据源二的 dao 如下:

package org.javaboy.jpa.dao2;
public interface UserDao2 extends JpaRepository<User,Integer> {
    List<User> getUserByAddressEqualsAndIdLessThanEqual(String address, Integer id);

    @Query(value = "select * from t_user where id=(select max(id) from t_user)",nativeQuery = true)
    User maxIdUser();
}

共同的实体类如下:

package org.javaboy.jpa.model;
@Entity(name = "t_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;
    private String address;
    //省略getter/setter
}

到此,所有的配置就算完成了,接下来就可以在 Service 中注入不同的 UserDao,不同的 UserDao 操作不同的数据源。

其实整合 Jpa 多数据源也不算难,就是有几个细节问题,这些细节问题解决,其实前面介绍的其他多数据源整个都差不多。

好了,本文就先介绍到这里。

相关案例已经上传到 GitHub,欢迎小伙伴们们下载:https://github.com/lenve/javaboy-code-samples