84. Data Access

Spring Boot 包含许多用于处理数据源的启动器。本部分回答与这样做有关的问题。

84.1 配置自定义数据源

要配置自己的DataSource,请在配置中定义该类型的@Bean。 Spring Boot 在需要的任何地方(包括数据库初始化)重用DataSource。如果需要外部化某些设置,则可以将DataSource绑定到环境(请参阅“ 第 24.8.1 节,“第三方配置””)。

以下示例显示了如何在 Bean 中定义数据源:

@Bean
@ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() {
	return new FancyDataSource();
}

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

app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30

假设FancyDataSource具有 URL,用户名和池大小的常规 JavaBean 属性,则在DataSource可用于其他组件之前,这些设置会自动绑定。常规database initialization也会发生(因此spring.datasource.*的相关子集仍可以与您的自定义配置一起使用)。

Spring Boot 还提供了一个名为DataSourceBuilder的 Util 生成器类,该类可用于创建标准数据源之一(如果它在 Classpath 中)。构建器可以根据 Classpath 上可用的内容来检测要使用的一个。它还基于 JDBC URL 自动检测驱动程序。

下面的示例演示如何使用DataSourceBuilder创建数据源:

@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
	return DataSourceBuilder.create().build();
}

要使用DataSource运行应用程序,您需要的只是连接信息。还可以提供特定于池的设置。有关更多详细信息,请检查将在运行时使用的实现。

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

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

但是,有一个陷阱。由于未公开连接池的实际类型,因此自定义DataSource的元数据中不会生成任何键,并且 IDE 中也无法完成操作(因为DataSource接口未公开任何属性)。另外,如果您碰巧在 Classpath 上有 Hikari,则此基本设置也不起作用,因为 Hikari 没有url属性(但确实具有jdbcUrl属性)。在这种情况下,您必须按照以下方式重写配置:

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

您可以通过强制连接池使用并返回专用的实现而不是DataSource来解决此问题。您无法在运行时更改实现,但是选项列表将是明确的。

以下示例显示了如何使用DataSourceBuilder创建HikariDataSource

@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource() {
	return DataSourceBuilder.create().type(HikariDataSource.class).build();
}

您甚至可以利用DataSourceProperties为您做的事情进一步 Developing,也就是,如果没有提供 URL,则为默认的嵌入式数据库提供合理的用户名和密码。您可以从任何DataSourceProperties对象的状态轻松地初始化DataSourceBuilder,因此还可以注入 Spring Boot 自动创建的数据源。但是,这会将您的配置分为两个名称空间:spring.datasource上的urlusernamepasswordtypedriver,其余的放在您的自定义名称空间(app.datasource)上。为避免这种情况,可以在自定义名称空间上重新定义自定义DataSourceProperties,如以下示例所示:

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

这个设置使您与 Spring Boot 默认为您保持同步,除了选择了一个专用连接池(以代码形式),并且其设置在app.datasource.configuration子命名空间中公开之外。由于DataSourceProperties正在为您处理url/jdbcUrl转换,因此您可以按以下方式进行配置:

app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30

Tip

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

Note

由于您的自定义配置选择使用 Hikari,因此app.datasource.type无效。实际上,将使用您可以在此处设置的任何值初始化构建器,然后通过对.type()的调用来覆盖构建器。

有关更多详细信息,请参见“ Spring Boot 功能”部分和DataSourceAutoConfiguration类中的“ 第 30.1 节“配置数据源””。

84.2 配置两个数据源

如果需要配置多个数据源,则可以应用上一节中介绍的相同技巧。但是,您必须将DataSource实例之一标记为@Primary,因为将来各种自动配置都希望能够按类型获得一个。

如果您创建自己的DataSource,则自动配置将退出。在下面的示例中,我们提供与自动配置在主要数据源上提供的功能完全相同的功能:

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

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

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

Tip

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

这两个数据源也都必须进行高级定制。例如,您可以按以下方式配置它们:

app.datasource.first.url=jdbc:mysql://localhost/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://localhost/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30

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

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

