Spring Boot 使用JPA 的多数据源示例

x33g5p2x  于2021-10-16 转载在 Spring  
字(13.5k)|赞(0)|评价(0)|浏览(385)

本指南将引导您了解如何使用 Spring Data JPA 在 Spring Boot 应用程序中配置多个数据源。有时需要将应用程序连接到多个数据库(数据源)并根据请求执行操作。
在这种情况下,Spring 框架的灵活性就体现出来了。

1. 我们将构建什么

在本教程中,我们将创建一个全新的 Spring Boot 应用程序,添加所需的依赖项,使用多个数据源(数据库)对其进行配置,公开 REST 端点并执行操作。

例如,我们有两个不同的数据库,即保存学校信息的 schooldb 和保存学生信息的 studentdb

1.1 API – http://localhost:8080/schoolschooldb 数据源获取学校的记录。

[
    {
        "id": 2,
        "name": "BHU",
        "address": "Lanka, Varanasi"
    }
]

1.2 API – http://localhost:8080/studentstudentdb 数据源获取学生的记录。

[
    {
        "id": 1,
        "name": "Pallavi",
        "age": 30
    },
    {
        "id": 2,
        "name": "Sunandana",
        "age": 27
    }
]

2. 我们需要什么

  • JDK 1.8 或更高版本
  • Spring Boot 2.2.1.RELEASE
  • Gradle 4+ 或 Maven 3.2+
  • MySQL 数据库

3. 需要依赖

这是 pom.xml 文件,包括此项目中使用的所需依赖项。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.websparrow</groupId>
	<artifactId>spring-boot-multiple-datasource</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<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>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

4. 项目结构

我们在 STS 4 IDE 中的应用程序的最终项目结构如下所示:

5. 配置数据库连接

将在 application.properties 文件中为两个数据源(即 schooldbstudentdb)配置数据库(数据源)连接字符串。
application.properties

# MySQL database connection strings for SCHOOL
school.datasource.url=jdbc:mysql://localhost:3306/schooldb?createDatabaseIfNotExist=true
school.datasource.username=root
school.datasource.password=root

# MySQL database connection strings for STUDENT
student.datasource.url=jdbc:mysql://localhost:3306/studentdb?createDatabaseIfNotExist=true
student.datasource.username=root
student.datasource.password=root

# JPA property settings
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database=mysql

