SQL 数据库

Spring Framework 提供了广泛的支持来处理 SQL 数据库,从使用 JdbcClientJdbcTemplate 进行直接 JDBC 访问到完整的“对象关系映射”技术(如 Hibernate)。 Spring Data 提供了额外的功能级别:直接从接口创建 Repository 实现,并使用约定从你的方法名称生成查询。

配置数据源

Java 的 javax.sql.DataSource 接口提供了一种处理数据库连接的标准方法。传统上,DataSource 使用 URL 以及一些凭据来建立数据库连接。

有关更高级的示例,请参阅“入门指南”中的配置自定义数据源部分,这些示例通常用于完全控制数据源的配置。

嵌入式数据库支持

使用内存中的嵌入式数据库来开发应用程序通常很方便。显然,内存数据库不提供持久性存储。您需要在应用程序启动时填充数据库,并准备好应用程序结束时丢弃数据。

“入门指南”部分包含一个有关如何初始化数据库的部分

Spring Boot 可以自动配置嵌入式H2HSQLDerby数据库。您无需提供任何连接 URL。您只需要在构建依赖项中包含要使用的嵌入式数据库即可。如果类路径上有多个嵌入式数据库,请设置spring.datasource.embedded-database-connection配置属性以控制使用哪个数据库。将该属性设置为none将禁用嵌入式数据库的自动配置。

如果您在测试中使用此功能,您可能会注意到,无论您使用多少个应用程序上下文,整个测试套件都会重复使用同一个数据库。如果您希望确保每个上下文都有一个单独的嵌入式数据库,则应将spring.datasource.generate-unique-name设置为true

例如,典型的 POM 依赖项如下所示

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.hsqldb</groupId>
	<artifactId>hsqldb</artifactId>
	<scope>runtime</scope>
</dependency>
要自动配置嵌入式数据库,您需要依赖spring-jdbc。在本例中,它通过spring-boot-starter-data-jpa传递引入。
如果出于任何原因,您确实配置了嵌入式数据库的连接 URL,请注意确保禁用了数据库的自动关闭。如果您使用 H2,则应使用DB_CLOSE_ON_EXIT=FALSE来实现。如果您使用 HSQLDB,则应确保不使用shutdown=true。禁用数据库的自动关闭可以让 Spring Boot 控制何时关闭数据库,从而确保在不再需要访问数据库时关闭它。

连接到生产数据库

生产数据库连接也可以通过使用池化DataSource来自动配置。

DataSource 配置

DataSource 配置由spring.datasource.*中的外部配置属性控制。例如,您可以在application.properties中声明以下部分

  • 属性

  • YAML

spring.datasource.url=jdbc:mysql://127.0.0.1/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring:
  datasource:
    url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
您至少应通过设置spring.datasource.url属性来指定 URL。否则,Spring Boot 会尝试自动配置嵌入式数据库。
Spring Boot 可以从 URL 中推断出大多数数据库的 JDBC 驱动程序类。如果您需要指定特定的类,可以使用spring.datasource.driver-class-name属性。
要创建池化DataSource,我们需要能够验证有效的Driver类是否可用,因此我们在执行任何操作之前都会检查这一点。换句话说,如果您设置spring.datasource.driver-class-name=com.mysql.jdbc.Driver,则该类必须是可加载的。

有关支持的更多选项,请参阅DataSourceProperties API 文档。这些是标准选项,无论实际实现如何都适用。还可以使用各自的前缀(spring.datasource.hikari.*spring.datasource.tomcat.*spring.datasource.dbcp2.*spring.datasource.oracleucp.*)微调特定于实现的设置。有关更多详细信息,请参阅您正在使用的连接池实现的文档。

例如,如果您使用Tomcat 连接池,您可以自定义许多其他设置,如下例所示

  • 属性

  • YAML

spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.test-on-borrow=true
spring:
  datasource:
    tomcat:
      max-wait: 10000
      max-active: 50
      test-on-borrow: true

