数据访问
Spring Boot 包含了许多用于处理数据源的启动器。本节将回答与之相关的问题。
配置自定义数据源
要配置你自己的 DataSource,可以在你的配置中定义一个该类型的 @Bean。Spring Boot 会在任何需要的地方重用你的 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://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
的转换。你可以通过 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://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 之外的连接池,请将其添加到类路径中,使用 type(Class)
方法指定要使用的连接池类,并更新 @Bean 方法的返回类型以匹配。这将为您提供所选特定连接池的配置属性元数据。
Spring Boot 会将 Hikari 特定的设置暴露给 spring.datasource.hikari
。此示例使用更通用的 configuration
子命名空间,因为该示例不支持多数据源实现。
请参阅配置数据源以及DataSourceAutoConfiguration类以获取更多详细信息。
配置两个数据源
要定义额外的 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://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
更高级的、与具体实现相关的配置可以通过 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 仓库
Spring Data 能够创建各种风格的 Repository 接口实现。只要这些 Repository 实现包含在某个 自动配置包 中,Spring Boot 就会为你处理所有这些工作。通常情况下,这个包(或其子包)是你的主应用程序类所在的包,该类通常使用 @SpringBootApplication 或 @EnableAutoConfiguration 注解进行标记。
对于许多应用程序而言,您只需在类路径上添加合适的 Spring Data 依赖项即可。例如,针对 JPA 有 spring-boot-starter-data-jpa
,针对 MongoDB 有 spring-boot-starter-data-mongodb
,以及其他各种支持技术的启动器。要开始使用,请创建一些仓库接口来处理您的 @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
。如果你使用其他形式,如 batchSize
或 batch-size
,Hibernate 将不会应用该设置。
如果你需要对 Hibernate 属性进行高级定制,可以考虑注册一个 HibernatePropertiesCustomizer 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
在 classpath 中可用。然后,按照以下示例添加一个 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,以便转换器和实体监听器可以使用常规的依赖注入。
你可以通过注册一个 HibernatePropertiesCustomizer 来禁用或调整此行为,该自定义器可以移除或更改 hibernate.resource.beans.container
属性。
使用自定义的 EntityManagerFactory
要完全控制 EntityManagerFactory 的配置,你需要添加一个名为 entityManagerFactory
的 @Bean。当存在该类型的 Bean 时,Spring Boot 的自动配置会关闭其默认的实体管理器。
当你自己为 LocalContainerEntityManagerFactoryBean 创建一个 bean 时,自动配置的 LocalContainerEntityManagerFactoryBean 创建过程中应用的所有自定义配置都会丢失。确保使用自动配置的 EntityManagerFactoryBuilder 以保留 JPA 和供应商属性。如果你依赖 spring.jpa.*
属性来配置命名策略或 DDL 模式,这一点尤为重要。
使用多个 EntityManagerFactory
如果你需要使用 JPA 操作多个数据源,你可能需要为每个数据源配置一个 EntityManagerFactory。Spring ORM 中的 LocalContainerEntityManagerFactoryBean 允许你根据需要配置一个 EntityManagerFactory。你还可以重用 JpaProperties 来为第二个 EntityManagerFactory 绑定设置。基于配置第二个数据源的示例,第二个 EntityManagerFactory 可以如下所示进行定义:
- Java
- Kotlin
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);
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.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)
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)
return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.properties, null)
}
private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
// ... map JPA properties as needed
return HibernateJpaVendorAdapter()
}
}
上面的示例使用带有 @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
,你需要自己定义一个类型为 LocalEntityManagerFactoryBean 的 @Bean(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。
特别是 JooqExceptionTranslator 和 SpringTransactionProvider 可以重复使用,以提供与自动配置使用单个 DataSource 时类似的功能。