資料存取

Spring Boot 包含許多用於資料來源的 Starter。本節回答與此相關的問題。

組態自訂 DataSource

若要組態您自己的 DataSource,請在您的組態中定義該類型的 @Bean。Spring Boot 會在任何需要 DataSource 的地方重複使用您的 DataSource,包括資料庫初始化。如果您需要外部化某些設定,您可以將您的 DataSource 綁定到環境 (請參閱 第三方組態)。

以下範例示範如何在 Bean 中定義資料來源

  • Java

  • Kotlin

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

	@Bean
	@ConfigurationProperties(prefix = "app.datasource")
	public SomeDataSource dataSource() {
		return new SomeDataSource();
	}

}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

	@Bean
	@ConfigurationProperties(prefix = "app.datasource")
	fun dataSource(): SomeDataSource {
		return SomeDataSource()
	}

}

以下範例示範如何透過設定屬性來定義資料來源

  • Properties

  • YAML

app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30
app:
  datasource:
    url: "jdbc:h2:mem:mydb"
    username: "sa"
    pool-size: 30

假設 SomeDataSource 針對 URL、使用者名稱和連線池大小具有常規 JavaBean 屬性,這些設定會在 DataSource 可供其他元件使用之前自動綁定。

Spring Boot 也提供了一個稱為 DataSourceBuilder 的實用工具建構器類別,可用於建立標準資料來源之一 (如果它在類別路徑上)。建構器可以根據類別路徑上可用的內容偵測要使用哪個資料來源。它也會根據 JDBC URL 自動偵測驅動程式。

以下範例示範如何使用 DataSourceBuilder 建立資料來源

  • Java

  • Kotlin

import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

	@Bean
	@ConfigurationProperties("app.datasource")
	public DataSource dataSource() {
		return DataSourceBuilder.create().build();
	}

}
import javax.sql.DataSource

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

	@Bean
	@ConfigurationProperties("app.datasource")
	fun dataSource(): DataSource {
		return DataSourceBuilder.create().build()
	}

}

若要使用該 DataSource 執行應用程式,您只需要連線資訊。也可以提供特定於連線池的設定。請查看執行階段將使用的實作以取得更多詳細資訊。

以下範例示範如何透過設定屬性來定義 JDBC 資料來源

  • Properties

  • YAML

app.datasource.url=jdbc:mysql://127.0.0.1/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
  datasource:
    url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

但是,這裡有一個陷阱。由於未公開連線池的實際類型,因此不會在自訂 DataSource 的元數據中產生任何鍵,並且您的 IDE 中沒有完成功能 (因為 DataSource 介面未公開任何屬性)。此外,如果您碰巧在類別路徑上有 Hikari,則此基本設定無法運作,因為 Hikari 沒有 url 屬性 (但確實有 jdbcUrl 屬性)。在這種情況下,您必須將您的組態重寫如下

  • Properties

  • YAML

app.datasource.jdbc-url=jdbc:mysql://127.0.0.1/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
  datasource:
    jdbc-url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

您可以透過強制連線池使用並傳回專用實作而非 DataSource 來修正此問題。您無法在執行階段變更實作,但選項清單將會是明確的。

以下範例示範如何使用 DataSourceBuilder 建立 HikariDataSource

  • Java

  • Kotlin

import com.zaxxer.hikari.HikariDataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

	@Bean
	@ConfigurationProperties("app.datasource")
	public HikariDataSource dataSource() {
		return DataSourceBuilder.create().type(HikariDataSource.class).build();
	}

}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

	@Bean
	@ConfigurationProperties("app.datasource")
	fun dataSource(): HikariDataSource {
		return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
	}

}

