数据访问

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()
	}

}

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

  • 属性

  • 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 依赖项放在您的类路径中。有一个spring-boot-starter-data-jpa用于 JPA,spring-boot-starter-data-mongodb用于 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。也可以使用单独的 CacheManager 实例。有关详细信息,请参阅 Hibernate 用户指南

在 Hibernate 组件中使用依赖注入

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

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

使用自定义 EntityManagerFactory

要完全控制 EntityManagerFactory 的配置,需要添加一个名为“entityManagerFactory”的 @Bean。Spring Boot 自动配置在存在此类型 bean 的情况下会关闭其实体管理器。

使用多个 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 所提供的功能。