这将设置池在没有可用连接时等待 10000 毫秒后再抛出异常,将最大连接数限制为 50,并在从池中借用连接之前验证连接。

支持的连接池

Spring Boot 使用以下算法选择特定的实现

  1. 我们更喜欢HikariCP,因为它具有良好的性能和并发性。如果 HikariCP 可用,我们始终选择它。

  2. 否则,如果 Tomcat 池化DataSource可用,则我们使用它。

  3. 否则,如果Commons DBCP2可用,则我们使用它。

  4. 如果 HikariCP、Tomcat 和 DBCP2 均不可用,并且 Oracle UCP 可用,则我们使用它。

如果您使用spring-boot-starter-jdbcspring-boot-starter-data-jpa启动器,则会自动获得对HikariCP的依赖项。

您可以完全绕过该算法,并通过设置spring.datasource.type属性来指定要使用的连接池。如果您在 Tomcat 容器中运行应用程序,这尤其重要,因为tomcat-jdbc默认提供。

可以使用DataSourceBuilder始终手动配置其他连接池。如果您定义了自己的DataSourcebean,则不会发生自动配置。以下连接池受DataSourceBuilder支持

  • HikariCP

  • Tomcat 池化DataSource

  • Commons DBCP2

  • Oracle UCP 和OracleDataSource

  • Spring 框架的SimpleDriverDataSource

  • H2 JdbcDataSource

  • PostgreSQL PGSimpleDataSource

  • C3P0

连接到 JNDI 数据源

如果您将 Spring Boot 应用程序部署到应用程序服务器,您可能希望使用应用程序服务器的内置功能配置和管理您的 DataSource,并使用 JNDI 访问它。

spring.datasource.jndi-name属性可以用作spring.datasource.urlspring.datasource.usernamespring.datasource.password属性的替代方案,以从特定的 JNDI 位置访问DataSource。例如,application.properties中的以下部分显示了如何访问 JBoss AS 定义的DataSource

  • 属性

  • YAML

spring.datasource.jndi-name=java:jboss/datasources/customers
spring:
  datasource:
    jndi-name: "java:jboss/datasources/customers"

使用 JdbcTemplate

Spring 的JdbcTemplateNamedParameterJdbcTemplate类已自动配置,您可以将它们直接自动装配到您自己的 bean 中,如下例所示

  • Java

  • Kotlin

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

	private final JdbcTemplate jdbcTemplate;

	public MyBean(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public void doSomething() {
		this.jdbcTemplate ...
	}

}
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component

@Component
class MyBean(private val jdbcTemplate: JdbcTemplate) {

	fun doSomething() {
		jdbcTemplate.execute("delete from customer")
	}

}

您可以使用spring.jdbc.template.*属性自定义模板的一些属性,如下例所示

  • 属性

  • YAML

spring.jdbc.template.max-rows=500
spring:
  jdbc:
    template:
      max-rows: 500
NamedParameterJdbcTemplate在幕后重用相同的JdbcTemplate实例。如果定义了多个JdbcTemplate并且不存在主要候选者,则不会自动配置NamedParameterJdbcTemplate

使用 JdbcClient

Spring 的JdbcClient基于NamedParameterJdbcTemplate的存在进行自动配置。您也可以将其直接注入到您自己的 bean 中,如下例所示

  • Java

  • Kotlin

import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

	private final JdbcClient jdbcClient;

	public MyBean(JdbcClient jdbcClient) {
		this.jdbcClient = jdbcClient;
	}

	public void doSomething() {
		this.jdbcClient ...
	}

}
import org.springframework.jdbc.core.simple.JdbcClient
import org.springframework.stereotype.Component

@Component
class MyBean(private val jdbcClient: JdbcClient) {

	fun doSomething() {
		jdbcClient.sql("delete from customer").update()
	}

}