您甚至可以更進一步,利用 DataSourceProperties 為您提供的功能 — 也就是說,如果未提供 URL,則提供具有合理使用者名稱和密碼的預設嵌入式資料庫。您可以輕鬆地從任何 DataSourceProperties 物件的狀態初始化 DataSourceBuilder,因此您也可以注入 Spring Boot 自動建立的 DataSource。但是,這會將您的組態分成兩個命名空間:spring.datasource 上的 urlusernamepasswordtypedriver,以及自訂命名空間 (app.datasource) 上的其餘部分。為了避免這種情況,您可以重新定義自訂 DataSourceProperties 在您的自訂命名空間上,如下列範例所示

  • Java

  • Kotlin

import com.zaxxer.hikari.HikariDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource")
	public DataSourceProperties dataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@ConfigurationProperties("app.datasource.configuration")
	public HikariDataSource dataSource(DataSourceProperties properties) {
		return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
	}

}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource")
	fun dataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@ConfigurationProperties("app.datasource.configuration")
	fun dataSource(properties: DataSourceProperties): HikariDataSource {
		return properties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
	}

}

此設定使您與 Spring Boot 預設為您執行的操作同步,除了選擇了專用連線池 (在程式碼中) 並且其設定公開在 app.datasource.configuration 子命名空間中。由於 DataSourceProperties 正在為您處理 url/jdbcUrl 轉換,因此您可以將其組態如下

  • Properties

  • YAML

app.datasource.url=jdbc:mysql://127.0.0.1/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
app:
  datasource:
    url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
    configuration:
      maximum-pool-size: 30
Spring Boot 會將 Hikari 特有的設定公開到 spring.datasource.hikari。此範例使用更通用的 configuration 子命名空間,因為該範例不支援多個資料來源實作。
由於您的自訂組態選擇使用 Hikari,因此 app.datasource.type 無效。實際上,建構器會使用您可能在那裡設定的任何值初始化,然後被對 .type() 的呼叫覆寫。

請參閱 “Spring Boot 功能” 章節中的 組態 DataSourceDataSourceAutoConfiguration 類別以取得更多詳細資訊。

組態兩個 DataSources

如果您需要組態多個資料來源,您可以應用先前章節中描述的相同技巧。但是,您必須將其中一個 DataSource 實例標記為 @Primary,因為後續的各種自動組態期望能夠依類型取得一個。

如果您建立自己的 DataSource,則自動組態會退回。在以下範例中,我們提供與自動組態在主要資料來源上提供的完全相同的功能集

  • Java

  • Kotlin

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	public DataSourceProperties firstDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	public BasicDataSource secondDataSource() {
		return DataSourceBuilder.create().type(BasicDataSource.class).build();
	}

}
import com.zaxxer.hikari.HikariDataSource
import org.apache.commons.dbcp2.BasicDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	fun firstDataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	fun secondDataSource(): BasicDataSource {
		return DataSourceBuilder.create().type(BasicDataSource::class.java).build()
	}

}
firstDataSourceProperties 必須標記為 @Primary,以便資料庫初始化程式功能使用您的副本 (如果您使用初始化程式)。

這兩個資料來源也綁定用於進階自訂。例如,您可以將它們組態如下

  • Properties

  • YAML

app.datasource.first.url=jdbc:mysql://127.0.0.1/first
app.datasource.first.username=dbuser
app.datasource.first.password=dbpass
app.datasource.first.configuration.maximum-pool-size=30
app.datasource.second.url=jdbc:mysql://127.0.0.1/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30
app:
  datasource:
    first:
      url: "jdbc:mysql://127.0.0.1/first"
      username: "dbuser"
      password: "dbpass"
      configuration:
        maximum-pool-size: 30

    second:
      url: "jdbc:mysql://127.0.0.1/second"
      username: "dbuser"
      password: "dbpass"
      max-total: 30

