数据访问

Spring Boot 包含多个用于处理数据源的 starters。 本节回答与此相关的问题。

配置自定义 DataSource

要配置您自己的 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,它将为您处理 urljdbc-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 负责 urljdbc-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 将不起作用。

Supported Connection Pools 中所述,DataSourceBuilder 支持几种不同的连接池。 要使用 Hikari 以外的池,请将其添加到类路径,使用 type(Class) 方法指定要使用的池类,并更新 @Bean 方法的返回类型以匹配。 这还将为您提供您选择的特定连接池的配置属性元数据。

提示:Spring Boot 将 Hikari 特定的设置公开给 spring.datasource.hikari。 此示例使用更通用的 configuration 子命名空间,因为该示例不支持多个数据源实现。

有关更多详细信息,请参阅 Configure a DataSourceDataSourceAutoConfiguration 类。

配置两个数据源

要定义额外的 DataSource,可以使用与前一节类似的方法。 一个关键区别是 DataSource @Bean 必须使用 defaultCandidate=false 声明。 这可以防止自动配置的 DataSource 回退。

注意:https://docs.spring.io/spring-framework/reference/6.2/core/beans/dependencies/factory-autowire.html#beans-factory-autowire-candidate[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 的类型。 有关支持的类型的详细信息,请参阅 Supported Connection Pools

使用 Spring Data Repositories

Spring Data 可以创建各种风格的 Repository 接口的实现。 Spring Boot 为您处理所有这些,只要这些 Repository 实现包含在 自动配置包 之一中,通常是使用 @SpringBootApplication@EnableAutoConfiguration 注释的主应用程序类的包(或子包)。

对于许多应用程序,您只需要在类路径上放置正确的 Spring Data 依赖项。 有用于 JPA 的 spring-boot-starter-data-jpa,用于 Mongodb 的 spring-boot-starter-data-mongodb,以及用于其他支持技术的各种 starters。 要开始使用,请创建一些存储库接口来处理您的 @Entity 对象。

Spring Boot 通过扫描 自动配置包 来确定您的 Repository 实现的位置。 要获得更多控制,请使用 Spring Data 的 @Enable…Repositories 注释。

有关 Spring Data 的更多信息,请参阅 Spring Data 项目页面

将 @Entity 定义与 Spring 配置分离

Spring Boot 通过扫描 自动配置包 来确定您的 @Entity 定义的位置。 要获得更多控制,请使用 @EntityScan 注释,如下例所示:

  • Java

  • Kotlin

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

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

	// ...

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

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

	// ...

}

过滤扫描的 @Entity 定义

可以使用 ManagedClassNameFilter bean 过滤 @Entity 定义。 这在测试中很有用,当只应考虑可用实体的子集时。 在以下示例中,只包含来自 com.example.app.customer 包的实体:

  • Java

  • Kotlin

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

@Configuration(proxyBeanMethods = false)
public class MyEntityScanConfiguration {

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

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

@Configuration(proxyBeanMethods = false)
class MyEntityScanConfiguration {

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

配置 JPA 属性

Spring Data JPA 已经提供了一些与供应商无关的配置选项(例如用于 SQL 日志记录的选项),Spring Boot 将这些选项以及一些 Hibernate 特定的选项作为外部配置属性公开。 其中一些会根据上下文自动检测,因此您不必设置它们。

spring.jpa.hibernate.ddl-auto 是一个特殊情况,因为根据运行时条件,它有不同的默认值。 如果使用嵌入式数据库且没有模式管理器(如 Liquibase 或 Flyway)处理 DataSource,则默认为 create-drop。 在所有其他情况下,它默认为 none

JPA 提供程序检测要使用的方言。 如果您希望自己设置方言,请设置 spring.jpa.database-platform 属性。

最常见的设置选项如下例所示:

  • Properties

  • YAML

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

此外,当创建本地 EntityManagerFactory 时,spring.jpa.properties.* 中的所有属性都会作为普通 JPA 属性传递(前缀被剥离)。

您需要确保在 spring.jpa.properties.* 下定义的名称与 JPA 提供程序期望的名称完全匹配。 Spring Boot 不会对这些条目尝试任何类型的宽松绑定。

例如,如果要配置 Hibernate 的批处理大小,必须使用 spring.jpa.properties.hibernate.jdbc.batch_size。 如果使用其他形式,如 batchSizebatch-size,Hibernate 将不会应用该设置。

提示:如果您需要对 Hibernate 属性应用高级自定义,请考虑注册一个 HibernatePropertiesCustomizer bean,它将在创建 EntityManagerFactory 之前被调用。 这优先于自动配置应用的任何内容。

配置 Hibernate 命名策略

Hibernate 使用 两种不同的命名策略 将对象模型中的名称映射到相应的数据库名称。 可以通过设置 spring.jpa.hibernate.naming.physical-strategyspring.jpa.hibernate.naming.implicit-strategy 属性分别配置物理和隐式策略实现的完全限定类名。 或者,如果应用程序上下文中存在 ImplicitNamingStrategyPhysicalNamingStrategy bean,Hibernate 将自动配置为使用它们。

默认情况下,Spring Boot 使用 CamelCaseToUnderscoresNamingStrategy 配置物理命名策略。 使用此策略,所有点都被下划线替换,驼峰命名也被下划线替换。 此外,默认情况下,所有表名都以小写形式生成。 例如,TelephoneNumber 实体映射到 telephone_number 表。 如果您的模式需要混合大小写标识符,请定义自定义 CamelCaseToUnderscoresNamingStrategy bean,如下例所示:

