1    第3-6课:Spring Data JPA 多数据源的使用

项目中使用多个数据源在以往工作中比较常见,微服务架构中不建议一个项目使用多个数据源。在微服务架构下,一个微服务拥有自己独立的一个数据库,如果此微服务要使用其他数据库的数据,需要调用对应库的微服务接口来调用,而不是在一个项目中连接使用多个数据库,这样微服务更独立、更容易水平扩展。

虽然在微服务架构下,不提倡一个项目拥有多个数据源,但在 Spring Boot 体系中,项目实现多数据源调用却是一件很容易的事情,本节课将介绍 Spring Data JPA 多数据源的使用。

Spring Data JPA 使用多数据源的整体思路是,配置不同的数据源,在启动时分别加载多个数据源配置,并且注入到不同的 repository 中。这样不同的 repository 包就有不同的数据源,使用时注入对应包下的 repository,就会使用对应数据源的操作。

对照前两课的示例项目,本课内容将会对项目结构有所调整,如下:

 

其中:

  • config 启动时加载、配置多数据源;
  • model 存放数据操作的实体类;
  • repository 目录下有两个包路径 test1 和 test2 ,分别代表两个不同数据源下的仓库,这两个包下的 repository 可以相同也可以不同。

下面演示一下项目。

1.1    多数据源的支持

配置 Spring Data JPA 对多数据源的使用,一般分为以下几步:

  • 创建数据库 test1 和 test2 
  • 配置多数据源
  • 不同源的 repository 放入不同包路径
  • 声明不同的包路径下使用不同的数据源、事务支持
  • 不同的包路径下创建对应的 repository
  • 测试使用

上面的一些步骤我们在前面两课中已经讲过了,这里只补充不同的内容。

配置两个数据源:

spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/
1?serverTimezone=UTC&useUnicode=
&characterEncoding=utf-8&useSSL=spring.datasource.primary.username=rootspring.datasource.primary.password=rootspring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/
2?serverTimezone=UTC&useUnicode=
&characterEncoding=utf-8&useSSL=spring.datasource.secondary.username=rootspring.datasource.secondary.password=rootspring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver


#SQL 输出



spring.jpa.show-sql=
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect



#format 一下 SQL 进行输出



spring.jpa.properties.hibernate.format_sql=

设置将项目中的 SQL 格式化后打印出来,方便在开发过程中调试跟踪。

创建 DataSourceConfig 添加 @Configuration 注解,在项目启动时运行初始化数据库资源。



publicclass DataSourceConfig {



}

在 DataSourceConfig 类中加载配置文件,利用 ConfigurationProperties 自动装配的特性加载两个数据源。

加载第一个数据源,数据源配置以 spring.datasource.primary 开头,注意当有多个数据源时,需要将其中一个标注为 @Primary,作为默认的数据源使用。

(name = 
)
(
)



public DataSource firstDataSource() {



DataSourceBuilder.create().build();
}

加载第二个数据源,数据源配置以 spring.datasource.secondary 为开头。

@
(
 = 
)@ConfigurationProperties(
)public DataSource secondDataSource() {()
();}

加载 JPA 的相关配置信息,JpaProperties 是 JPA 的一些属性配置信息,构建 LocalEntityManagerFactoryBean 需要参数信息注入到方法中。

@private JpaProperties jpaProperties;@private HibernateProperties hibernateProperties;@
(
 = 
)public Map<String, Object> getVendorProperties() {(
(), 
 
());}

第一个数据源的加载配置过程

首先来看第一个数据源的加载配置过程,创建 PrimaryConfig 类,将上面创建好的第一个数据源注入到类中,添加 @Configuration 和 @EnableTransactionManagement 注解,第一个代表启动时加载,第二个注解表示启用事务,同时将第一个数据源和 JPA 配置信息注入到类中。

publicclass PrimaryConfig {

 
 
(
)DataSource primaryDataSource;(
)Map<String, Object> vendorProperties;}