您可以將相同的概念應用於次要 DataSource,如下列範例所示

  • Java

  • Kotlin

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyCompleteDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	public DataSourceProperties firstDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	public DataSourceProperties secondDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@ConfigurationProperties("app.datasource.second.configuration")
	public BasicDataSource secondDataSource(
			@Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
		return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build();
	}

}
import com.zaxxer.hikari.HikariDataSource
import org.apache.commons.dbcp2.BasicDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyCompleteDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	fun firstDataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	fun secondDataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@ConfigurationProperties("app.datasource.second.configuration")
	fun secondDataSource(secondDataSourceProperties: DataSourceProperties): BasicDataSource {
		return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource::class.java).build()
	}

}

先前的範例在自訂命名空間上組態了兩個資料來源,其邏輯與 Spring Boot 在自動組態中使用的邏輯相同。請注意,每個 configuration 子命名空間都根據所選的實作提供進階設定。

使用 Spring Data Repositories

Spring Data 可以建立各種風格的 Repository 介面的實作。Spring Boot 為您處理所有這些,只要這些 Repository 實作包含在 自動組態套件 之一中,通常是使用 @SpringBootApplication@EnableAutoConfiguration 註解的主要應用程式類別的套件 (或子套件)。

對於許多應用程式,您只需要將正確的 Spring Data 依賴放在您的類別路徑上。JPA 有 spring-boot-starter-data-jpa,Mongodb 有 spring-boot-starter-data-mongodb,以及其他各種 Starter 用於支援的技術。若要開始使用,請建立一些 Repository 介面來處理您的 @Entity 物件。

Spring Boot 透過掃描 自動組態套件 來判斷您的 Repository 實作的位置。為了獲得更多控制權,請使用 Spring Data 中的 @Enable…Repositories 註解。

有關 Spring Data 的更多資訊,請參閱 Spring Data 專案頁面

將 @Entity 定義與 Spring 組態分開

Spring Boot 透過掃描 自動組態套件 來判斷您的 @Entity 定義的位置。為了獲得更多控制權,請使用 @EntityScan 註解,如下列範例所示

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {

	// ...

}
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = [City::class])
class MyApplication {

	// ...

}

篩選掃描的 @Entity 定義

可以使用 ManagedClassNameFilter Bean 篩選 @Entity 定義。當只需要考慮可用實體的子集時,這在測試中可能很有用。在以下範例中,僅包含來自 com.example.app.customer 套件的實體

  • Java

  • Kotlin

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.persistenceunit.ManagedClassNameFilter;

@Configuration(proxyBeanMethods = false)
public class MyEntityScanConfiguration {

	@Bean
	public ManagedClassNameFilter entityScanFilter() {
		return (className) -> className.startsWith("com.example.app.customer");
	}

}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.orm.jpa.persistenceunit.ManagedClassNameFilter

@Configuration(proxyBeanMethods = false)
class MyEntityScanConfiguration {

	@Bean
	fun entityScanFilter() : ManagedClassNameFilter {
		return ManagedClassNameFilter { className ->
			className.startsWith("com.example.app.customer")
		}
	}
}

組態 JPA 屬性

Spring Data JPA 已經提供了一些與供應商無關的組態選項 (例如用於 SQL 記錄的選項),而 Spring Boot 公開了這些選項以及 Hibernate 的更多選項作為外部組態屬性。其中一些會根據上下文自動偵測,因此您應該不必設定它們。

spring.jpa.hibernate.ddl-auto 是一種特殊情況,因為根據執行階段條件,它具有不同的預設值。如果使用嵌入式資料庫且沒有架構管理器 (例如 Liquibase 或 Flyway) 處理 DataSource,則預設為 create-drop。在所有其他情況下,它預設為 none

要使用的方言由 JPA 提供者偵測。如果您偏好自行設定方言,請設定 spring.jpa.database-platform 屬性。

以下範例顯示了要設定的最常見選項

  • Properties

  • YAML

spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=true
spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: "com.example.MyPhysicalNamingStrategy"
    show-sql: true

此外,當建立本機 EntityManagerFactory 時,spring.jpa.properties.* 中的所有屬性都會作為常規 JPA 屬性傳遞 (並剝離前綴)。

