SpringBoot - 使用Spring Data JPA操作数据库2(多数据源的配置与使用)
所谓多数据源,就是一个 Java EE 项目中采用了不同数据库实例中的多个库,或者同一个数据库实例中多个不同的库。JPA 和 MyBatis 配置多数据源类似(点击查看),不同的是,JPA 配置时主要提供不同的 LocalContainerEntityManagerFactoryBean 以及事务管理器,具体配置步骤如下。
三、多数据源的配置与使用
1,添加依赖
首先编辑 pom.xml 文件,添加相关依赖:
注意:跟前文相比,这里的数据库连接池依赖改用 druid-spring-boot-starter。druid-spring-boot-starter 可以帮助开发者在 Spring Boot 项目中轻松集成 Druid 数据库连接池和监控。
<!-- Spring Data JPA 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- 数据库驱动依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>
2,配置数据库链接
(1)假设我们需要同时连接如下 hangge、hangge2 这两个数据库。
(2)在 application.properties 中配置数据库连接信息,以及 JPA 相关的配置:
注意:这里的 JPA 配置与单独数据库的 JPA 有区别,因为后面的配置中要从 JpaProperties 中的 getProperties 方法中获取所有 JPA 相关的配置,因此这里的属性前缀都是 spring.jpa.properties
# 数据源 1
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.url=jdbc:mysql://localhost:3306/hangge
spring.datasource.one.username=root
spring.datasource.one.password=hangge1234
# 数据源 2
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.url=jdbc:mysql://localhost:3306/hangge2
spring.datasource.two.username=root
spring.datasource.two.password=hangge1234
#是否在控制台打印JPA执行过程生成的SQL
spring.jpa.properties.show-sql=true
#表示JPA对应的数据库是MySQL
spring.jpa.properties.database=mysql
#表示在项目启动时根据实体类更新数据库中的表
spring.jpa.properties.hibernate.hbm2ddl.auto=update
#表示使用的数据库方言是MySQL57Dialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
3,配置数据源
创建 DataSourceConfig 配置数据源,根据 application.properties 中的配置生成两个数据源:
代码说明:
- DataSourceConfig 中提供了两个数据源:dsOne 和 dsTwo ,默认方法名即为实例名。
- @ConfigurationProperties 注解表示使用不同前缀的配置文件来创建不同的 DataSource 实例。
- 注意:在第一个 DataSource 上还标注了 @Primary 注解。否则程序启动时会报 Parameter 0 of method entityManagerFactoryBeanOne in com.example.demo.config.JpaConfigOne required a bean of type 'org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder' that could not be found. 错误
@Configuration public class DataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.one") @Primary DataSource dsOne() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.two") DataSource dsTwo() { return DruidDataSourceBuilder.create().build(); } }
4,创建 JPA 配置
(1)接下需要根据两个配置好的数据源创建两个不同的 JPA 配置。第一个 JPA 配置如下:
(1)使用 @EnableTransactionManagement 注解来进行 JPA 的配置,该注解中主要配置了三个属性:
(4)配置类中第二个方法表示创建一个事务管理器。JpaTransactionManager 提供对单个 EntityManagerFactory 的事务支持,专门用于解决 JPA 中的事务管理。
- basePackages 用来指定 Repository 所在的位置。
- entityManagerFactoryRef 用来指定实体类管理工厂 Bean 的名称。
- transactionManagerRef 用来指定事物管理器的引用名称,这里的引用名称就是 JpaConfigOne 类中注册的 Bean 的名称(默认的 Bean 名称为方法名)
- 首先配置数据源
- 然后设置 JPA 相关配置(JpaProperties 由系统自动加载)
- 再设置实体类所在的位置。
- 最后配置持久化单元名。若项目中只有一个 EntityManagerFactory,则 persistenceUnit 可以省略掉,若有多个,则必须明确指定持久化单元名。
(4)配置类中第二个方法表示创建一个事务管理器。JpaTransactionManager 提供对单个 EntityManagerFactory 的事务支持,专门用于解决 JPA 中的事务管理。
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "com.example.demo.dao1", entityManagerFactoryRef = "entityManagerFactoryBeanOne", transactionManagerRef = "platformTransactionManagerOne") public class JpaConfigOne { @Resource(name = "dsOne") DataSource dsOne; @Autowired JpaProperties jpaProperties; @Bean @Primary LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanOne( EntityManagerFactoryBuilder builder) { return builder.dataSource(dsOne) .properties(jpaProperties.getProperties()) .packages("com.example.demo.bean") .persistenceUnit("pu1") .build(); } @Bean PlatformTransactionManager platformTransactionManagerOne( EntityManagerFactoryBuilder builder) { LocalContainerEntityManagerFactoryBean factoryOne = entityManagerFactoryBeanOne(builder); return new JpaTransactionManager(factoryOne.getObject()); } }
(2)第二个 JPA 配置如下:
JpaConfigTwo 的配置与 JpaConfigOne 类似,主要区别在于 LocalContainerEntityManagerFactoryBean 实例不需要添加 @Primary 注解。
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages = "com.example.demo.dao2", entityManagerFactoryRef = "entityManagerFactoryBeanTwo", transactionManagerRef = "platformTransactionManagerTwo") public class JpaConfigTwo { @Resource(name = "dsTwo") DataSource dsTwo; @Autowired JpaProperties jpaProperties; @Bean LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanTwo( EntityManagerFactoryBuilder builder) { return builder.dataSource(dsTwo) .properties(jpaProperties.getProperties()) .packages("com.example.demo.bean") .persistenceUnit("pu2") .build(); } @Bean PlatformTransactionManager platformTransactionManagerTwo( EntityManagerFactoryBuilder builder) { LocalContainerEntityManagerFactoryBean factoryTwo = entityManagerFactoryBeanTwo(builder); return new JpaTransactionManager(factoryTwo.getObject()); } }
5,创建 Repository(数据库访问层)
根据上面第 4 步的配置,我们分别在 com.example.demo.dao1 和 com.example.demo.dao2 包下创建两个 Repository,分别用于操作不同的数据源。
- BookDao1 内容如下:
package com.example.demo.dao1; import com.example.demo.bean.Book; import org.springframework.data.jpa.repository.JpaRepository; public interface BookDao1 extends JpaRepository<Book, Integer> { }
- BookDao2 内容如下:
package com.example.demo.dao2; import com.example.demo.bean.Book; import org.springframework.data.jpa.repository.JpaRepository; public interface BookDao2 extends JpaRepository<Book, Integer> { }
6,创建 Controller
为方便演示,这里省略了 Service 层,将 BookDao1、BookDao2 直接注入到 Controller 中使用:分别向两个数据库中各插入一条数据。@RestController public class HelloController { @Autowired BookDao1 bookDao1; @Autowired BookDao2 bookDao2; @RequestMapping("/test") public void test() { // 往第一个数据库中插入数据 Book book1 = new Book(); book1.setName("时间的秩序"); book1.setAuthor("卡洛·罗韦利"); book1.setPrice(56f); bookDao1.save(book1); //使用save方法将数据保存到数据库 // 往第二个数据库中插入数据 Book book2 = new Book(); book2.setName("发条橙"); book2.setAuthor("安东尼·伯吉斯 "); book2.setPrice(22f); bookDao2.save(book2); //使用save方法将数据保存到数据库 }
7,运行测试
在浏览器中访问 http://localhost:8080/test 地址,可以看到两个数据库中的表和数据都已经存在了。