­

Spring Boot 整合 JPA 使用多个数据源

  • 2019 年 10 月 3 日
  • 筆記

介绍

JPA(Java Persistence API)Java 持久化 API,是 Java 持久化的标准规范,Hibernate 是持久化规范的技术实现,而 Spring Data JPA 是在 Hibernate 基础上封装的一款框架。
第一次使用 Spring JPA 的时候,感觉这东西简直就是神器,几乎不需要写什么关于数据库访问的代码一个基本的 CURD 的功能就出来了。在这篇文章中,我们将介绍 Spring Boot 整合 JPA 使用多个数据源的方法。
开发环境:

  • Spring Boot 2.0.5
  • Spring Data JPA 2.0.5
  • MySQL 5.6
  • JDK 8
  • IDEA 2018.3
  • Windows 10

引入依赖

首先我们要 Spring Boot 引入 spring-boot-starter-data-jpa 依赖。

Maven 配置:

   <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>org.springframework.boot</groupId>          <artifactId>spring-boot-devtools</artifactId>          <scope>runtime</scope>      </dependency>      <dependency>          <groupId>mysql</groupId>          <artifactId>mysql-connector-java</artifactId>          <scope>runtime</scope>      </dependency>

Gradle 配置:

compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.5.RELEASE'  compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.0.5.RELEASE'  compile group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '2.0.5.RELEASE'  compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'

配置数据源

Spring Boot 提供了使用 application.properties 或 application.yml 文件配置项目属性的方法。我比较习惯使用 application.yml 文件,所以这里我只列出 application.yml 文件的写法。

spring:    datasource:      product:        driver-class-name: com.mysql.jdbc.Driver        jdbc-url: jdbc:mysql://127.0.0.1:3306/product?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull        username: root        password: test123$      customer:        driver-class-name: com.mysql.jdbc.Driver        jdbc-url: jdbc:mysql://127.0.0.1:3306/customer?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull        username: root        password: test123$    jpa:      generate-ddl: true

配置好 application.yml 文件后分别在数据库创建 customer 和 product 数据库。

添加实体(Entity)类

客户实体:

package com.springboot.jpa.customer.models;    import javax.persistence.*;    @Entity  public class Customer {        @Id      @GeneratedValue(strategy = GenerationType.IDENTITY)      private Integer id;      @Column(unique = true, nullable = false)      private String email;      private String firstName;      private String lastName;        protected Customer() {      }        public Customer(String email, String firstName, String lastName) {          this.email = email;          this.firstName = firstName;          this.lastName = lastName;      }        @Override      public String toString() {          return String.format("Customer[id=%d, firstName='%s', lastName='%s',email='%s']", id, firstName, lastName, email);      }        public Integer getId() {          return id;      }        public String getEmail() {          return email;      }        public String getFirstName() {          return firstName;      }        public String getLastName() {          return lastName;      }  }

产品实体:

package com.springboot.jpa.product.models;    import javax.persistence.*;    @Entity  public class Product {        @Id      @GeneratedValue(strategy = GenerationType.IDENTITY)      private int id;        @Column(nullable = false)      private String code;      private String name;      private double price;          protected Product() {      }        public Product(String code, String name, double price) {          this.code = code;          this.name = name;          this.price = price;      }        @Override      public String toString() {          return String.format("Product[id=%d, code='%s', name='%s', price='%s']", id, code, name, price);      }        public int getId() {          return id;      }        public String getCode() {          return code;      }        public String getName() {          return name;      }        public double getPrice() {          return price;      }  }  

添加数据仓库(Repository)类

客户 Repository:

package com.springboot.jpa.customer.repository;    import com.springboot.jpa.customer.models.Customer;  import org.springframework.data.jpa.repository.JpaRepository;  import org.springframework.stereotype.Repository;    @Repository  public interface CustomerRepository extends JpaRepository<Customer, Integer> {  }

产品 Repository:

package com.springboot.jpa.product.repository;    import com.springboot.jpa.product.models.Product;  import org.springframework.data.jpa.repository.JpaRepository;  import org.springframework.stereotype.Repository;    @Repository  public interface ProductRepository extends JpaRepository<Product, Integer> {  }

添加配置(Config)类

客户配置:

package com.springboot.jpa.customer.config;    import org.springframework.beans.factory.annotation.Qualifier;  import org.springframework.boot.context.properties.ConfigurationProperties;  import org.springframework.boot.jdbc.DataSourceBuilder;  import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.context.annotation.Primary;  import org.springframework.data.jpa.repository.config.EnableJpaRepositories;  import org.springframework.orm.jpa.JpaTransactionManager;  import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;  import org.springframework.transaction.PlatformTransactionManager;  import org.springframework.transaction.annotation.EnableTransactionManagement;    import javax.persistence.EntityManagerFactory;  import javax.sql.DataSource;    @Configuration  @EnableTransactionManagement  @EnableJpaRepositories(entityManagerFactoryRef = "customerEntityManagerFactory", transactionManagerRef = "customerTransactionManager", basePackages = {"com.springboot.jpa.customer.repository"})  public class CustomerConfig {        @Primary      @Bean(name = "customerDataSource")      @ConfigurationProperties(prefix = "spring.datasource.customer")      public DataSource customerDataSource() {          return DataSourceBuilder.create().build();      }        @Primary      @Bean(name = "customerEntityManagerFactory")      public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("customerDataSource") DataSource dataSource) {          return builder.dataSource(dataSource).packages("com.springboot.jpa.customer.models").persistenceUnit("customer").build();      }        @Primary      @Bean(name = "customerTransactionManager")      public PlatformTransactionManager customerTransactionManager(@Qualifier("customerEntityManagerFactory") EntityManagerFactory customerEntityManagerFactory) {          return new JpaTransactionManager(customerEntityManagerFactory);      }  }

