SQL 資料庫

Spring Framework 提供了廣泛的支援來處理 SQL 資料庫,從使用 JdbcClientJdbcTemplate 直接存取 JDBC,到完整的「物件關聯映射」技術,例如 Hibernate。Spring Data 提供了額外的功能層級:直接從介面建立 Repository 實作,並使用慣例從您的方法名稱產生查詢。

設定 DataSource

Java 的 javax.sql.DataSource 介面提供了一種使用資料庫連線的標準方法。 傳統上,DataSource 使用 URL 以及一些憑證來建立資料庫連線。

請參閱「操作指南」中的 設定自訂 DataSource 章節,以取得更進階的範例,通常是為了完全控制 DataSource 的組態。

嵌入式資料庫支援

使用記憶體內嵌入式資料庫來開發應用程式通常很方便。 顯然,記憶體內資料庫不提供持久性儲存。 您需要在應用程式啟動時填入資料庫,並準備在應用程式結束時丟棄資料。

「操作指南」章節包含 關於如何初始化資料庫的章節

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 手動組態其他連線池。 如果您定義了自己的 DataSource bean,則不會發生自動組態。 DataSourceBuilder 支援以下連線池

  • HikariCP

  • Tomcat 連線池 DataSource

  • Commons DBCP2

  • Oracle UCP & OracleDataSource

  • Spring Framework 的 SimpleDriverDataSource

  • H2 JdbcDataSource

  • PostgreSQL PGSimpleDataSource

  • C3P0

連線到 JNDI DataSource

如果您將 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 Persistence API 是一種標準技術,可讓您將物件「映射」到關聯式資料庫。 spring-boot-starter-data-jpa POM 提供了一種快速入門的方法。 它提供了以下關鍵依賴

  • Hibernate:最受歡迎的 JPA 實作之一。

  • Spring Data JPA:協助您實作基於 JPA 的儲存庫。

  • 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 儲存庫

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 資料庫

依預設,JPA 資料庫僅在您使用嵌入式資料庫 (H2、HSQL 或 Derby) 時才會自動建立。 您可以使用 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"

前述範例中的行將值 true 傳遞給 hibernate.globally_quoted_identifiers 屬性給 Hibernate 實體管理器。

依預設,DDL 執行 (或驗證) 會延遲到 ApplicationContext 啟動之後。

在檢視中開啟 EntityManager

如果您正在執行 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 Console

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 的 Connection Factory 探索中取得驅動程式。
至少應提供 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 參考文件