LocalEntityManagerFactoryBean 负责创建一个适合于仅使用 JPA 进行数据访问的环境的 EntityManager,构建的时候需要指明提示实体类的包路径、数据源和 JPA 配置信息。

(name = 
) 
 
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {

 
 
builder.dataSource(primaryDataSource).properties(vendorProperties).packages(
).persistenceUnit(
).build();}

利用上面的 entityManagerFactoryPrimary() 方法构建好最终的 EntityManager。

@
(
 = 
)

EntityManager 是 JPA 中用于增、删、改、查的接口,它的作用相当于一座桥梁,连接内存中的 Java 对象和数据库的数据存储。使用 EntityManager 中的相关接口对数据库实体进行操作的时候, EntityManager 会跟踪实体对象的状态,并决定在特定时刻将对实体的操作映射到数据库操作上面。

@
(
 = 
)@Primarypublic EntityManager entityManager(EntityManagerFactoryBuilder builder) {(
)
()
();}

同时给数据源添加上 JPA 事务。

(name = 
)



PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {



JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}

最后一步最为关键,将我们在类中配置好的 EntityManager 和事务信息注入到对应数据源的 repository 目录下,这样此目录下的 repository 就会拥有对应数据源和事务的信息。

(entityManagerFactoryRef=
,transactionManagerRef=
,basePackages= { 
 })


publicclass PrimaryConfig {}



其中,basePackages 支持设置多个包路径,例如,basePackages= { "com.neo.repository.test1","com.neo.repository.test3" }

到此第一个数据源配置完成了。

第二个数据源的加载配置过程

第二个数据源配置和第一个数据源配置类似,只是方法上去掉了注解:@Primary,第二个数据源数据源加载配置类 SecondaryConfig 完整代码如下:

(entityManagerFactoryRef=
,transactionManagerRef=
,basePackages= { 
 }) 
 
publicclass SecondaryConfig {

 
 
(
)DataSource secondaryDataSource;(
)Map<String, Object> vendorProperties;(name = 
){builder.dataSource(secondaryDataSource).properties(vendorProperties).packages(
).persistenceUnit(
).build();}(name = 
){entityManagerFactorySecondary(builder).getObject().createEntityManager();}(name = 
){JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());}}

到此多数据源的配置就完成了,项目中使用哪个数据源的操作,就注入对应包下的 repository 进行操作即可,接下来我们对上面配置好的数据源进行测试。

创建 UserRepositoryTests 测试类,将两个包下的 repository 都注入到测试类中:

(SpringRunner.class)



publicclass UserRepositoryTests {

UserTest1Repository userTest1Repository;UserTest2Repository userTest2Repository;}

首先测试两个数据库中都存入数据,数据源1插入 2 条用户信息,数据源2插入 1 条用户信息。



public void testSave() throws Exception {

Date date = 
 Date();DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);String formattedDate = dateFormat.format(date);userTest1Repository.save(
 User(
, 
,
, 
,  formattedDate));userTest1Repository.save(
 User(
, 
,
, 
,  formattedDate));userTest2Repository.save(
 User(
, 
,
, 
,  formattedDate));}

执行完测试用例后查看数据库,发现 test1 库有两条数据,test2 有一条,证明两个数据源均保存数据正常。下面继续测试删除功能,使用两个数据源的 repository 将用户信息全部删除。



public void testDelete() throws Exception {

userTest1Repository.deleteAll();userTest2Repository.deleteAll();}

执行完测试用例后,发现 test1 库和 test2 库用户表的信息已经被清空,证明多数据源删除成功。

1.2    总结

Spring Data JPA 通过在启动时加载不同的数据源,并将不同的数据源注入到不同的 repository 包下,从而实现项目多数据源操作,在项目中使用多数据源时,需要用到哪个数据源,只需要将对应包下的 repository 注入操作即可。本课示例中以两个数据源作为演示,但其实三个或者更多数据源配置、操作,都可以按照上面方法进行配置使用。