产品配置:

package com.springboot.jpa.product.config;      import org.springframework.beans.factory.annotation.Qualifier;  import org.springframework.boot.context.properties.ConfigurationProperties;  import org.springframework.boot.jdbc.DataSourceBuilder;  import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.data.jpa.repository.config.EnableJpaRepositories;  import org.springframework.orm.jpa.JpaTransactionManager;  import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;  import org.springframework.transaction.PlatformTransactionManager;  import org.springframework.transaction.annotation.EnableTransactionManagement;    import javax.persistence.EntityManagerFactory;  import javax.sql.DataSource;    @Configuration  @EnableTransactionManagement  @EnableJpaRepositories(entityManagerFactoryRef = "productEntityManagerFactory", transactionManagerRef = "productTransactionManager", basePackages = {"com.springboot.jpa.product.repository"}  )  public class ProductConfig {        @Bean(name = "productDataSource")      @ConfigurationProperties(prefix = "spring.datasource.product")      public DataSource dataSource() {          return DataSourceBuilder.create().build();      }        @Bean(name = "productEntityManagerFactory")      public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("productDataSource") DataSource dataSource) {          return builder.dataSource(dataSource).packages("com.springboot.jpa.product.models").persistenceUnit("product").build();      }        @Bean(name = "productTransactionManager")      public PlatformTransactionManager productTransactionManager(@Qualifier("productEntityManagerFactory") EntityManagerFactory productEntityManagerFactory) {          return new JpaTransactionManager(productEntityManagerFactory);      }  }

项目结构:

src/main/java  - com.springboot.jpa        - product          - config          - models          - repository        - customer          - config          - models          - repository

添加测试类

客户测试类 CustomerDataSourcesTests:

package com.springboot.jpa;    import com.springboot.jpa.customer.repository.CustomerRepository;  import com.springboot.jpa.customer.models.Customer;  import org.junit.runner.RunWith;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.boot.test.context.SpringBootTest;  import org.springframework.test.context.junit4.SpringRunner;  import org.springframework.transaction.annotation.Transactional;    import org.junit.Test;    import static org.junit.Assert.assertEquals;  import static org.junit.Assert.assertNotNull;    @RunWith(SpringRunner.class)  @SpringBootTest  public class CustomerDataSourcesTests {        @Autowired      private CustomerRepository customerRepository;        @Test      @Transactional("customerTransactionManager")      public void createCustomer() {            Customer customer = new Customer("master@weilog.net", "Charles", "Zhang");          customer = customerRepository.save(customer);          assertNotNull(customerRepository.findById(customer.getId()));          assertEquals(customerRepository.findById(customer.getId()).get().getEmail(), "master@weilog.net");      }  }

产品测试类 ProductDataSourcesTests:

package com.springboot.jpa;    import com.springboot.jpa.product.models.Product;  import com.springboot.jpa.product.repository.ProductRepository;  import org.junit.runner.RunWith;  import org.springframework.beans.factory.annotation.Autowired;  import org.springframework.boot.test.context.SpringBootTest;  import org.springframework.test.context.junit4.SpringRunner;  import org.springframework.transaction.annotation.Transactional;    import org.junit.Test;    import static org.junit.Assert.assertNotNull;    @RunWith(SpringRunner.class)  @SpringBootTest  public class ProductDataSourcesTests {        @Autowired      private ProductRepository productRepository;        @Test      @Transactional("productTransactionManager")      public void createProduct() {          Product product = new Product("10000", "Book", 80.0);          product = productRepository.save(product);            assertNotNull(productRepository.findById(product.getId()));      }    }

测试

分别运行两个测试类通过后,查询数据库。
客户表:

mysql> SELECT * FROM customer;  +----+-------------------+-----------+----------+  | id | email             | firstName | lastName |  +----+-------------------+-----------+----------+  |  1 | master@weilog.net | Charles   | Zhang    |  +----+-------------------+-----------+----------+  1 row in set

产品表:

mysql> SELECT * FROM product;  +----+-------+------+-------+  | id | code  | name | price |  +----+-------+------+-------+  |  1 | 10000 | Book |    80 |  +----+-------+------+-------+  1 row in set

本文地址:Spring Boot 整合 JPA 使用多个数据源
项目地址:spring-boot-jpa