如果您依赖自动配置来创建底层的JdbcTemplate,则客户端也会考虑使用spring.jdbc.template.*属性进行的任何自定义。

JPA 和 Spring Data JPA

Java 持久性 API 是一种标准技术,允许您将对象“映射”到关系数据库。spring-boot-starter-data-jpa POM 提供了一种快速入门的方法。它提供以下关键依赖项

  • Hibernate:最流行的 JPA 实现之一。

  • Spring Data JPA:帮助您实现基于 JPA 的存储库。

  • Spring ORM:来自 Spring 框架的核心 ORM 支持。

我们在这里不会详细介绍 JPA 或Spring Data。您可以按照使用 JPA 访问数据指南(来自spring.io)并阅读Spring Data JPAHibernate参考文档。

实体类

传统上,JPA“实体”类是在persistence.xml文件中指定的。使用 Spring Boot,此文件不是必需的,而是使用“实体扫描”。默认情况下,会扫描自动配置包

任何用@Entity@Embeddable@MappedSuperclass注释的类都被视为实体类。典型的实体类类似于以下示例

  • Java

  • Kotlin

import java.io.Serializable;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class City implements Serializable {

	@Id
	@GeneratedValue
	private Long id;

	@Column(nullable = false)
	private String name;

	@Column(nullable = false)
	private String state;

	// ... additional members, often include @OneToMany mappings

	protected City() {
		// no-args constructor required by JPA spec
		// this one is protected since it should not be used directly
	}

	public City(String name, String state) {
		this.name = name;
		this.state = state;
	}

	public String getName() {
		return this.name;
	}

	public String getState() {
		return this.state;
	}

	// ... etc

}
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id
import java.io.Serializable

@Entity
class City : Serializable {

	@Id
	@GeneratedValue
	private val id: Long? = null

	@Column(nullable = false)
	var name: String? = null
		private set

	// ... etc
	@Column(nullable = false)
	var state: String? = null
		private set

	// ... additional members, often include @OneToMany mappings

	protected constructor() {
		// no-args constructor required by JPA spec
		// this one is protected since it should not be used directly
	}

	constructor(name: String?, state: String?) {
		this.name = name
		this.state = state
	}

}
您可以使用@EntityScan注释自定义实体扫描位置。请参阅“入门指南”中的将 @Entity 定义与 Spring 配置分离部分。

Spring Data JPA 存储库

Spring Data JPA存储库是您可以定义以访问数据的接口。JPA 查询会根据您的方法名称自动创建。例如,CityRepository接口可能会声明一个findAllByState(String state)方法来查找给定州中的所有城市。

对于更复杂的查询,您可以使用 Spring Data 的Query注释来注释您的方法。

Spring Data 存储库通常扩展自RepositoryCrudRepository接口。如果您使用自动配置,则会搜索自动配置包以查找存储库。

您可以使用@EnableJpaRepositories自定义查找存储库的位置。

以下示例显示了典型的 Spring Data 存储库接口定义

  • Java

  • Kotlin

import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;

public interface CityRepository extends Repository<City, Long> {

	Page<City> findAll(Pageable pageable);

	City findByNameAndStateAllIgnoringCase(String name, String state);

}
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.Repository

interface CityRepository : Repository<City?, Long?> {

	fun findAll(pageable: Pageable?): Page<City?>?

	fun findByNameAndStateAllIgnoringCase(name: String?, state: String?): City?

}

Spring Data JPA 存储库支持三种不同的引导模式:默认、延迟和懒加载。要启用延迟或懒加载引导,请分别将spring.data.jpa.repositories.bootstrap-mode属性设置为deferredlazy。使用延迟或懒加载引导时,自动配置的EntityManagerFactoryBuilder将使用上下文的AsyncTaskExecutor(如果存在)作为引导执行程序。如果存在多个,则将使用名为applicationTaskExecutor的那个。

