SQL 数据库
Spring Framework 为使用 SQL 数据库提供了广泛的支持,从使用 JdbcClient 或 JdbcTemplate 进行直接的 JDBC 访问,到完整的“对象关系映射”(ORM)技术(例如 Hibernate)。Spring Data 提供了更高层次的功能:直接从接口创建 Repository 实现,并使用约定根据方法名自动生成查询。
配置 DataSource
Java 的 DataSource 接口提供了一种操作数据库连接的标准方法。传统上,DataSource 使用一个 URL 以及一些凭据来建立数据库连接。
有关更高级的示例(通常用于完全控制 DataSource 的配置),请参阅“操作指南”中的 配置自定义 DataSource 一节。
嵌入式数据库支持
使用内存嵌入式数据库来开发应用程序通常非常方便。显然,内存数据库不提供持久化存储。你需要在应用程序启动时填充数据库,并在应用程序结束时准备好丢弃数据。
“操作指南”部分包含了一个关于如何初始化数据库的章节。
如果你在测试中使用此功能,可能会注意到无论你使用多少个应用上下文,整个测试套件都会复用同一个数据库。如果你希望确保每个上下文都拥有一个独立的嵌入式数据库,应将 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 中声明以下部分:
- 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 连接池,你可以自定义许多额外的设置,如下例所示:
- Properties
- 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 ms 后抛出异常,将最大连接数限制为 50,并在从池中借用连接之前对其进行验证。
支持的连接池
Spring Boot 使用以下算法来选择特定的实现:
-
我们因其性能和并发性而优先选择 HikariCP。如果 HikariCP 可用,我们总是选择它。
-
否则,如果 Tomcat pooling DataSource 可用,我们就使用它。
-
否则,如果 Commons DBCP2 可用,我们就使用它。
-
如果 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 支持以下连接池:
-
HikariCP
-
Tomcat pooling DataSource
-
Commons DBCP2
-
Oracle UCP 与
OracleDataSource -
Spring Framework 的 SimpleDriverDataSource
-
PostgreSQL PGSimpleDataSource
-
C3P0
-
Vibur
连接到 JNDI DataSource
如果你将 Spring Boot 应用程序部署到 Application Server,你可能希望使用 Application Server 内置的功能来配置和管理你的 DataSource,并通过 JNDI 来访问它。
spring.datasource.jndi-name 属性可以用作 spring.datasource.url、spring.datasource.username 和 spring.datasource.password 属性的替代方案,以从特定的 JNDI 位置访问 DataSource。例如,application.properties 中的以下配置段展示了如何访问在 JBoss AS 中定义的 DataSource:
- Properties
- YAML
spring.datasource.jndi-name=java:jboss/datasources/customers
spring:
datasource:
jndi-name: "java:jboss/datasources/customers"
使用 JdbcTemplate
Spring 的 JdbcTemplate 和 NamedParameterJdbcTemplate 类会自动配置,你可以直接将它们注入到自己的 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.* 属性来自定义模板的一些属性,如下例所示:
- Properties
- YAML
spring.jdbc.template.max-rows=500
spring:
jdbc:
template:
max-rows: 500
如果需要调整 SQL 异常的处理,你可以定义自己的 SQLExceptionTranslator bean,使其与自动配置的 JdbcTemplate 关联。
NamedParameterJdbcTemplate 在底层复用同一个 JdbcTemplate 实例。如果定义了多个 JdbcTemplate 且不存在主候选(primary candidate),则 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 Persistence API 是一种标准技术,可让你将对象“映射”到关系型数据库。spring-boot-starter-data-jpa POM 提供了一种快速入门的方式。它提供了以下关键依赖项:
-
Hibernate:最流行的 JPA 实现之一。
-
Spring Data JPA:帮助你实现基于 JPA 的仓库。
-
Spring ORM:Spring Framework 提供的核心 ORM 支持。
我们在这里不会深入探讨 JPA 或 Spring Data 的太多细节。你可以参考 spring.io 提供的 Accessing Data with JPA 指南,并阅读 Spring Data JPA 和 Hibernate 的参考文档。
Entity Classes
传统上,JPA 的 “Entity” 类是在 persistence.xml 文件中指定的。而在 Spring Boot 中,该文件不是必需的,取而代之的是使用 “Entity 扫描”。默认情况下,会扫描 自动配置包。
任何带有 @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 注解来自定义实体扫描位置。参见 “How-to Guides” 中的 将 @Entity 定义与 Spring 配置分离 一节。
Spring Data JPA Repositories
Spring Data JPA 仓库是你可以定义的接口,用于访问数据。JPA 查询会根据你的方法名自动生成。例如,一个 CityRepository 接口可能会声明一个 findAllByState(String state) 方法,用于查找给定州内的所有城市。
对于更复杂的查询,你可以使用 Spring Data 的 Query 注解来标注你的方法。
Spring Data 仓库通常继承自 Repository 或 CrudRepository 接口。如果你使用自动配置,则会在自动配置包中搜索仓库。
你可以使用 @EnableJpaRepositories 自定义用于查找 repository 的位置。
以下示例展示了一个典型的 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 仓库支持三种不同的引导模式:default、deferred 和 lazy。要启用 deferred 或 lazy 引导模式,请分别将 spring.data.jpa.repositories.bootstrap-mode 属性设置为 deferred 或 lazy。当使用 deferred 或 lazy 引导模式时,自动配置的 EntityManagerFactoryBuilder 将使用上下文中的 AsyncTaskExecutor(如果存在)作为引导执行器。如果存在多个,将使用名为 applicationTaskExecutor 的那个。
在使用延迟(deferred)或懒加载(lazy)引导时,请确保将对 JPA 基础设施的任何访问推迟到应用上下文引导阶段之后。你可以使用 SmartInitializingSingleton 来调用需要 JPA 基础设施的初始化逻辑。对于作为 Spring Bean 创建的 JPA 组件(例如转换器),如果存在依赖项,请使用 ObjectProvider 来延迟依赖项的解析。
我们 barely touched the surface of Spring Data JPA。有关完整详情,请参阅 Spring Data JPA 参考文档。
Spring Data Envers Repositories
如果 Spring Data Envers 可用,则 JPA repositories 会自动配置以支持典型的 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 数据库
默认情况下,JPA 数据库仅在你使用嵌入式数据库(H2、HSQL 或 Derby)时才会自动创建。你可以通过使用 spring.jpa.* 属性显式配置 JPA 设置。例如,若要创建并删除表,可以在 application.properties 中添加以下行:
- 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 属性的一个示例:
- Properties
- YAML
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring:
jpa:
properties:
hibernate:
"globally_quoted_identifiers": "true"
上例中的代码行为 Hibernate 实体管理器的 hibernate.globally_quoted_identifiers 属性传递了一个 true 值。
默认情况下,DDL 的执行(或验证)会延迟到 ApplicationContext 启动之后才进行。
Open EntityManager in View
如果你正在运行一个 Web 应用程序,Spring Boot 默认会注册 OpenEntityManagerInViewInterceptor 以应用 “Open EntityManager in View” 模式,从而允许在 Web 视图中进行懒加载。如果你不希望启用此行为,应在 application.properties 中将 spring.jpa.open-in-view 设置为 false。
Spring Data JDBC
Spring Data 包含对 JDBC 的 Repository 支持,并会为 CrudRepository 上的方法自动生成 SQL。对于更高级的查询,提供了 @Query 注解。
当类路径中存在必要的依赖项时,Spring Boot 会自动配置 Spring Data 的 JDBC 仓库。只需在项目中添加一个 spring-boot-starter-data-jdbc 依赖即可。如有必要,您可以通过在应用程序中添加 @EnableJdbcRepositories 注解或 AbstractJdbcConfiguration 的子类来控制 Spring Data JDBC 的配置。
有关 Spring Data JDBC 的完整详细信息,请参阅 参考文档。
使用 H2 的 Web 控制台
-
你正在开发一个基于 Servlet 的 Web 应用程序。
-
com.h2database:h2在 classpath 中。 -
你正在使用 Spring Boot 的开发者工具。
如果你没有使用 Spring Boot 的开发者工具,但仍希望使用 H2 的控制台,可以将 spring.h2.console.enabled 属性配置为 true。
H2 控制台仅用于开发阶段,因此你应确保在生产环境中 spring.h2.console.enabled 未被设置为 true。
更改 H2 Console 的路径
默认情况下,控制台位于 /h2-console。你可以通过使用 spring.h2.console.path 属性来自定义控制台的路径。
在受保护的应用程序中访问 H2 控制台
H2 Console 使用了 frames,并且由于它仅用于开发目的,因此未实现 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 Object Oriented Querying(jOOQ)是 Data Geekery 推出的一款流行产品,它能从你的数据库生成 Java 代码,并通过其流畅的 API 构建类型安全的 SQL 查询。商业版和开源版均可与 Spring Boot 一起使用。
代码生成
为了使用 jOOQ 的类型安全查询,你需要从数据库模式生成 Java 类。你可以按照 jOOQ 用户手册 中的说明进行操作。如果你使用 jooq-codegen-maven 插件,并且也使用了 spring-boot-starter-parent “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 提供的 fluent 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 之前进行更高级的自定义。这种方式优先于自动配置所应用的任何设置。
你也可以创建自己的 Configuration @Bean,以便完全控制 jOOQ 的配置。
使用 R2DBC
Reactive Relational Database Connectivity(R2DBC)项目将响应式编程 API 引入关系型数据库。R2DBC 的 Connection 提供了一种使用非阻塞数据库连接的标准方法。连接通过 ConnectionFactory 提供,类似于 JDBC 中的 DataSource。
ConnectionFactory 的配置由 spring.r2dbc.* 中的外部配置属性控制。例如,你可以在 application.properties 中声明以下内容:
- 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 中指定的信息优先于单独的属性,即 name、username、password 和连接池选项。
“操作指南”部分包含关于如何初始化数据库的章节。
要自定义由 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 仓库是你可以定义的接口,用于访问数据。查询会根据你的方法名自动生成。例如,一个 CityRepository 接口可能会声明一个 findAllByState(String state) 方法,用于查找给定州内的所有城市。
对于更复杂的查询,你可以使用 Spring Data 的 @Query 注解来标注你的方法。
Spring Data 仓库通常继承自 Repository 或 CrudRepository 接口。如果你使用自动配置,自动配置包 将被搜索以查找仓库。
以下示例展示了一个典型的 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 R2DB C 参考文档。