数据访问

Spring Boot 包含许多用于处理数据源的启动器。本节解答了与数据源相关的疑问。

配置自定义 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()
	}

}

以下示例演示如何通过设置属性来定义数据源

  • 属性

  • 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 数据源

  • 属性

  • YAML

app.datasource.url=jdbc:mysql://127.0.0.1/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
  datasource:
    url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

但是,有一个问题。由于连接池的实际类型未公开,因此不会为您的自定义DataSource在元数据中生成键,并且您的 IDE 中也没有代码补全功能(因为DataSource接口不公开任何属性)。此外,如果您的类路径上恰好有 Hikari,则此基本设置不起作用,因为 Hikari 没有url属性(但有jdbcUrl属性)。在这种情况下,您必须按如下所示重写配置

  • 属性

  • YAML

app.datasource.jdbc-url=jdbc:mysql://127.0.0.1/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
  datasource:
    jdbc-url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

您可以通过强制连接池使用并返回专用实现而不是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()
	}

}

您甚至可以更进一步,利用DataSourceProperties为您做的事情——也就是说,如果未提供 URL,则提供具有合理用户名和密码的默认嵌入式数据库。您可以轻松地根据任何DataSourceProperties对象的狀態初始化DataSourceBuilder,因此您也可以注入 Spring Boot 自动创建的 DataSource。但是,这会将您的配置拆分为两个命名空间:spring.datasource上的urlusernamepasswordtypedriver,以及您自定义命名空间(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/jdbcUrl转换,所以您可以按如下方式配置它

  • 属性

  • YAML

app.datasource.url=jdbc:mysql://127.0.0.1/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
app:
  datasource:
    url: "jdbc:mysql://127.0.0.1/test"
    username: "dbuser"
    password: "dbpass"
    configuration:
      maximum-pool-size: 30
Spring Boot 会将 Hikari 特定的设置公开给spring.datasource.hikari。此示例使用更通用的configuration子命名空间,因为该示例不支持多个数据源实现。
由于您的自定义配置选择使用 Hikari,因此app.datasource.type无效。实际上,构建器将使用您可能在那里设置的任何值进行初始化,然后被对.type()的调用覆盖。

请参阅“Spring Boot 功能”部分中的配置 DataSource以及DataSourceAutoConfiguration类以了解更多详细信息。

配置两个数据源

如果您需要配置多个数据源,您可以应用上一节中描述的相同技巧。但是,您必须将其中一个DataSource实例标记为@Primary,因为以后的各种自动配置预计能够按类型获取一个实例。

如果您创建自己的DataSource,则自动配置将回退。在以下示例中,我们提供了与自动配置在主数据源上提供的完全相同的特性集

  • Java

  • Kotlin

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	public DataSourceProperties firstDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	public BasicDataSource secondDataSource() {
		return DataSourceBuilder.create().type(BasicDataSource.class).build();
	}

}
import com.zaxxer.hikari.HikariDataSource
import org.apache.commons.dbcp2.BasicDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
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
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	fun firstDataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	fun secondDataSource(): BasicDataSource {
		return DataSourceBuilder.create().type(BasicDataSource::class.java).build()
	}

}
firstDataSourceProperties必须标记为@Primary,以便数据库初始化程序功能使用您的副本(如果您使用初始化程序)。

两个数据源也都被绑定以进行高级自定义。例如,您可以按如下方式配置它们

  • 属性

  • YAML

app.datasource.first.url=jdbc:mysql://127.0.0.1/first
app.datasource.first.username=dbuser
app.datasource.first.password=dbpass
app.datasource.first.configuration.maximum-pool-size=30
app.datasource.second.url=jdbc:mysql://127.0.0.1/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30
app:
  datasource:
    first:
      url: "jdbc:mysql://127.0.0.1/first"
      username: "dbuser"
      password: "dbpass"
      configuration:
        maximum-pool-size: 30

    second:
      url: "jdbc:mysql://127.0.0.1/second"
      username: "dbuser"
      password: "dbpass"
      max-total: 30