使用延迟或懒加载引导时,请确保在应用程序上下文引导阶段之后延迟对 JPA 基础设施的任何访问。您可以使用SmartInitializingSingleton来调用任何需要 JPA 基础设施的初始化。对于作为 Spring bean 创建的 JPA 组件(例如转换器),请使用ObjectProvider来延迟任何依赖项(如果存在)的解析。

我们仅仅触及了 Spring Data JPA 的表面。有关完整详细信息,请参阅Spring Data JPA 参考文档

Spring Data Envers 存储库

如果启用了 Spring Data Envers,JPA 存储库将自动配置为支持典型的 Envers 查询。

要使用 Spring Data Envers,请确保您的存储库扩展自 RevisionRepository,如下例所示

  • Java

  • Kotlin

import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.Country;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.history.RevisionRepository;

public interface CountryRepository extends RevisionRepository<Country, Long, Integer>, Repository<Country, Long> {

	Page<Country> findAll(Pageable pageable);

}
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.Country
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.Repository
import org.springframework.data.repository.history.RevisionRepository

interface CountryRepository :
		RevisionRepository<Country?, Long?, Int>,
		Repository<Country?, Long?> {

	fun findAll(pageable: Pageable?): Page<Country?>?

}
有关更多详细信息,请查看 Spring Data Envers 参考文档

创建和删除 JPA 数据库

默认情况下,仅当您使用嵌入式数据库(H2、HSQL 或 Derby)时,JPA 数据库才会自动创建。您可以使用 spring.jpa.* 属性显式配置 JPA 设置。例如,要创建和删除表,您可以将以下行添加到您的 application.properties

  • 属性

  • YAML

spring.jpa.hibernate.ddl-auto=create-drop
spring:
  jpa:
    hibernate.ddl-auto: "create-drop"
Hibernate 自身的内部属性名称(如果您碰巧记得它)是 hibernate.hbm2ddl.auto。您可以使用 spring.jpa.properties.* 设置它以及其他 Hibernate 本地属性(在将它们添加到实体管理器之前会去除前缀)。以下行显示了设置 Hibernate 的 JPA 属性的示例
  • 属性

  • YAML

spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring:
  jpa:
    properties:
      hibernate:
        "globally_quoted_identifiers": "true"

前面示例中的行将 hibernate.globally_quoted_identifiers 属性的值 true 传递给 Hibernate 实体管理器。

默认情况下,DDL 执行(或验证)将推迟到 ApplicationContext 启动之后。

在视图中打开实体管理器

如果您正在运行 Web 应用程序,则 Spring Boot 默认情况下会注册 OpenEntityManagerInViewInterceptor 以应用“在视图中打开实体管理器”模式,以允许在 Web 视图中进行延迟加载。如果您不希望此行为,则应在 application.properties 中将 spring.jpa.open-in-view 设置为 false

Spring Data JDBC

Spring Data 包含对 JDBC 的存储库支持,并将自动为 CrudRepository 上的方法生成 SQL。对于更高级的查询,提供了 @Query 注解。

当类路径上存在必要的依赖项时,Spring Boot 将自动配置 Spring Data 的 JDBC 存储库。可以通过在项目中添加对 spring-boot-starter-data-jdbc 的单个依赖项来添加它们。如有必要,可以通过添加 @EnableJdbcRepositories 注解或 AbstractJdbcConfiguration 子类到您的应用程序中来控制 Spring Data JDBC 的配置。

有关 Spring Data JDBC 的完整详细信息,请参阅 参考文档

使用 H2 的 Web 控制台

H2 数据库 提供了一个 基于浏览器的控制台,Spring Boot 可以为您自动配置。当满足以下条件时,控制台将自动配置

如果您没有使用 Spring Boot 的开发者工具,但仍然希望使用 H2 的控制台,则可以使用 spring.h2.console.enabled 属性并将其值设置为 true 进行配置。
H2 控制台仅供开发使用,因此您应该注意确保在生产环境中 spring.h2.console.enabled 未设置为 true

