1. 程式人生 > >Spring Boot 整合 JPA 使用多個數據源

Spring Boot 整合 JPA 使用多個數據源

介紹

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.data").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.data").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("[email protected]", "Charles", "Zhang");
        customer = customerRepository.save(customer);
        assertNotNull(customerRepository.findById(customer.getId()));
        assertEquals(customerRepository.findById(customer.getId()).get().getEmail(), "[email protected]");
    }
}

產品測試類 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 | [email protected] | 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-