Data Access
Spring Boot 包含了多个用于处理数据源的 starters。本节将回答与此相关的常见问题。
配置自定义 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("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("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 的工具构建器类,可用于创建标准数据源之一(前提是该数据源位于 classpath 中)。该构建器可以根据 classpath 中可用的内容自动检测应使用哪一个数据源。它还能根据 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://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
url: "jdbc:mysql://localhost/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://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
jdbc-url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
为了解决这个问题,可以使用 DataSourceProperties,它会自动为你处理 url 到 jdbc-url 的转换。你可以通过其 initializeDataSourceBuilder() 方法,从任意 DataSourceProperties 对象的状态初始化一个 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://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
请注意,由于自定义配置在代码中指定了应使用 Hikari,因此 app.datasource.type 将不会生效。
如 支持的连接池 中所述,DataSourceBuilder 支持多种不同的连接池。若要使用 Hikari 以外的连接池,请将其添加到 classpath 中,使用 type(Class) 方法指定要使用的连接池类,并将 @Bean 方法的返回类型更新为与之匹配。这样还会为你所选的特定连接池提供配置属性元数据。
Spring Boot 会将 Hikari 特定的设置暴露在 spring.datasource.hikari 下。本示例使用了更通用的 configuration 子命名空间,因为该示例不支持多种数据源实现。
详见 配置 DataSource 和 DataSourceAutoConfiguration 类以获取更多详情。
配置两个 DataSource
要定义一个额外的 DataSource,可以采用与前一节类似的方法。关键区别在于,该 DataSource 的 @Bean 必须声明为 defaultCandidate=false。这样可以防止自动配置的 DataSource 被取消(backing off)。
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://localhost/first
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.configuration.maximum-pool-size=30
app.datasource.url=jdbc:mysql://localhost/second
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.max-total=30
spring:
datasource:
url: "jdbc:mysql://localhost/first"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
app:
datasource:
url: "jdbc:mysql://localhost/second"
username: "dbuser"
password: "dbpass"
max-total: 30
更高级的、与具体实现相关的自动配置 DataSource 可通过 spring.datasource.configuration.* 属性进行设置。你也可以将相同的概念应用于额外的 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("second") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.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(@Qualifier("second") dataSourceProperties: DataSourceProperties): HikariDataSource {
return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
}
前面的示例使用了与 Spring Boot 在自动配置中所用的相同逻辑来配置额外的数据源。请注意,app.datasource.configuration.* 属性基于所选的实现提供了高级设置。
与配置单个自定义 DataSource 类似,可以使用 DataSourceBuilder 上的 type(Class) 方法来自定义一个或两个 DataSource Bean 的类型。有关支持的类型详情,请参阅 Supported Connection Pools。
使用 Spring Data Repositories
Spring Data 可以为各种风格的 Repository 接口创建实现。只要这些 Repository 实现在某个 自动配置包 中,Spring Boot 就会为你处理所有这些工作。通常,这些包是你主应用类所在的包(或其子包),而该主应用类使用了 @SpringBootApplication 或 @EnableAutoConfiguration 注解。
对于许多应用程序来说,你只需要在 classpath 中添加正确的 Spring Data 依赖项即可。JPA 有 spring-boot-starter-data-jpa,MongoDB 有 spring-boot-starter-data-mongodb,以及其他各种支持的技术对应的 starter。要开始使用,只需创建一些 repository 接口来处理你的 @Entity 对象。
Spring Boot 通过扫描 auto-configuration packages 来确定你的 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。
所使用的方言由 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 不会对这些条目进行任何形式的宽松绑定(relaxed binding)。
例如,如果你想配置 Hibernate 的批处理大小,必须使用 spring.jpa.properties.hibernate.jdbc.batch_size。如果你使用其他形式,例如 batchSize 或 batch-size,Hibernate 将不会应用该设置。
如果你需要对 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 表。如果你的数据库模式要求使用混合大小写的标识符,请定义一个自定义的 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()
}
}
配置 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 实例。详情请参见 Hibernate 用户指南。
在 Hibernate 组件中使用依赖注入
默认情况下,Spring Boot 会注册一个 BeanContainer 实现,该实现使用 BeanFactory,以便转换器(converters)和实体监听器(entity listeners)可以使用常规的依赖注入。
你可以通过注册一个 HibernatePropertiesCustomizer 来禁用或调整此行为,该 Customizer 会移除或修改 hibernate.resource.beans.container 属性。
使用自定义的 EntityManagerFactory
要完全控制 EntityManagerFactory 的配置,你需要添加一个名为 ‘entityManagerFactory’ 的 @Bean。Spring Boot 在检测到该类型的 Bean 时,会自动关闭其自动配置的实体管理器。
当你自己创建一个 LocalContainerEntityManagerFactoryBean 的 bean 时,在自动配置的 LocalContainerEntityManagerFactoryBean 创建过程中所应用的任何自定义配置都会丢失。请务必使用自动配置的 EntityManagerFactoryBuilder 来保留 JPA 和供应商相关的属性。如果你依赖 spring.jpa.* 属性来配置诸如命名策略(naming strategy)或 DDL 模式(DDL mode)等内容,这一点尤为重要。
使用多个 EntityManagerFactory
如果你需要针对多个数据源使用 JPA,通常每个数据源都需要一个 EntityManagerFactory。Spring ORM 提供的 LocalContainerEntityManagerFactoryBean 允许你根据需求配置一个 EntityManagerFactory。你还可以复用 JpaProperties 来为第二个 EntityManagerFactory 绑定相关设置。基于配置第二个数据源 的示例,可以按如下方式定义第二个 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 secondEntityManagerFactory(
@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 = "entityManagerFactory")
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 的实现。如果它们同时存在于 classpath 中,你可能需要做一些额外的配置,以告知 Spring Boot 应该创建哪些 Repository。最明确的方式是使用标准的 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")
使用两个 DataSource 配置 jOOQ
如果你需要将 jOOQ 与多个数据源一起使用,应该为每个数据源创建自己的 DSLContext。更多细节请参见 JooqAutoConfiguration。
特别是,ExceptionTranslatorExecuteListener 和 SpringTransactionProvider 可以被复用,以提供与自动配置对单个 DataSource 所实现的类似功能。