更改 H2 控制台的路径

默认情况下,控制台可在 /h2-console 访问。您可以使用 spring.h2.console.path 属性自定义控制台的路径。

在受保护的应用程序中访问 H2 控制台

H2 控制台使用框架,并且由于它仅用于开发,因此没有实现 CSRF 保护措施。如果您的应用程序使用 Spring Security,则需要将其配置为

  • 禁用针对控制台的请求的 CSRF 保护,

  • 在来自控制台的响应中将 X-Frame-Options 标头设置为 SAMEORIGIN

有关 CSRF 和标头 X-Frame-Options 的更多信息,请参阅 Spring Security 参考指南。

在简单的设置中,可以使用以下 SecurityFilterChain

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
import org.springframework.security.web.SecurityFilterChain;

@Profile("dev")
@Configuration(proxyBeanMethods = false)
public class DevProfileSecurityConfiguration {

	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	SecurityFilterChain h2ConsoleSecurityFilterChain(HttpSecurity http) throws Exception {
		http.securityMatcher(PathRequest.toH2Console());
		http.authorizeHttpRequests(yourCustomAuthorization());
		http.csrf(CsrfConfigurer::disable);
		http.headers((headers) -> headers.frameOptions(FrameOptionsConfig::sameOrigin));
		return http.build();
	}


}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.SecurityFilterChain

@Profile("dev")
@Configuration(proxyBeanMethods = false)
class DevProfileSecurityConfiguration {

	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	fun h2ConsoleSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
		return http.authorizeHttpRequests(yourCustomAuthorization())
			.csrf { csrf -> csrf.disable() }
			.headers { headers -> headers.frameOptions { frameOptions -> frameOptions.sameOrigin() } }
			.build()
	}


}
H2 控制台仅供开发使用。在生产环境中,禁用 CSRF 保护或允许网站的框架可能会造成严重的安全性风险。
PathRequest.toH2Console() 即使在自定义控制台路径时也能返回正确的请求匹配器。

使用 jOOQ

jOOQ 面向对象查询 (jOOQ) 是 Data Geekery 的一款流行产品,它根据您的数据库生成 Java 代码,并允许您通过其流畅的 API 构建类型安全的 SQL 查询。Spring Boot 可以使用商业版和开源版。

代码生成

为了使用 jOOQ 类型安全的查询,您需要根据数据库模式生成 Java 类。您可以按照 jOOQ 用户手册 中的说明进行操作。如果您使用 jooq-codegen-maven 插件并且还使用 spring-boot-starter-parent 的“父 POM”,则可以安全地省略插件的 <version> 标记。您还可以使用 Spring Boot 定义的版本变量(例如 h2.version)来声明插件的数据库依赖项。以下列表显示了一个示例

<plugin>
	<groupId>org.jooq</groupId>
	<artifactId>jooq-codegen-maven</artifactId>
	<executions>
		...
	</executions>
	<dependencies>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>${h2.version}</version>
		</dependency>
	</dependencies>
	<configuration>
		<jdbc>
			<driver>org.h2.Driver</driver>
			<url>jdbc:h2:~/yourdatabase</url>
		</jdbc>
		<generator>
			...
		</generator>
	</configuration>
</plugin>

使用 DSLContext

jOOQ 提供的流畅 API 通过 org.jooq.DSLContext 接口启动。Spring Boot 自动配置 DSLContext 作为 Spring Bean,并将其连接到您的应用程序 DataSource。要使用 DSLContext,您可以将其注入,如下例所示

  • Java

  • Kotlin

import java.util.GregorianCalendar;
import java.util.List;

import org.jooq.DSLContext;

import org.springframework.stereotype.Component;

import static org.springframework.boot.docs.data.sql.jooq.dslcontext.Tables.AUTHOR;

@Component
public class MyBean {

	private final DSLContext create;