  • Java

  • Kotlin

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

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

@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {

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

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

		};
	}

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

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

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

}

如果您希望使用 Hibernate 的默认值,请设置以下属性:

  • Properties

  • YAML

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

或者,您可以配置以下 bean:

  • Java

  • Kotlin

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

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

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

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

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

@Configuration(proxyBeanMethods = false)
internal class MyHibernateConfiguration {

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

}

有关更多详细信息,请参阅 HibernateJpaAutoConfigurationJpaBaseConfiguration

配置 Hibernate 二级缓存

Hibernate 二级缓存 可以为各种缓存提供程序配置。 与其配置 Hibernate 再次查找缓存提供程序,不如在可能的情况下提供上下文中可用的缓存提供程序。

要使用 JCache 执行此操作,首先确保类路径上有 org.hibernate.orm:hibernate-jcache。 然后,添加 HibernatePropertiesCustomizer bean,如下例所示:

  • Java

  • Kotlin

import org.hibernate.cache.jcache.ConfigSettings;

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

@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {

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

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

@Configuration(proxyBeanMethods = false)
class MyHibernateSecondLevelCacheConfiguration {

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

}

此自定义器将配置 Hibernate 使用与应用程序相同的 CacheManager。 也可以使用单独的 CacheManager 实例。 有关详细信息,请参阅 Hibernate 用户指南

在 Hibernate 组件中使用依赖注入

默认情况下,Spring Boot 注册一个 BeanContainer 实现,它使用 BeanFactory,以便转换器和实体监听器可以使用常规依赖注入。

您可以通过注册一个 HibernatePropertiesCustomizer 来禁用或调整此行为,该自定义器删除或更改 hibernate.resource.beans.container 属性。

使用自定义 EntityManagerFactory

要完全控制 EntityManagerFactory 的配置,您需要添加一个名为 ‘entityManagerFactory’ 的 @Bean。 Spring Boot 自动配置在存在该类型的 bean 时会关闭其实体管理器。

注意:当您自己创建 LocalContainerEntityManagerFactoryBean 的 bean 时,在创建自动配置的 LocalContainerEntityManagerFactoryBean 期间应用的任何自定义都会丢失。 确保使用自动配置的 EntityManagerFactoryBuilder 来保留 JPA 和供应商属性。 如果您依赖 spring.jpa.* 属性来配置命名策略或 DDL 模式等内容,这一点尤其重要。

使用多个 EntityManagerFactories

如果您需要对多个数据源使用 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) 允许定义 secondJpaPropertiessecondEntityManagerFactory bean,而不会干扰相同类型的自动配置 bean。

注意:https://docs.spring.io/spring-framework/reference/6.2/core/beans/dependencies/factory-autowire.html#beans-factory-autowire-candidate[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 Repositories

Spring Data JPA 和 Spring Data Mongo 都可以自动为您创建 Repository 实现。 如果它们都在类路径上,您可能需要进行一些额外的配置来告诉 Spring Boot 创建哪些存储库。 最明确的方法是使用标准的 Spring Data @EnableJpaRepositories@EnableMongoRepositories 注释,并提供您的 Repository 接口的位置。

还有一些标志(spring.data.*.repositories.enabledspring.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 Repositories 公开为 REST 端点

Spring Data REST 可以为您将 Repository 实现公开为 REST 端点, 前提是已为应用程序启用 Spring MVC。

Spring Boot 公开了一组有用的属性(来自 spring.data.rest 命名空间),用于自定义 RepositoryRestConfiguration。 如果您需要提供额外的自定义,您应该使用 RepositoryRestConfigurer bean。

注意:如果您没有在自定义 RepositoryRestConfigurer 上指定任何顺序,它会在 Spring Boot 内部使用的那个之后运行。 如果您需要指定顺序,请确保它高于 0。

配置 JPA 使用的组件

如果您想配置 JPA 使用的组件,则需要确保该组件在 JPA 之前初始化。 当组件是自动配置的时,Spring Boot 会为您处理这个问题。 例如,当 Flyway 是自动配置的时,Hibernate 被配置为依赖于 Flyway,以便 Flyway 有机会在 Hibernate 尝试使用数据库之前初始化数据库。

如果您自己配置组件,您可以使用 EntityManagerFactoryDependsOnPostProcessor 子类作为设置必要依赖项的便捷方式。 例如,如果您使用 Hibernate Search 并将 Elasticsearch 作为其索引管理器,则必须将任何 EntityManagerFactory bean 配置为依赖于 elasticsearchClient bean,如下例所示:

  • Java

  • Kotlin

import jakarta.persistence.EntityManagerFactory;

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

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

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

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

@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
	EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")

使用两个数据源配置 jOOQ

如果您需要使用 jOOQ 处理多个数据源,您应该为每个数据源创建自己的 DSLContext。 有关更多详细信息,请参阅 JooqAutoConfiguration

提示:特别是,可以重用 ExceptionTranslatorExecuteListenerSpringTransactionProvider 来提供与自动配置使用单个 DataSource 时类似的功能。