您需要確保在 spring.jpa.properties.* 下定義的名稱與您的 JPA 提供者預期的名稱完全匹配。Spring Boot 不會嘗試對這些條目進行任何形式的寬鬆綁定。

例如,如果您要組態 Hibernate 的批次大小,則必須使用 spring.jpa.properties.hibernate.jdbc.batch_size。如果您使用其他形式,例如 batchSizebatch-size,Hibernate 將不會套用設定。

如果您需要對 Hibernate 屬性套用進階自訂,請考慮註冊一個 HibernatePropertiesCustomizer Bean,該 Bean 將在建立 EntityManagerFactory 之前被調用。這優先於自動組態套用的任何內容。

組態 Hibernate 命名策略

Hibernate 使用 兩種不同的命名策略 來將名稱從物件模型對應到對應的資料庫名稱。可以透過設定 spring.jpa.hibernate.naming.physical-strategyspring.jpa.hibernate.naming.implicit-strategy 屬性,組態物理策略和隱含策略實作的完整類別名稱。或者,如果 ImplicitNamingStrategyPhysicalNamingStrategy Bean 在應用程式上下文中可用,則會自動組態 Hibernate 以使用它們。

依預設,Spring Boot 使用 CamelCaseToUnderscoresNamingStrategy 組態物理命名策略。使用此策略,所有點都會替換為底線,並且駝峰式大小寫也會替換為底線。此外,依預設,所有表格名稱都以小寫產生。例如,TelephoneNumber 實體會對應到 telephone_number 表格。如果您的架構需要混合大小寫識別碼,請定義自訂 CamelCaseToUnderscoresNamingStrategy Bean,如下列範例所示

  • Java

  • Kotlin

import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {

	@Bean
	public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
		return new CamelCaseToUnderscoresNamingStrategy() {

			@Override
			protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
				return false;
			}

		};
	}

}
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

	@Bean
	fun caseSensitivePhysicalNamingStrategy(): CamelCaseToUnderscoresNamingStrategy {
		return object : CamelCaseToUnderscoresNamingStrategy() {
			override fun isCaseInsensitive(jdbcEnvironment: JdbcEnvironment): Boolean {
				return false
			}
		}
	}

}

如果您偏好使用 Hibernate 的預設值,請設定以下屬性

  • Properties

  • YAML

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

或者,您可以組態以下 Bean

  • Java

  • Kotlin

import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

	@Bean
	PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
		return new PhysicalNamingStrategyStandardImpl();
	}

}
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
internal class MyHibernateConfiguration {

	@Bean
	fun caseSensitivePhysicalNamingStrategy(): PhysicalNamingStrategyStandardImpl {
		return PhysicalNamingStrategyStandardImpl()
	}

}

請參閱 HibernateJpaAutoConfigurationJpaBaseConfiguration 以取得更多詳細資訊。

組態 Hibernate 第二層快取

Hibernate 第二層快取 可以針對各種快取提供者進行組態。最好提供上下文中可用的快取提供者,而不是組態 Hibernate 再次查閱快取提供者。

若要使用 JCache 執行此操作,請先確保類別路徑上提供 org.hibernate.orm:hibernate-jcache。然後,新增一個 HibernatePropertiesCustomizer Bean,如下列範例所示

  • Java

  • Kotlin

import org.hibernate.cache.jcache.ConfigSettings;

import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {

	@Bean
	public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
		return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
	}

}
import org.hibernate.cache.jcache.ConfigSettings
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer
import org.springframework.cache.jcache.JCacheCacheManager
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyHibernateSecondLevelCacheConfiguration {

	@Bean
	fun hibernateSecondLevelCacheCustomizer(cacheManager: JCacheCacheManager): HibernatePropertiesCustomizer {
		return HibernatePropertiesCustomizer { properties ->
			properties[ConfigSettings.CACHE_MANAGER] = cacheManager.cacheManager
		}
	}

}

