77. Data Access

77.1 配置自定义数据源

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

@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.*的相关子集仍可以与您的自定义配置一起使用)。

如果要配置定制 JNDI DataSource,则可以应用相同的原理:

@Bean(destroyMethod="")
@ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() throws Exception {
    JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
    return dataSourceLookup.getDataSource("java:comp/env/jdbc/YourDS");
}

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

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

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

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来解决此问题。您将无法在运行时更改实现,但选项列表将是明确的。

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

您甚至可以利用DataSourceProperties为您做的事情走得更远,如果没有提供有意义的用户名和密码的 url,它就会提供默认的嵌入式数据库。您可以从任何DataSourceProperties的状态轻松地初始化DataSourceBuilder,因此您也可以注入一个 Spring Boot 自动创建的状态。但是,这会将您的配置分为两个名称空间:spring.datasource上的 url,用户名,密码,类型和驱动程序,其余部分放在您的自定义名称空间(app.datasource)上。为避免这种情况,您可以在自定义名称空间上重新定义自定义DataSourceProperties

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

@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource(DataSourceProperties properties) {
    return (HikariDataSource) properties.initializeDataSourceBuilder()
            .type(HikariDataSource.class).build();
}

该设置使您与 Spring Boot 的默认设置成对使用,只是选择了专用连接池(以代码形式),并且其设置在同一名称空间中公开。由于DataSourceProperties正在为您处理url/jdbcUrl的翻译,因此可以这样配置:

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

Note

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

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

77.2 配置两个数据源

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

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

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

@Bean
@Primary
@ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
    return fooDataSourceProperties().initializeDataSourceBuilder().build();
}

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

Tip

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

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

app.datasource.foo.type=com.zaxxer.hikari.HikariDataSource
app.datasource.foo.maximum-pool-size=30

app.datasource.bar.url=jdbc:mysql://localhost/test
app.datasource.bar.username=dbuser
app.datasource.bar.password=dbpass
app.datasource.bar.max-total=30

当然,您也可以将相同的概念应用于辅助DataSource

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

@Bean
@Primary
@ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
    return fooDataSourceProperties().initializeDataSourceBuilder().build();
}

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

@Bean
@ConfigurationProperties("app.datasource.bar")
public DataSource barDataSource() {
    return barDataSourceProperties().initializeDataSourceBuilder().build();
}

最后一个示例在自定义名称空间上配置两个数据源,其逻辑与 Spring Boot 在自动配置中的逻辑相同。

77.3 使用 Spring 数据存储库

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

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

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

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

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

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

    //...

}

77.5 配置 JPA 属性

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

spring.jpa.hibernate.ddl-auto是一种特殊情况,它具有不同的默认值,具体取决于是否使用嵌入式数据库(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 属性(前缀被去除)传递。

77.6 配置 Hibernate 命名策略

无论您使用的是 Hibernate 一代,Spring Boot 都会提供一致的命名策略。如果您使用的是 Hibernate 4,则可以使用spring.jpa.hibernate.naming.strategy对其进行自定义; Hibernate 5 定义了PhysicalImplicit命名策略。

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

具体来说,一个TelephoneNumber实体将 Map 到telephone_number表。

如果您想改用 Hibernate 5 的默认值,请设置以下属性:

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

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

77.7 使用自定义 EntityManagerFactory

要完全控制EntityManagerFactory的配置,您需要添加一个名为'entityManagerFactory'的@Bean。 Spring Boot 自动配置会基于该类型的 Bean 的存在来关闭其实体 Management 器。

77.8 使用两个 EntityManager

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

Example:

// 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 {
    ...
}

77.9 使用传统的 persistence.xml

Spring 不需要使用 XML 来配置 JPA 提供程序,并且 Spring Boot 假定您想利用该功能。如果您更喜欢使用persistence.xml,则需要定义自己的LocalEntityManagerFactoryBean类型的@Bean(ID 为'entityManagerFactory',并在其中设置持久性单元名称)。

默认设置请参见JpaBaseConfiguration

77.10 使用 Spring Data JPA 和 Mongo 存储库

Spring Data JPA 和 Spring Data Mongo 都可以自动为您创建Repository实现。如果它们都存在于 Classpath 中,则可能必须做一些额外的配置,以告诉 Spring Boot 您要为您创建存储库中的哪个(或两个)。最明确的方法是使用标准 Spring Data @Enable*Repositories并告诉它Repository接口的位置(其中“ *”为“ Jpa”或“ Mongo”或二者兼有)。

还有标志spring.data.*.repositories.enabled,您可以使用它们在外部配置中打开和关闭自动配置的存储库。例如,这在您要关闭 Mongo 存储库并仍使用自动配置的MongoTemplate的情况下很有用。

对于其他自动配置的 Spring Data 存储库类型(Elasticsearch,Solr),存在相同的障碍和相同的功能。只需分别更改 Comments 和标志的名称。

77.11 将 Spring Data 存储库公开为 REST 端点

只要已为应用程序启用了 Spring MVC,Spring Data REST 就可以将Repository实现作为 REST 终结点公开给您。

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

Note

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

77.12 配置 JPA 使用的组件

如果要配置将由 JPA 使用的组件,则需要确保在 JPA 之前初始化了该组件。自动配置组件的位置 Spring Boot 将为您解决此问题。例如,当自动配置 Flyway 时,Hibernate 被配置为依赖 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");
    }

}