@Bean
@Primary
@ConfigurationProperties("app.datasource.first.configuration")
public HikariDataSource firstDataSource() {
	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() {
	return secondDataSourceProperties().initializeDataSourceBuilder()
			.type(BasicDataSource.class).build();
}

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

84.3 使用 Spring 数据存储库

Spring Data 可以创建各种风格的@Repository接口的实现。只要@Repositories包含在@EnableAutoConfiguration类的同一包(或子包)中,Spring Boot 就会为您处理所有这些操作。

对于许多应用程序,您所需要做的就是将正确的 Spring Data 依赖项放在 Classpath 上(对于 JPA,有spring-boot-starter-data-jpa;对于 Mongodb 是spring-boot-starter-data-mongodb),并创建了一些存储库接口来处理@Entity对象。示例在JPA sampleMongodb sample中。

Spring Boot 会根据找到的@EnableAutoConfiguration来猜测您的@Repository定义的位置。要获得更多控制权,请使用@EnableJpaRepositoriesComments(来自 Spring Data JPA)。

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

84.4 将@Entity 定义与 Spring 配置分开

Spring Boot 会根据找到的@EnableAutoConfiguration来猜测您的@Entity定义的位置。要获得更多控制权,可以使用@EntityScan注解,如以下示例所示:

@Configuration
@EnableAutoConfiguration
@EntityScan(basePackageClasses=City.class)
public class Application {

	//...

}

84.5 配置 JPA 属性

Spring Data JPA 已经提供了一些独立于供应商的配置选项(例如用于 SQL 日志记录的那些),并且 Spring Boot 公开了这些选项,还为 Hibernate 提供了一些其他选项作为外部配置属性。其中一些会根据上下文自动检测到,因此您不必进行设置。

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

还会根据当前的DataSource自动检测要使用的方言,但是如果您要明确表示并绕过启动检查,则可以自己设置spring.jpa.database

Note

指定database将导致配置良好的 Hibernate 方言。多个数据库有多个Dialect,这可能不满足您的需求。在这种情况下,您可以将spring.jpa.database设置为default以便让 Hibernate 弄清楚,也可以通过设置spring.jpa.database-platform属性来设置方言。

以下示例显示了最常用的设置选项:

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

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

Tip

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

84.6 配置 Hibernate 命名策略

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

默认情况下,Spring Boot 使用SpringPhysicalNamingStrategy配置物理命名策略。此实现提供了与 Hibernate 4 相同的表结构:所有点都由下划线替换,骆驼套也由下划线替换。默认情况下,所有表名均以小写形式生成,但是如果您的模式需要它,则可以覆盖该标志。

例如,一个TelephoneNumber实体被 Map 到telephone_number表。

如果您更喜欢使用 Hibernate 5 的默认设置,请设置以下属性:

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

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

@Bean
public PhysicalNamingStrategy physicalNamingStrategy() {
	return new PhysicalNamingStrategyStandardImpl();
}

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

84.7 配置 Hibernate 二级缓存

可以为一系列缓存提供程序配置 Hibernate second-level cache。与其将 Hibernate 配置为再次查找缓存提供程序,不如提供尽可能在上下文中可用的缓存提供程序。

如果您使用的是 JCache,这非常简单。首先,确保org.hibernate:hibernate-jcache在 Classpath 上可用。然后,添加一个HibernatePropertiesCustomizer bean,如以下示例所示:

@Configuration
public class HibernateSecondLevelCacheExample {

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

	}

}

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

84.8 在 Hibernate 组件中使用依赖注入

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

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

84.9 使用自定义 EntityManagerFactory

要完全控制EntityManagerFactory的配置,您需要添加一个名为'entityManagerFactory'的@Bean。如果存在这种类型的 Bean,Spring Boot 自动配置将关闭其实体 Management 器。

84.10 使用两个 EntityManager

即使默认的EntityManagerFactory可以正常工作,您也需要定义一个新的。否则,该类型的第二个 bean 的存在将关闭默认值。为了简化操作,您可以使用 Spring Boot 提供的便捷的EntityManagerBuilder。另外,您也可以直接从 Spring ORM 中使用LocalContainerEntityManagerFactoryBean,如以下示例所示:

// add two data sources configured as above

@Bean
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
		EntityManagerFactoryBuilder builder) {
	return builder
			.dataSource(customerDataSource())
			.packages(Customer.class)
			.persistenceUnit("customers")
			.build();
}

@Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
		EntityManagerFactoryBuilder builder) {
	return builder
			.dataSource(orderDataSource())
			.packages(Order.class)
			.persistenceUnit("orders")
			.build();
}

上面的配置几乎可以独立工作。要完成图片,您还需要为两个EntityManagers配置TransactionManagers。如果将其中一个标记为@Primary,则可以在 Spring Boot 中将其默认为JpaTransactionManager拾取。另一个必须显式地注入到新实例中。另外,您也许可以使用跨这两者的 JTA 事务 Management 器。

如果使用 Spring Data,则需要相应地配置@EnableJpaRepositories,如以下示例所示:

@Configuration
@EnableJpaRepositories(basePackageClasses = Customer.class,
		entityManagerFactoryRef = "customerEntityManagerFactory")
public class CustomerConfiguration {
	...
}

@Configuration
@EnableJpaRepositories(basePackageClasses = Order.class,
		entityManagerFactoryRef = "orderEntityManagerFactory")
public class OrderConfiguration {
	...
}

84.11 使用传统的 persistence.xml 文件

默认情况下,Spring Boot 不会搜索或使用META-INF/persistence.xml。如果您喜欢使用传统的persistence.xml,则需要定义自己的LocalEntityManagerFactoryBean类型的@Bean(ID 为'entityManagerFactory'),并在其中设置持久性单元名称。

默认设置请参见JpaBaseConfiguration

84.12 使用 Spring Data JPA 和 Mongo 存储库

Spring Data JPA 和 Spring Data Mongo 都可以为您自动创建Repository实现。如果它们都存在于 Classpath 中,则可能必须做一些额外的配置以告诉 Spring Boot 要创建哪个存储库。最明确的方法是使用标准 Spring Data @EnableJpaRepositories@EnableMongoRepositories注解并提供Repository接口的位置。

还有一些标志(spring.data.*.repositories.enabledspring.data.*.repositories.type),可用于在外部配置中打开和关闭自动配置的存储库。这样做很有用,例如,如果您想关闭 Mongo 存储库并仍然使用自动配置的MongoTemplate

其他自动配置的 Spring Data 存储库类型(Elasticsearch,Solr 等)存在相同的障碍和相同的功能。要使用它们,请相应地更改 Comments 和标志的名称。

84.13 自定义 Spring Data 的 Web 支持

Spring Data 提供了 Web 支持,可简化 Web 应用程序中 Spring Data 存储库的使用。 Spring Boot 在spring.data.web名称空间中提供了用于自定义其配置的属性。请注意,如果您使用的是 Spring Data REST,则必须改为使用spring.data.rest名称空间中的属性。

84.14 将 Spring 数据存储库公开为 REST 端点

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

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

Note

如果您未在自定义RepositoryRestConfigurer上指定任何 Sequences,它将在一个 Spring Boot 内部使用之后运行。如果您需要指定一个订单,请确保它大于 0.

84.15 配置 JPA 使用的组件

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

如果您自己配置组件,则可以使用EntityManagerFactoryDependsOnPostProcessor子类作为构建必要依赖关系的便捷方法。例如,如果您将 Hibernate Search 和 Elasticsearch 用作其索引 Management 器,则必须将任何EntityManagerFactory bean 配置为依赖elasticsearchClient bean,如以下示例所示:

/**
 * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
 * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
 */
@Configuration
static class ElasticsearchJpaDependencyConfiguration
		extends EntityManagerFactoryDependsOnPostProcessor {

	ElasticsearchJpaDependencyConfiguration() {
		super("elasticsearchClient");
	}

}

84.16 使用两个数据源配置 jOOQ

如果需要将 jOOQ 与多个数据源一起使用,则应该为每个数据源创建自己的DSLContext。有关更多详细信息,请参考JooqAutoConfiguration

Tip

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