此自訂器將組態 Hibernate 以使用與應用程式使用的 CacheManager 相同的 CacheManager。也可以使用個別的 CacheManager 實例。有關詳細資訊,請參閱 Hibernate 使用者指南

在 Hibernate 元件中使用依賴注入

依預設,Spring Boot 會註冊一個使用 BeanFactoryBeanContainer 實作,以便轉換器和實體監聽器可以使用常規依賴注入。

您可以透過註冊一個 HibernatePropertiesCustomizer 來停用或調整此行為,該自訂器會移除或變更 hibernate.resource.beans.container 屬性。

使用自訂 EntityManagerFactory

若要完全控制 EntityManagerFactory 的組態,您需要新增一個名為 ‘entityManagerFactory’ 的 @Bean。在存在該類型的 Bean 時,Spring Boot 自動組態會關閉其實體管理器。

使用多個 EntityManagerFactories

如果您需要針對多個資料來源使用 JPA,您可能需要每個資料來源一個 EntityManagerFactory。來自 Spring ORM 的 LocalContainerEntityManagerFactoryBean 可讓您針對您的需求組態 EntityManagerFactory。您也可以重複使用 JpaProperties 來綁定每個 EntityManagerFactory 的設定,如下列範例所示

  • Java

  • Kotlin

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

@Configuration(proxyBeanMethods = false)
public class MyEntityManagerFactoryConfiguration {

	@Bean
	@ConfigurationProperties("app.jpa.first")
	public JpaProperties firstJpaProperties() {
		return new JpaProperties();
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource,
			JpaProperties firstJpaProperties) {
		EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
		return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build();
	}

	private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
		JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
		return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
	}

	private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
		// ... map JPA properties as needed
		return new HibernateJpaVendorAdapter();
	}

}
import javax.sql.DataSource

import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.orm.jpa.JpaVendorAdapter
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter

@Configuration(proxyBeanMethods = false)
class MyEntityManagerFactoryConfiguration {

	@Bean
	@ConfigurationProperties("app.jpa.first")
	fun firstJpaProperties(): JpaProperties {
		return JpaProperties()
	}

	@Bean
	fun firstEntityManagerFactory(
		firstDataSource: DataSource?,
		firstJpaProperties: JpaProperties
	): LocalContainerEntityManagerFactoryBean {
		val builder = createEntityManagerFactoryBuilder(firstJpaProperties)
		return builder.dataSource(firstDataSource).packages(Order::class.java).persistenceUnit("firstDs").build()
	}

	private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
		val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
		return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.properties, null)
	}

	private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
		// ... map JPA properties as needed
		return HibernateJpaVendorAdapter()
	}

}

上面的範例使用名為 firstDataSourceDataSource Bean 建立 EntityManagerFactory。它會掃描與 Order 位於同一套件中的實體。可以使用 app.first.jpa 命名空間對應其他 JPA 屬性。

當您自己為 LocalContainerEntityManagerFactoryBean 建立 Bean 時,在自動組態的 LocalContainerEntityManagerFactoryBean 建立期間套用的任何自訂都會遺失。例如,對於 Hibernate,spring.jpa.hibernate 前綴下的任何屬性都不會自動套用至您的 LocalContainerEntityManagerFactoryBean。如果您依賴這些屬性來組態命名策略或 DDL 模式之類的事項,則在建立 LocalContainerEntityManagerFactoryBean Bean 時,您需要明確組態。

對於您需要 JPA 存取的任何其他資料來源,您應該提供類似的組態。為了完整起見,您還需要為每個 EntityManagerFactory 組態一個 JpaTransactionManager。或者,您或許可以使用跨越兩者的 JTA 交易管理器。

如果您使用 Spring Data,則需要相應地組態 @EnableJpaRepositories,如下列範例所示

  • Java

  • Kotlin

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
public class OrderConfiguration {

}
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Order::class], entityManagerFactoryRef = "firstEntityManagerFactory")
class OrderConfiguration
  • Java

  • Kotlin

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {

}
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Customer::class], entityManagerFactoryRef = "secondEntityManagerFactory")
class CustomerConfiguration