	public MyBean(DSLContext dslContext) {
		this.create = dslContext;
	}


}
import org.jooq.DSLContext
import org.springframework.stereotype.Component
import java.util.GregorianCalendar

@Component
class MyBean(private val create: DSLContext) {


}
jOOQ 手册倾向于使用名为 create 的变量来保存 DSLContext

然后,您可以使用 DSLContext 来构建查询,如下例所示

  • Java

  • Kotlin

	public List<GregorianCalendar> authorsBornAfter1980() {
		return this.create.selectFrom(AUTHOR)
			.where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1)))
			.fetch(AUTHOR.DATE_OF_BIRTH);
	fun authorsBornAfter1980(): List<GregorianCalendar> {
		return create.selectFrom<Tables.TAuthorRecord>(Tables.AUTHOR)
			.where(Tables.AUTHOR?.DATE_OF_BIRTH?.greaterThan(GregorianCalendar(1980, 0, 1)))
			.fetch(Tables.AUTHOR?.DATE_OF_BIRTH)
	}

jOOQ SQL 方言

除非已配置 spring.jooq.sql-dialect 属性,否则 Spring Boot 会确定要用于数据源的 SQL 方言。如果 Spring Boot 无法检测到方言,则它使用 DEFAULT

Spring Boot 只能自动配置 jOOQ 开源版本支持的方言。

自定义 jOOQ

可以通过定义您自己的 DefaultConfigurationCustomizer bean 来实现更高级的自定义,该 bean 将在创建 org.jooq.Configuration @Bean 之前被调用。这优先于自动配置应用的任何内容。

如果您希望完全控制 jOOQ 配置,还可以创建您自己的 org.jooq.Configuration @Bean

使用 R2DBC

Reactive Relational Database Connectivity (R2DBC) 项目将反应式编程 API 引入关系数据库。R2DBC 的 io.r2dbc.spi.Connection 提供了一种使用非阻塞数据库连接的标准方法。连接是通过使用 ConnectionFactory 提供的,类似于 jdbc 中的 DataSource

ConnectionFactory 配置由 spring.r2dbc.* 中的外部配置属性控制。例如,您可以在 application.properties 中声明以下部分

  • 属性

  • YAML

spring.r2dbc.url=r2dbc:postgresql://127.0.0.1/test
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpass
spring:
  r2dbc:
    url: "r2dbc:postgresql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
您不需要指定驱动程序类名,因为 Spring Boot 从 R2DBC 的连接工厂发现中获取驱动程序。
至少应提供 url。URL 中指定的信息优先于各个属性,即 nameusernamepassword 和池选项。
“操作指南”部分包含一个 有关如何初始化数据库的部分

要自定义 ConnectionFactory 创建的连接,即设置您不想(或无法)在中央数据库配置中配置的特定参数,您可以使用 ConnectionFactoryOptionsBuilderCustomizer @Bean。以下示例显示了如何在手动覆盖数据库端口的同时,从应用程序配置中获取其余选项

  • Java

  • Kotlin

import io.r2dbc.spi.ConnectionFactoryOptions;

import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyR2dbcConfiguration {

	@Bean
	public ConnectionFactoryOptionsBuilderCustomizer connectionFactoryPortCustomizer() {
		return (builder) -> builder.option(ConnectionFactoryOptions.PORT, 5432);
	}

}
import io.r2dbc.spi.ConnectionFactoryOptions
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyR2dbcConfiguration {

	@Bean
	fun connectionFactoryPortCustomizer(): ConnectionFactoryOptionsBuilderCustomizer {
		return ConnectionFactoryOptionsBuilderCustomizer { builder ->
			builder.option(ConnectionFactoryOptions.PORT, 5432)
		}
	}

}

以下示例显示了如何设置一些 PostgreSQL 连接选项

  • Java

  • Kotlin

import java.util.HashMap;
import java.util.Map;

import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider;

