資料訪問
Spring Boot 包含許多用於處理資料來源的啟動器。本節回答與此相關的常見問題。
配置自定義 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:///test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
url: "jdbc:mysql:///test"
username: "dbuser"
password: "dbpass"
pool-size: 30
然而,由於方法的 DataSource
返回型別,這裡存在一個問題。這隱藏了連線池的實際型別,因此不會為你的自定義 DataSource
生成配置屬性元資料,並且 IDE 中也無法獲得自動補全。要解決此問題,請使用構建器的 type(Class)
方法指定要構建的 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()
}
}
不幸的是,這種基本設定不起作用,因為 Hikari 沒有 url
屬性。相反,它有一個 jdbc-url
屬性,這意味著你必須按如下方式重寫配置
-
Properties
-
YAML
app.datasource.jdbc-url=jdbc:mysql:///test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
jdbc-url: "jdbc:mysql:///test"
username: "dbuser"
password: "dbpass"
pool-size: 30
為了解決這個問題,可以利用 DataSourceProperties
,它會幫你處理 url
到 jdbc-url
的轉換。你可以使用 DataSourceProperties
物件的 initializeDataSourceBuilder()
方法從其狀態初始化一個 DataSourceBuilder
。你可以注入 Spring Boot 自動建立的 DataSourceProperties
,但這會使你的配置分散在 spring.datasource.*
和 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
到 jdbc-url
的轉換,因此你可以按如下方式配置它
-
Properties
-
YAML
app.datasource.url=jdbc:mysql:///test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
app:
datasource:
url: "jdbc:mysql:///test"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
請注意,由於自定義配置在程式碼中指定應使用 Hikari,因此 app.datasource.type
將不起作用。
正如支援的連線池中所述,DataSourceBuilder
支援多種不同的連線池。要使用 Hikari 以外的連線池,將其新增到類路徑中,使用 type(Class)
方法指定要使用的連線池類,並更新 @Bean
方法的返回型別以匹配。這還將為你選擇的特定連線池提供配置屬性元資料。
Spring Boot 將 Hikari 特定設定暴露給 spring.datasource.hikari 。由於此示例不支援多種資料來源實現,因此使用了更通用的 configuration 子名稱空間。 |
有關更多詳細資訊,請參見配置 DataSource 和 DataSourceAutoConfiguration
類。
配置兩個 DataSource
要定義額外的 DataSource
,可以使用與上一節類似的方法。一個關鍵區別是必須使用 defaultCandidate=false
宣告 DataSource
的 @Bean
。這可以防止自動配置的 DataSource
回退。
Spring Framework 參考文件更詳細地描述了此特性。 |
為了讓額外的 DataSource
在需要的地方被注入,還要使用 @Qualifier
註解它,如以下示例所示
-
Java
-
Kotlin
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 MyAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
public HikariDataSource secondDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.beans.factory.annotation.Qualifier
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 MyAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
fun secondDataSource(): HikariDataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
}
要使用額外的 DataSource
,請使用相同的 @Qualifier
註解注入點。
自動配置的資料來源和額外的資料來源可以按如下方式配置
-
Properties
-
YAML
spring.datasource.url=jdbc:mysql:///first
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.configuration.maximum-pool-size=30
app.datasource.url=jdbc:mysql:///second
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.max-total=30
spring:
datasource:
url: "jdbc:mysql:///first"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
app:
datasource:
url: "jdbc:mysql:///second"
username: "dbuser"
password: "dbpass"
max-total: 30
透過 spring.datasource.configuration.*
屬性可以對自動配置的 DataSource
進行更高階、實現特定的配置。你也可以將相同的概念應用於額外的 DataSource
,如以下示例所示
-
Java
-
Kotlin
import com.zaxxer.hikari.HikariDataSource;
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;
@Configuration(proxyBeanMethods = false)
public class MyCompleteAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource.configuration")
public HikariDataSource secondDataSource(
@Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
return secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
import com.zaxxer.hikari.HikariDataSource
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
@Configuration(proxyBeanMethods = false)
class MyCompleteAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
fun secondDataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource.configuration")
fun secondDataSource(secondDataSourceProperties: DataSourceProperties): HikariDataSource {
return secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
}
上述示例使用與 Spring Boot 在自動配置中使用的相同邏輯配置了額外的資料來源。請注意,app.datasource.configuration.*
屬性提供基於所選實現的進階設定。
與配置單個自定義 DataSource
一樣,可以使用 DataSourceBuilder
上的 type(Class)
方法自定義一個或兩個 DataSource
bean 的型別。有關支援型別的詳細資訊,請參見支援的連線池。
使用 Spring Data Repositories
Spring Data 可以建立各種風格的 Repository
介面的實現。只要這些 Repository
實現包含在自動配置包中,通常是主應用程式類所在的包(或其子包),並用 @SpringBootApplication
或 @EnableAutoConfiguration
註解,Spring Boot 就會幫你處理所有這些事情。
對於許多應用程式來說,你只需要在類路徑中放置正確的 Spring Data 依賴項即可。針對 JPA 有 spring-boot-starter-data-jpa
,針對 Mongodb 有 spring-boot-starter-data-mongodb
,以及針對其他支援技術的各種啟動器。要開始使用,建立一些 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
是一個特殊情況,因為它會根據執行時條件有不同的預設值。如果使用嵌入式資料庫且沒有 schema 管理器(如 Liquibase 或 Flyway)處理 DataSource
,則預設為 create-drop
。在所有其他情況下,預設為 none
。
要使用的 dialect 由 JPA 供應商檢測。如果你更喜歡自己設定 dialect,請設定 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 屬性(去除字首)傳遞。
你需要確保在 例如,如果你想配置 Hibernate 的 batch size,你必須使用 |
如果你需要對 Hibernate 屬性進行高階定製,請考慮註冊一個 HibernatePropertiesCustomizer bean,該 bean 將在建立 EntityManagerFactory 之前呼叫。這將優先於自動配置應用的任何設定。 |
配置 Hibernate 命名策略
Hibernate 使用兩種不同的命名策略將物件模型中的名稱對映到相應的資料庫名稱。可以透過設定 spring.jpa.hibernate.naming.physical-strategy
和 spring.jpa.hibernate.naming.implicit-strategy
屬性分別配置物理策略和隱式策略實現的完全限定類名。另外,如果應用程式上下文中存在 ImplicitNamingStrategy
或 PhysicalNamingStrategy
bean,Hibernate 將自動配置為使用它們。
預設情況下,Spring Boot 使用 CamelCaseToUnderscoresNamingStrategy
配置物理命名策略。使用此策略,所有點(.)都替換為下劃線(_),駝峰命名法也替換為下劃線。此外,預設情況下,所有表名都生成為小寫。例如,TelephoneNumber
實體對映到 telephone_number
表。如果你的 schema 需要大小寫混合的識別符號,請定義一個自定義的 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()
}
}
有關更多詳細資訊,請參見 HibernateJpaAutoConfiguration
和 JpaBaseConfiguration
。
配置 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
}
}
}
此 customizer 將配置 Hibernate 使用與應用程式相同的 CacheManager
。也可以使用獨立的 CacheManager
例項。詳細資訊請參見Hibernate 使用者指南。
在 Hibernate 元件中使用依賴注入
預設情況下,Spring Boot 註冊一個使用 BeanFactory
的 BeanContainer
實現,以便 converter 和 entity listener 可以使用常規依賴注入。
你可以透過註冊一個 HibernatePropertiesCustomizer
來停用或調整此行為,該 customizer 會移除或更改 hibernate.resource.beans.container
屬性。
使用自定義 EntityManagerFactory
要完全控制 EntityManagerFactory
的配置,你需要新增一個名為 'entityManagerFactory' 的 @Bean
。如果存在該型別的 bean,Spring Boot 的自動配置會關閉其 entity manager。
當你自己為 LocalContainerEntityManagerFactoryBean 建立一個 bean 時,在建立自動配置的 LocalContainerEntityManagerFactoryBean 期間應用的任何自定義都會丟失。務必使用自動配置的 EntityManagerFactoryBuilder 來保留 JPA 和供應商屬性。如果你依賴 spring.jpa.* 屬性來配置命名策略或 DDL 模式等內容,這一點尤其重要。 |
使用多個 EntityManagerFactory
如果您需要針對多個數據源使用 JPA,您可能需要為每個資料來源配置一個 EntityManagerFactory
。Spring ORM 中的 LocalContainerEntityManagerFactoryBean
允許您根據需要配置一個 EntityManagerFactory
。您還可以重用 JpaProperties
來繫結第二個 EntityManagerFactory
的設定。基於配置第二個 DataSource
的示例,第二個 EntityManagerFactory
可以定義如下例所示:
-
Java
-
Kotlin
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 MyAdditionalEntityManagerFactoryConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.jpa")
public JpaProperties secondJpaProperties() {
return new JpaProperties();
}
@Qualifier("second")
@Bean(defaultCandidate = false)
public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(@Qualifier("second") DataSource dataSource,
@Qualifier("second") JpaProperties jpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(jpaProperties);
return builder.dataSource(dataSource).packages(Order.class).persistenceUnit("second").build();
}
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
Function<DataSource, Map<String, ?>> jpaPropertiesFactory = (dataSource) -> createJpaProperties(dataSource,
jpaProperties.getProperties());
return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaPropertiesFactory, null);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
// ... map JPA properties as needed
return new HibernateJpaVendorAdapter();
}
private Map<String, ?> createJpaProperties(DataSource dataSource, Map<String, ?> existingProperties) {
Map<String, ?> jpaProperties = new LinkedHashMap<>(existingProperties);
// ... map JPA properties that require the DataSource (e.g. DDL flags)
return jpaProperties;
}
}
import org.springframework.beans.factory.annotation.Qualifier
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
import javax.sql.DataSource
@Configuration(proxyBeanMethods = false)
class MyAdditionalEntityManagerFactoryConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.jpa")
fun secondJpaProperties(): JpaProperties {
return JpaProperties()
}
@Qualifier("second")
@Bean(defaultCandidate = false)
fun firstEntityManagerFactory(
@Qualifier("second") dataSource: DataSource,
@Qualifier("second") jpaProperties: JpaProperties
): LocalContainerEntityManagerFactoryBean {
val builder = createEntityManagerFactoryBuilder(jpaProperties)
return builder.dataSource(dataSource).packages(Order::class.java).persistenceUnit("second").build()
}
private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
val jpaPropertiesFactory = { dataSource: DataSource ->
createJpaProperties(dataSource, jpaProperties.properties) }
return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaPropertiesFactory, null)
}
private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
// ... map JPA properties as needed
return HibernateJpaVendorAdapter()
}
private fun createJpaProperties(dataSource: DataSource, existingProperties: Map<String, *>): Map<String, *> {
val jpaProperties: Map<String, *> = LinkedHashMap(existingProperties)
// ... map JPA properties that require the DataSource (e.g. DDL flags)
return jpaProperties
}
}
上面的示例使用透過 @Qualifier("second")
限定的 DataSource
bean 建立了一個 EntityManagerFactory
。它掃描位於與 Order
同一包中的實體。可以使用 app.jpa
名稱空間對映額外的 JPA 屬性。使用 @Bean(defaultCandidate=false)
允許定義 secondJpaProperties
和 secondEntityManagerFactory
bean,而不會干擾相同型別的自動配置 bean。
Spring Framework 參考文件更詳細地描述了此特性。 |
對於需要 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 = "entityManagerFactory")
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
,您需要定義自己的 @Bean
,其型別為 LocalEntityManagerFactoryBean
(ID 為 'entityManagerFactory'),並在其中設定永續性單元名稱。
有關預設設定,請參閱 JpaBaseConfiguration
。
使用 Spring Data JPA 和 Mongo 倉庫
Spring Data JPA 和 Spring Data Mongo 都可以為您自動建立 Repository
實現。如果它們同時存在於類路徑上,您可能需要進行一些額外的配置來告知 Spring Boot 建立哪些倉庫。最明確的方法是使用標準的 Spring Data 註解 @EnableJpaRepositories
和 @EnableMongoRepositories
,並提供您的 Repository
介面的位置。
還有一些標誌(spring.data.*.repositories.enabled
和 spring.data.*.repositories.type
)可用於在外部配置中開啟或關閉自動配置的倉庫。這樣做很有用,例如,如果您想關閉 Mongo 倉庫但仍使用自動配置的 MongoTemplate
。
對於其他自動配置的 Spring Data 倉庫型別(Elasticsearch、Redis 等),也存在同樣的障礙和相同的功能。要使用它們,請相應地更改註解和標誌的名稱。
自定義 Spring Data 的 Web 支援
Spring Data 提供了 web 支援,簡化了在 web 應用程式中使用 Spring Data 倉庫的過程。Spring Boot 在 spring.data.web
名稱空間中提供了用於自定義其配置的屬性。請注意,如果您使用 Spring Data REST,則必須改用 spring.data.rest
名稱空間中的屬性。
將 Spring Data 倉庫暴露為 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")
使用兩個資料來源配置 jOOQ
如果您需要使用 jOOQ 針對多個數據源,您應該為每個資料來源建立自己的 DSLContext
。有關更多詳細資訊,請參閱 JooqAutoConfiguration
。
特別是,ExceptionTranslatorExecuteListener 和 SpringTransactionProvider 可以重用,以提供與自動配置處理單個 DataSource 時類似的功能。 |