数据访问
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://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 子命名空间。 |
有关更多详细信息,请参见配置 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://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 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 时类似的功能。 |