import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyPostgresR2dbcConfiguration {

	@Bean
	public ConnectionFactoryOptionsBuilderCustomizer postgresCustomizer() {
		Map<String, String> options = new HashMap<>();
		options.put("lock_timeout", "30s");
		options.put("statement_timeout", "60s");
		return (builder) -> builder.option(PostgresqlConnectionFactoryProvider.OPTIONS, options);
	}

}
import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyPostgresR2dbcConfiguration {

	@Bean
	fun postgresCustomizer(): ConnectionFactoryOptionsBuilderCustomizer {
		val options: MutableMap<String, String> = HashMap()
		options["lock_timeout"] = "30s"
		options["statement_timeout"] = "60s"
		return ConnectionFactoryOptionsBuilderCustomizer { builder ->
			builder.option(PostgresqlConnectionFactoryProvider.OPTIONS, options)
		}
	}

}

ConnectionFactory bean 可用时,常规 JDBC DataSource 自动配置将回退。如果您希望保留 JDBC DataSource 自动配置,并且可以接受在反应式应用程序中使用阻塞式 JDBC API 的风险,请在应用程序中的 @Configuration 类上添加 @Import(DataSourceAutoConfiguration.class) 以重新启用它。

嵌入式数据库支持

JDBC 支持 类似,Spring Boot 可以自动配置嵌入式数据库以进行反应式使用。您无需提供任何连接 URL。您只需要包含对要使用的嵌入式数据库的构建依赖项,如下例所示

<dependency>
	<groupId>io.r2dbc</groupId>
	<artifactId>r2dbc-h2</artifactId>
	<scope>runtime</scope>
</dependency>

如果您在测试中使用此功能,您可能会注意到,无论您使用多少个应用程序上下文,整个测试套件都会重用相同的数据库。如果您希望确保每个上下文都有一个单独的嵌入式数据库,则应将 spring.r2dbc.generate-unique-name 设置为 true

使用 DatabaseClient

DatabaseClient bean 是自动配置的,您可以将其直接自动装配到您自己的 bean 中,如下例所示

  • Java

  • Kotlin

import java.util.Map;

import reactor.core.publisher.Flux;

import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

	private final DatabaseClient databaseClient;

	public MyBean(DatabaseClient databaseClient) {
		this.databaseClient = databaseClient;
	}

	// ...

	public Flux<Map<String, Object>> someMethod() {
		return this.databaseClient.sql("select * from user").fetch().all();
	}

}
import org.springframework.r2dbc.core.DatabaseClient
import org.springframework.stereotype.Component
import reactor.core.publisher.Flux

@Component
class MyBean(private val databaseClient: DatabaseClient) {

	// ...

	fun someMethod(): Flux<Map<String, Any>> {
		return databaseClient.sql("select * from user").fetch().all()
	}

}

Spring Data R2DBC 存储库

Spring Data R2DBC 存储库是您可以定义以访问数据的接口。查询会根据您的方法名称自动创建。例如,CityRepository 接口可能会声明一个 findAllByState(String state) 方法来查找给定州中的所有城市。

对于更复杂的查询,您可以使用 Spring Data 的 @Query 注解来注释您的方法。

Spring Data 存储库通常扩展自RepositoryCrudRepository接口。如果您使用自动配置,则会搜索自动配置包以查找存储库。

以下示例显示了典型的 Spring Data 存储库接口定义

  • Java

  • Kotlin

import reactor.core.publisher.Mono;

import org.springframework.data.repository.Repository;

public interface CityRepository extends Repository<City, Long> {

	Mono<City> findByNameAndStateAllIgnoringCase(String name, String state);

}
import org.springframework.data.repository.Repository
import reactor.core.publisher.Mono

interface CityRepository : Repository<City?, Long?> {

	fun findByNameAndStateAllIgnoringCase(name: String?, state: String?): Mono<City?>?

}
我们只是触及了 Spring Data R2DBC 的皮毛。有关完整详细信息,请参阅 Spring Data R2DBC 参考文档