您也可以将相同的概念应用于辅助DataSource,如下例所示

  • Java

  • Kotlin

import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

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;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyCompleteDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	public DataSourceProperties firstDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	public DataSourceProperties secondDataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean
	@ConfigurationProperties("app.datasource.second.configuration")
	public BasicDataSource secondDataSource(
			@Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
		return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build();
	}

}
import com.zaxxer.hikari.HikariDataSource
import org.apache.commons.dbcp2.BasicDataSource
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 MyCompleteDataSourcesConfiguration {

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first")
	fun firstDataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@Primary
	@ConfigurationProperties("app.datasource.first.configuration")
	fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
		return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
	}

	@Bean
	@ConfigurationProperties("app.datasource.second")
	fun secondDataSourceProperties(): DataSourceProperties {
		return DataSourceProperties()
	}

	@Bean
	@ConfigurationProperties("app.datasource.second.configuration")
	fun secondDataSource(secondDataSourceProperties: DataSourceProperties): BasicDataSource {
		return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource::class.java).build()
	}

}

前面的示例在自定义命名空间上配置了两个数据源,其逻辑与 Spring Boot 在自动配置中使用的逻辑相同。请注意,每个configuration子命名空间都根据所选实现提供高级设置。

使用 Spring Data 存储库

Spring Data 可以创建各种类型的Repository接口的实现。Spring Boot 会为您处理所有这些,只要这些Repository实现包含在自动配置包之一中,通常是使用@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属性。

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

  • 属性

  • 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,该 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 的默认值,请设置以下属性

  • 属性

  • 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 注册一个使用BeanFactoryBeanContainer实现,以便转换器和实体侦听器可以使用常规依赖注入。

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

使用自定义 EntityManagerFactory

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

使用多个 EntityManagerFactory

如果您需要对多个数据源使用 JPA,则可能需要每个数据源一个EntityManagerFactory。Spring ORM 中的LocalContainerEntityManagerFactoryBean允许您根据需要配置EntityManagerFactory。您还可以重用JpaProperties来绑定每个EntityManagerFactory的设置,如下例所示

  • Java

  • Kotlin

import javax.sql.DataSource;

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 MyEntityManagerFactoryConfiguration {

	@Bean
	@ConfigurationProperties("app.jpa.first")
	public JpaProperties firstJpaProperties() {
		return new JpaProperties();
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource,
			JpaProperties firstJpaProperties) {
		EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
		return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").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.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 MyEntityManagerFactoryConfiguration {

	@Bean
	@ConfigurationProperties("app.jpa.first")
	fun firstJpaProperties(): JpaProperties {
		return JpaProperties()
	}

	@Bean
	fun firstEntityManagerFactory(
		firstDataSource: DataSource?,
		firstJpaProperties: JpaProperties
	): LocalContainerEntityManagerFactoryBean {
		val builder = createEntityManagerFactoryBuilder(firstJpaProperties)
		return builder.dataSource(firstDataSource).packages(Order::class.java).persistenceUnit("firstDs").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()
	}

}

上面的示例使用名为firstDataSourceDataSource bean 创建EntityManagerFactory。它扫描位于与Order相同的包中的实体。可以使用app.first.jpa命名空间映射其他 JPA 属性。

如果您自己创建LocalContainerEntityManagerFactoryBean的bean,则在自动配置的LocalContainerEntityManagerFactoryBean创建过程中应用的任何自定义配置都将丢失。例如,在Hibernate的情况下,spring.jpa.hibernate前缀下的任何属性都不会自动应用于您的LocalContainerEntityManagerFactoryBean。如果您依赖这些属性来配置命名策略或DDL模式等内容,则需要在创建LocalContainerEntityManagerFactoryBean bean时显式配置它们。

对于您需要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 = "firstEntityManagerFactory")
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.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存储库公开为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

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