默认情况下,Spring Boot 将使用前缀为 spring.datasource./* 的配置属性实例化其默认数据源

6. 实体

首先——让我们创建两个简单的实体——每个实体都位于一个单独的数据库中。
Student.java

package org.websparrow.entity.student;

@Entity
@Table(name = "student")
public class Student {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	private String name;
	private int age;
	// Generate Getters and Setters...

}

School.java

package org.websparrow.entity.school;

@Entity
@Table(name = "school")
public class School {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	private String name;
	private String address;
	// Generate Getters and Setters...
}

由于我们已经创建了两个实体,现在我们必须告诉 Spring 哪个实体属于哪个数据源。这可以通过两种方式进行配置:
1.@Table 注释中设置架构属性。

@Entity
@Table(name = "student", schema = "studentdb")
public class Student {
	....
}

2. 在创建 EntityManagerFactoryBuilder 时设置包(在下一步中解释)。

7. 数据源配置

由于我们有两个不同的数据源(数据库),因此需要为这两个数据源创建两个不同的 bean。并确保通过使用 @Primary 注释将一个 bean 作为主要数据源。

7.1 主要数据源

// creates data-source properties bean with student database details
@Bean
@Primary
@ConfigurationProperties(prefix = "student.datasource")
public DataSourceProperties studentDataSourceProperties() {
	return new DataSourceProperties();
}

// creates data-source bean
@Bean
@Primary
public DataSource studentDataSource() {
	return studentDataSourceProperties().initializeDataSourceBuilder()
			.type(BasicDataSource.class).build();
}

如果我们未能将一个数据源设为主要数据源,应用程序将无法启动。

7.2 辅助数据源

@Bean
@ConfigurationProperties(prefix = "school.datasource")
public DataSourceProperties schoolDataSourceProperties() {
	return new DataSourceProperties();
}

@Bean
public DataSource schoolDataSource() {
	return schoolDataSourceProperties().initializeDataSourceBuilder()
			.type(BasicDataSource.class).build();
}

7.3 EntityManagerFactory Bean

为数据源创建 EnttityManager bean。我们将使用 EntityManagerFactory bean 来获取与 JPA 实体交互的 EntityManager 实例。

//creates entity manager with scanned entity classes of student database
@Bean(name = "studentEntityManager")
@Primary
public LocalContainerEntityManagerFactoryBean studentEntityManager(
		EntityManagerFactoryBuilder builder) {
	return builder.dataSource(studentDataSource()).packages(Student.class)
			.build();
}
	
//creates entity manager with scanned entity classes of school database	
@Bean(name = "schoolEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean schoolEntityManagerFactory(
		EntityManagerFactoryBuilder builder) {
	return builder.dataSource(schoolDataSource()).packages(School.class)
			.build();
}

如您所见,我们已经在 packages(School.*class*) 方法中传递了我们的实体。

7.4 事务管理

现在我们将为两个数据源创建 TransactionManager,我们将使用 @Qualifier 注释将特定的实体管理器自动连接到特定的数据源事务管理器。

// Transaction Manager for Student
@Bean(name = "studentTransactionManager")
@Primary
public PlatformTransactionManager studentTransactionManager(
		@Qualifier("studentEntityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
	return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
	
// Transaction Manager for School
@Bean(name = "schoolTransactionManager")
public PlatformTransactionManager schoolTransactionManager(
		@Qualifier("schoolEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
	return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}

7.5 JPA 存储库配置

使用 @EnableJPARepositories 注释配置我们的 JPA 存储库。使用此注释,我们将为每个数据源指定以下属性:

  • basePackages:该属性包含数据源下的所有存储库。
  • entityManagerFactoryRef:该属性包含实体管理器的 bean 名称。
  • transactionManagerRef:该属性包含事务管理器的 bean 名称。
@EnableJpaRepositories(
		basePackages = "org.websparrow.repository.school", 
		entityManagerFactoryRef = "schoolEntityManagerFactory", 
		transactionManagerRef = "schoolTransactionManager"
		)

最终的存储库配置文件如下所示。我们为每个数据源创建了不同的存储库配置。

StudentRepositoryConfiguration.java

package org.websparrow.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.websparrow.entity.student.Student;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
		basePackages = "org.websparrow.repository.student", 
		entityManagerFactoryRef = "studentEntityManager",
		transactionManagerRef = "studentTransactionManager"
		)
public class StudentRepositoryConfiguration {

	// creates data-source properties bean with student database details

	@Bean
	@Primary
	@ConfigurationProperties(prefix = "student.datasource")
	public DataSourceProperties studentDataSourceProperties() {
		return new DataSourceProperties();
	}

	// creates data-source bean

	@Bean
	@Primary
	public DataSource studentDataSource() {
		return studentDataSourceProperties().initializeDataSourceBuilder()
				.type(BasicDataSource.class).build();
	}

	// creates entity manager with scanned entity classes of student database
	@Bean(name = "studentEntityManager")
	@Primary
	public LocalContainerEntityManagerFactoryBean studentEntityManager(
			EntityManagerFactoryBuilder builder) {
		return builder.dataSource(studentDataSource()).packages(Student.class)
				.build();
	}

	@Bean(name = "studentTransactionManager")
	@Primary
	public PlatformTransactionManager studentTransactionManager(
			@Qualifier("studentEntityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
		return new JpaTransactionManager(entityManagerFactoryBean.getObject());
	}
}

SchoolRepositoryConfiguration.java

package org.websparrow.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.websparrow.entity.school.School;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
		basePackages = "org.websparrow.repository.school",
		entityManagerFactoryRef = "schoolEntityManagerFactory",
		transactionManagerRef = "schoolTransactionManager"
		)
public class SchoolRepositoryConfiguration {

	@Bean
	@ConfigurationProperties(prefix = "school.datasource")
	public DataSourceProperties schoolDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	public DataSource schoolDataSource() {
		return schoolDataSourceProperties().initializeDataSourceBuilder()
				.type(BasicDataSource.class).build();
	}

	@Bean(name = "schoolEntityManagerFactory")
	public LocalContainerEntityManagerFactoryBean schoolEntityManagerFactory(
			EntityManagerFactoryBuilder builder) {
		return builder.dataSource(schoolDataSource()).packages(School.class)
				.build();
	}

	@Bean(name = "schoolTransactionManager")
	public PlatformTransactionManager schoolTransactionManager(
			@Qualifier("schoolEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
		return new JpaTransactionManager(entityManagerFactoryBean.getObject());
	}
}

8. 存储库

两个实体的存储库接口。
StudentRepository.java

package org.websparrow.repository.student;

@Repository
public interface StudentRepository
		extends JpaRepository<Student, Integer> {

}

SchoolRepository.java

package org.websparrow.repository.school;

@Repository
public interface SchoolRepository extends JpaRepository<School, Integer> {

}

9. 控制器

MainController 类为应用程序用户公开 REST 端点。在这个控制器类中,我们创建了 2 个不同的 REST 端点,如下所示:

  1. http://localhost:8080/school:从schooldb数据源中检索记录。

  2. http://localhost:8080/student:将从studentdb数据源中检索记录。
    MainController.java

package org.websparrow.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.websparrow.entity.school.School;
import org.websparrow.entity.student.Student;
import org.websparrow.repository.school.SchoolRepository;
import org.websparrow.repository.student.StudentRepository;

@RestController
public class MainController {

	@Autowired
	private SchoolRepository schoolRepository;

	@Autowired
	private StudentRepository studentRepository;

	@GetMapping(value = "school")
	public ResponseEntity<List<School>> getSchool() {
		return ResponseEntity.status(HttpStatus.ACCEPTED)
				.body(schoolRepository.findAll());
	}

	@GetMapping(value = "student")
	public ResponseEntity<List<Student>> getStudent() {
		return ResponseEntity.status(HttpStatus.ACCEPTED)
				.body(studentRepository.findAll());
	}
}

10. 运行应用程序

MultipleDataSourceApplication 类包含主要方法并负责启动应用程序。
MultipleDataSourceApplication.java

package org.websparrow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MultipleDataSourceApplication {

	public static void main(String[] args) {
		SpringApplication.run(MultipleDataSourceApplication.class, args);
	}
}

11. 测试应用程序

要测试应用程序,请通过执行上述类来启动 Spring Boot 应用程序,并一一点击以下 API:

**1.**http://localhost:8080/school

它将从 schooldb 获取数据并返回 JSON,如下所示:

[
    {
        "id": 1,
        "name": "RSMT",
        "address": "UP College Campus, Varanasi"
    },
    {
        "id": 2,
        "name": "BHU",
        "address": "Lanka, Varanasi"
    }
]

**2.**http://localhost:8080/student

它将从 studentdb 获取数据并返回 JSON,如下所示:

[
    {
        "id": 1,
        "name": "Pallavi",
        "age": 30
    },
    {
        "id": 2,
        "name": "Sunandana",
        "age": 27
    },
    {
        "id": 3,
        "name": "Kumud",
        "age": 25
    }
]

相关文章