使用傳統 persistence.xml 檔案

Spring Boot 預設不會搜尋或使用 META-INF/persistence.xml。如果您偏好使用傳統的 persistence.xml,則需要定義您自己的 LocalEntityManagerFactoryBean 類型的 @Bean (ID 為 ‘entityManagerFactory’),並在那裡設定持久性單元名稱。

請參閱 JpaBaseConfiguration 以取得預設設定。

使用 Spring Data JPA 和 Mongo Repositories

Spring Data JPA 和 Spring Data Mongo 都可以自動為您建立 Repository 實作。如果它們都存在於類別路徑上,您可能必須執行一些額外的組態,以告知 Spring Boot 要建立哪些 Repository。最明確的方法是使用標準 Spring Data @EnableJpaRepositories@EnableMongoRepositories 註解,並提供您的 Repository 介面的位置。

還有一些旗標 (spring.data.*.repositories.enabledspring.data.*.repositories.type) 可用於在外部組態中開啟和關閉自動組態的 Repository。例如,如果您想要關閉 Mongo Repository 並仍然使用自動組態的 MongoTemplate,則這樣做很有用。

其他自動組態的 Spring Data Repository 類型 (Elasticsearch、Redis 和其他類型) 也存在相同的障礙和相同的功能。若要使用它們,請相應地變更註解和旗標的名稱。

自訂 Spring Data 的 Web 支援

Spring Data 提供 Web 支援,可簡化在 Web 應用程式中使用 Spring Data Repository。Spring Boot 在 spring.data.web 命名空間中提供屬性,用於自訂其組態。請注意,如果您使用的是 Spring Data REST,則必須改用 spring.data.rest 命名空間中的屬性。

將 Spring Data Repositories 公開為 REST 端點

Spring Data REST 可以為您將 Repository 實作公開為 REST 端點,前提是已為應用程式啟用 Spring MVC。

Spring Boot 公開了一組有用的屬性 (來自 spring.data.rest 命名空間),這些屬性自訂了 RepositoryRestConfiguration。如果您需要提供其他自訂,則應使用 RepositoryRestConfigurer Bean。

如果您未在自訂 RepositoryRestConfigurer 上指定任何順序,它會在 Spring Boot 在內部使用的順序之後執行。如果您需要指定順序,請確保它高於 0。

組態 JPA 使用的元件

如果您想要組態 JPA 使用的元件,則需要確保在 JPA 之前初始化該元件。當元件自動組態時,Spring Boot 會為您處理此問題。例如,當 Flyway 自動組態時,Hibernate 會組態為依賴 Flyway,以便 Flyway 有機會在 Hibernate 嘗試使用資料庫之前初始化資料庫。

如果您自己組態元件,則可以使用 EntityManagerFactoryDependsOnPostProcessor 子類別作為設定必要依賴關係的便捷方式。例如,如果您將 Hibernate Search 與 Elasticsearch 用作其索引管理器,則必須將任何 EntityManagerFactory Bean 組態為依賴 elasticsearchClient Bean,如下列範例所示

  • Java

  • Kotlin

import jakarta.persistence.EntityManagerFactory;

import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.stereotype.Component;

/**
 * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
 * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
 */
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
		extends EntityManagerFactoryDependsOnPostProcessor {

	public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
		super("elasticsearchClient");
	}

}
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor
import org.springframework.stereotype.Component

@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
	EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")

使用兩個 DataSources 組態 jOOQ

如果您需要將 jOOQ 與多個資料來源搭配使用,則應為每個資料來源建立自己的 DSLContext。請參閱 JooqAutoConfiguration 以取得更多詳細資訊。

特別是,JooqExceptionTranslatorSpringTransactionProvider 可以重複使用,以提供與自動組態使用單一 DataSource 提供的功能類似的功能。