SQL 数据库

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

配置 DataSource

Java 的 DataSource 接口提供了一种使用数据库连接的标准方法。传统上,DataSource 使用 `URL` 和一些凭据来建立数据库连接。

请参阅“操作指南”中的 配置自定义 DataSource 部分,了解更多高级示例,通常用于完全控制 DataSource 的配置。

嵌入式数据库支持

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

“操作指南”部分包含一个 关于如何初始化数据库的章节

Spring Boot 可以自动配置嵌入式的 H2HSQLDerby 数据库。您无需提供任何连接 URL。您只需包含所需使用的嵌入式数据库的构建依赖项即可。如果 classpath 上存在多个嵌入式数据库,请设置 `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://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring:
  datasource:
    url: "jdbc:mysql://localhost/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

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

支持的连接池

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

  1. 我们优先选择 HikariCP,因为它具有卓越的性能和并发性。如果 HikariCP 可用,我们总是选择它。

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

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

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

如果您使用 `spring-boot-starter-jdbc` 或 `spring-boot-starter-data-jpa` starter,您将自动获得 HikariCP 的依赖。

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

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

连接到 JNDI DataSource

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

`spring.datasource.jndi-name` 属性可以作为 `spring.datasource.url`、`spring.datasource.username` 和 `spring.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 的 Repository。

  • Spring ORM:Spring Framework 提供的核心 ORM 支持。

我们在此不详细介绍 JPA 或 Spring Data。您可以按照 spring.io 上的 使用 JPA 访问数据 指南,并阅读 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 Repository

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

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

Spring Data Repository 通常继承自 RepositoryCrudRepository 接口。如果您使用自动配置,则会在自动配置包中搜索 Repository。

您可以使用 @EnableJpaRepositories 来自定义查找 Repository 的位置。

以下示例显示了一个典型的 Spring Data Repository 接口定义:

  • 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 Repository 支持三种不同的引导模式:default(默认)、deferred(延迟)和 lazy(懒加载)。要启用延迟或懒加载引导,请分别将 `spring.data.jpa.repositories.bootstrap-mode` 属性设置为 `deferred` 或 `lazy`。当使用延迟或懒加载引导时,自动配置的 EntityManagerFactoryBuilder 将使用上下文中的 AsyncTaskExecutor(如果存在)作为引导执行器。如果存在多个,将使用名称为 `applicationTaskExecutor` 的执行器。

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

我们对 Spring Data JPA 只做了初步的介绍。要获取完整详细信息,请参阅 Spring Data JPA 参考文档

Spring Data Envers Repository

如果 Spring Data Envers 可用,则会自动配置 JPA Repository 以支持典型的 Envers 查询。

要使用 Spring Data Envers,请确保您的 Repository 继承自 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 启动后进行。

在 View 中打开 EntityManager

如果您正在运行 Web 应用,Spring Boot 默认会注册 OpenEntityManagerInViewInterceptor 来应用“在 View 中打开 EntityManager”模式,以便在 Web 视图中实现延迟加载。如果您不希望这种行为,您应该在 `application.properties` 中将 `spring.jpa.open-in-view` 设置为 `false`。

Spring Data JDBC

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

当 classpath 上存在必要的依赖项时,Spring Boot 会自动配置 Spring Data 的 JDBC Repository。您只需添加 `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`。

有关 CSRFX-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 Object Oriented Querying (jOOQ) 是 Data Geekery 的一款流行产品,它可以根据您的数据库生成 Java 代码,并通过其流畅的 API 构建类型安全的 SQL 查询。商业版和开源版都可以与 Spring Boot 一起使用。

代码生成

为了使用 jOOQ 的类型安全查询,您需要根据数据库 schema 生成 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 通过 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,可以在创建 Configuration @Bean 之前进行更高级的定制。这优先于自动配置应用的任何内容。

如果您想完全控制 jOOQ 配置,也可以创建自己的 Configuration @Bean

使用 R2DBC

反应式关系数据库连接 (R2DBC) 项目为关系数据库带来了反应式编程 API。R2DBC 的 Connection 提供了一种处理非阻塞数据库连接的标准方法。连接是通过使用 ConnectionFactory 提供的,类似于 JDBC 的 DataSource

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

  • 属性文件

  • YAML

spring.r2dbc.url=r2dbc:postgresql://localhost/test
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpass
spring:
  r2dbc:
    url: "r2dbc:postgresql://localhost/test"
    username: "dbuser"
    password: "dbpass"
您无需指定驱动类名称,因为 Spring Boot 会从 R2DBC 的 Connection Factory 发现机制中获取驱动。
至少需要提供 url。URL 中指定的信息优先于单个属性,即 nameusernamepassword 和连接池选项。
“How-to Guides” 部分包含关于如何初始化数据库的部分。

要定制由 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 Repositories

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

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

Spring Data Repository 通常继承自 RepositoryCrudRepository 接口。如果您使用自动配置,则会在自动配置包中搜索 Repository。

以下示例显示了一个典型的 Spring Data Repository 接口定义:

  • 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 参考文档