SQL 数据库
Spring 框架 提供了对 SQL 数据库的广泛支持,从使用 JdbcClient 或 JdbcTemplate 的直接 JDBC 访问,到完整的“对象关系映射”技术(如 Hibernate)。Spring Data 提供了额外的功能:直接从接口创建 Repository 实现,并使用约定从方法名称生成查询。
配置数据源
Java 的 DataSource 接口提供了一种标准的数据库连接处理方法。传统上,DataSource 使用 URL
和一些凭证来建立数据库连接。
有关更高级的示例,请参阅“操作指南”中的配置自定义数据源部分,通常是为了完全控制数据源的配置。
嵌入式数据库支持
使用内存嵌入式数据库开发应用程序通常非常方便。显然,内存数据库不提供持久化存储。你需要在应用程序启动时填充数据库,并在应用程序结束时准备好丢弃数据。
“操作指南”部分包含了一个关于如何初始化数据库的章节。
如果您在测试中使用此功能,您可能会注意到,无论您使用了多少个应用程序上下文,整个测试套件都会重复使用同一个数据库。如果您希望确保每个上下文都有一个独立的嵌入式数据库,您应该将 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 来自动配置。
数据源配置
数据源配置由 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 毫秒后抛出异常,将最大连接数限制为 50,并在从连接池借用连接之前验证连接。
支持的连接池
Spring Boot 使用以下算法来选择特定的实现:
-
我们更喜欢使用 HikariCP,因为它的性能和并发性优异。如果 HikariCP 可用,我们总是选择它。
-
否则,如果 Tomcat 连接池 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 连接池 DataSource
-
Commons DBCP2
-
Oracle UCP 和
OracleDataSource
-
Spring Framework 的 SimpleDriverDataSource
-
H2 的 JdbcDataSource
-
PostgreSQL 的 PGSimpleDataSource
-
C3P0
连接到 JNDI 数据源
如果你将 Spring Boot 应用部署到应用服务器上,你可能希望通过使用应用服务器的内置功能来配置和管理 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
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 Persistence API 是一种标准技术,允许你将对象“映射”到关系型数据库。spring-boot-starter-data-jpa
POM 提供了一种快速入门的方式。它包含以下关键依赖项:
-
Hibernate:最受欢迎的 JPA 实现之一。
-
Spring Data JPA:帮助你实现基于 JPA 的仓库。
-
Spring ORM:Spring 框架的核心 ORM 支持。
我们在这里不会深入探讨 JPA 或 Spring Data 的太多细节。你可以参考 spring.io 上的 使用 JPA 访问数据 指南,并阅读 Spring Data JPA 和 Hibernate 的参考文档。
实体类
传统上,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 仓库通常继承自 Repository 或 CrudRepository 接口。如果你使用自动配置,系统会在 自动配置包 中搜索仓库。
你可以使用 @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
属性设置为 deferred
或 lazy
。当使用延迟或惰性引导模式时,自动配置的 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 数据库
默认情况下,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.globally_quoted_identifiers
属性的值 true
传递给了 Hibernate 实体管理器。
默认情况下,DDL 的执行(或验证)会延迟到 ApplicationContext 启动之后。
视图中的打开实体管理器
如果你正在运行一个 Web 应用程序,Spring Boot 默认会注册 OpenEntityManagerInViewInterceptor 来应用“在视图中打开 EntityManager”模式,以支持在 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 控制台
-
你正在开发一个基于 servlet 的 Web 应用程序。
-
com.h2database: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 通过 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 将在创建 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 仓库
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 R2DBC 参考文档。