跳至主要內容

23. Data(五)-Spring Data JPA(配置,Bootstrap Mode,数据库初始化,命名策略)

安图新大约 10 分钟

23. Data(五)-Spring Data JPA(配置,Bootstrap Mode,数据库初始化,命名策略)

JPA 配置

Spring Data JPA 已经提供了一些独立于供应商的配置选项(例如 SQL 日志),Spring Boot 将这些选项以及一些针对Hibernate的选项作为外部配置属性公开。其中一些是根据上下文自动检测的,因此你不应该设置它们。

属性描述备注
spring.jpa.database要操作的目标数据库,默认自动检测可选配置
spring.jpa.database-platform要操作的目标数据库的名称,默认情况下是自动检测的可以使用"Database"枚举
spring.jpa.defer-datasource-initializationdatasource初始化延迟默认false
spring.jpa.generate-ddl启动时是否初始化数据库schema默认false
spring.jpa.show-sql是否启用SQL语句日志记录默认false
spring.jpa.mapping-resources资源映射(等价于persistence.xml中的“mapping-file”条目)
spring.jpa.open-in-viewOpenEntityManagerInViewInterceptor注册,将JPA EntityManager绑定到线程,用于整个请求处理默认true
spring.jpa.properties要在JPA提供程序上设置的其他本地属性例如:spring.jpa.properties.hibernate.connection.autocommit
spring.data.jpa.repositories.enabled是否启用JPA Repository默认true
spring.data.jpa.repositories.bootstrap-modeJPA Repository的引导模式三种模式:DEFAULT(默认), DEFERRED ,LAZY
spring.jpa.hibernate.ddl-autoDDL模式hibernate.hbm2ddl的快捷方式,当使用嵌入式数据库时,默认为create-drop,否则默认值为 none
spring.jpa.hibernate.naming.implicit-strategy全限定名的隐式命名策略例如:org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
spring.jpa.hibernate.naming.physical-strategy物理命名策略的完全限定名
spring.jpa.hibernate.use-new-id-generator-mappings是否使用Hibernate更新的IdentifierGenerator AUTO, TABLESEQUENCE默认truehibernate.id.new_generator_mappings快捷方式

Bootstrap Mode

默认情况下,Spring Data JPA Repository 是默认的 Spring bean。它们是单例作用域,并且是提前初始化的。在启动期间,它们已经与 JPA EntityManager交互,以进行验证和元数据分析。Spring 框架支持在后台线程中初始化JPA EntityManagerFactory,因为在 Spring 应用程序中,这个过程通常会占用大量的启动时间。为了有效地利用后台初始化,我们需要确保 JPA Repository 的初始化时间越晚越好。

在 Spring Data JPA 中,可以使用spring.data.jpa.repositories.bootstrap-mode配置BootstrapMode:

  • DEFAULT (默认) —  默认模式,除非显示的使用@Lazy 注解延迟加载,否则 Repository 在容器启动过程中初始化。
  • LAZY —  懒加载模式,启动过程中 JPA Respository 组件 bean 并没有真正被初始化。这些 JPA Respository 组件 bean 仅在首次调用前才会被初始化,以及进行相应的验证和元数据分析。
  • DEFERRED —延迟加载模式,本质上和 LAZY 操作模式相同,但是触发一个 ContextRefreshedEvent 事件来通知 Respository 初始化,这样 Respository 在应用程序完全启动之前就被验证了。

模式选择建议:
1.如果你不使用异步 JPA 引导,则坚持使用默认引导模式。
2.如果你想异步引导 JPA, DEFERRED是一个合理的默认值,因为它将确保 Spring Data
JPA 引导只等待EntityManagerFactory设置,如果EntityManagerFactory设置本身需要比初始化所有其他应用程序组件更长的时间。尽管如此,它确保存储库在应用程序发出启动信号之前得到了正确的初始化和验证。
3.对于测试场景和本地开发,LAZY是一个不错的选择。

命名策略

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

默认情况下,Spring Boot 使用CamelCaseToUnderscoresNamingStrategy配置物理命名策略。使用这种策略,所有的点都被下划线取代驼峰大小写也被下划线取代。另外,默认情况下,所有表名都是小写的。例如,TelephoneNumber实体被映射到telephone_number表。如果你的模式需要混合大小写标识符,定义一个自定义CamelCaseToUnderscoresNamingStrategy bean,如下面的示例所示:

@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {



    @Bean
    public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {


        return new CamelCaseToUnderscoresNamingStrategy() {



            @Override
            protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {


                return false;
            }

        };
    }

}

数据库初始化

一个SQL数据库可以用不同的方式初始化,这取决于你的技术栈是什么。

使用 JPA 初始化数据库

JPA 具有用于 DDL 生成的特性,可以将这些特性设置为在启动时针对数据库运行。这是通过两个外部属性控制的:

spring.jpa.generate-ddl (boolean) : 开启和关闭该特性,并且与厂商无关。
spring.jpa.hibernate.ddl-auto (enum) :是以更细粒度的方式控制行为的 Hibernate 特性。

ddl-auto 枚举:
none(默认):禁用DDL处理
validate:验证 schema,不对数据库做任何操作
update: 更新 schema
create: 创建 schema,并销毁之前数据
create-drop: 会话创建时创建 schema,会话关闭时销毁 schema

使用 Hibernate 初始化数据库

你可以显式地设置spring.jpa.hibernate.ddl-auto,标准的 Hibernate 属性值为nonevalidateupdatecreatecreate-drop。 根据 Spring Boot 是否认为数据库是嵌入式的,它会为你选择一个默认值。如果没有检测到 schema 管理器,或者在所有其他情况下都没有检测到schema管理器,则默认为create-drop。通过查看 Connection 类型和 JDBC url 来检测嵌入式数据库(Hsqldbh2Derby是候选项)。从内存数据库切换到“真正的”数据库时要小心,不要假设新平台中存在表和数据。你要么必须显式地设置ddl-auto,要么使用其他机制之一来初始化数据库。

你可以通过启用org.hibernate.SQL日志记录器来输出模式创建。如果你启用调试模式,这将自动完成。

另外,在classpath根目录命名为import.sql的文件,Hibernate 在启动时执行此文件(如果将ddl-auto属性设置为createcreate-drop)。这个是 Hibernate 的特性。

新建实体类:

@Entity
public class Blog {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String content;
    //get set
    }

配置:

spring:
  datasource:
    #数据库驱动完整类名
    driver-class-name: com.mysql.jdbc.Driver
    #数据库连接url
    url: jdbc:mysql://127.0.0.1:3306/spring-boot-data-learn
    #数据库用户名
    username: root
    #数据库密码
    password: 123456
  jpa:
    hibernate:
      ddl-auto: create
logging:
  level:
    org.hibernate.SQL: debug

启动程序:

HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
 org.hibernate.SQL : drop table if exists blog
org.hibernate.SQL : create table blog (id bigint not null auto_increment, content varchar(255), title varchar(255), primary key (id)) engine=InnoDB

使用 SQL 脚本初始化

Spring Boot 可以自动创建 JDBC DataSource Schema (DDL 脚本)或R2DBC ConnectionFactory并初始化它(DML脚本)。 它从标准classpath的根路径位置:分别加载schema.sqldata.sql
另外,Spring Boot 还自动处理schema-${platform}.sqldata-${platform}.sql (如果存在)文件,platform的值来自spring.sql.init.platform变量。这允许你在必要时切换到特定于数据库的脚本。例如,你可以选择将其设置为数据库(hsqldbh2oraclemysqlpostgresql等)的供应商名称。
默认情况下,只有在使用嵌入式内存数据库时才执行SQL数据库初始化。 要始终初始化一个SQL数据库,不管它是什么类型,请将spring.sql.init.mode设置为always。类似地,要禁用初始化,可以将spring.sql.init.mode设置为never。默认情况下,Spring Boot 启用其基于脚本的数据库初始化器的快速失败特性。这意味着,如果脚本导致异常,应用程序将无法启动。你可以通过设置spring.sql.init.continue-on-error来调整该行为。
默认情况下,在创建 JPA EntityManagerFactory bean 之前执行基于脚本的数据源初始化。schema.sql可用于为 JPA-managed 的实体创建schemadata.sql可以填充它。
不建议使用多个数据源初始化技术,如果你希望基于脚本的数据源初始化能够构建在 Hibernate 执行的 schema 创建之上,请将spring.jpa.defer-datasource-initialization设置为true。这将推迟数据源初始化,直到创建并初始化任何 EntityManagerFactory bean 之后。

如果你正在使用高级数据库迁移工具,如FlywayLiquibase,你应该单独使用它们来创建和初始化schema。不建议在FlywayLiquibase一起使用schema.sqldata.sql,并且在未来的版本中将删除支持。

新建 resources/schema.sql 和 data.sql

//schema.sql
create table blog_always (id bigint not null auto_increment, content varchar(255), title varchar(255), primary key (id)) engine=InnoDB;

//data.sql
INSERT INTO blog_always(title,content)
VALUES('hello word',"content");

application.yaml 配置

spring:
  datasource:
    #数据库驱动完整类名
    driver-class-name: com.mysql.jdbc.Driver
    #数据库连接url
    url: jdbc:mysql://127.0.0.1:3306/spring-boot-data-learn
    #数据库用户名
    username: root
    #数据库密码
    password: 123456
  jpa:
    hibernate:
      ddl-auto: create
  sql:
    init:
      mode: always
      continue-on-error: false
logging:
  level:
    org.hibernate.SQL: debug

启动程序:
 

使用 Spring Batch 初始化数据库

如果你使用 Spring Batch,那么它已经预先打包了用于大多数流行数据库平台的SQL初始化脚本。 Spring Boot 可以检测你的数据库类型,并在启动时执行这些脚本。如果使用嵌入式数据库,默认情况下会发生这种情况。你也可以为任何数据库类型启用它,如下所示的示例:

spring:
  batch:
    jdbc:
      initialize-schema: "always"

你还可以通过设置spring.batch.jdbc.initialize-schema=never显式地关闭初始化。

数据库初始化依赖

数据库初始化是在应用程序启动时作为应用程序上下文刷新的一部分执行的。为了允许在启动期间访问已初始化的数据库,将自动检测充当数据库初始化器的 bean 和要求对数据库进行初始化的 bean。其初始化依赖于已初始化的数据库的 bean 被配置为依赖于那些初始化数据库的 bean。如果在启动期间,应用程序试图访问数据库,而数据库还没有初始化,则可以配置对初始化数据库并要求初始化数据库的 bean 的额外检测

检测数据库初始化器

Spring Boot 将自动检测以下类型的 bean 来初始化 SQL 数据库:

  • DataSourceScriptDatabaseInitializer
  • EntityManagerFactory
  • Flyway
  • FlywayMigrationInitializer
  • R2dbcScriptDatabaseInitializer
  • SpringLiquibase

如果你正在为数据库初始化库使用第三方启动器,那么它可能会提供一个检测器,以便自动检测其他类型的bean。要检测其他bean,请在META-INF/spring-factories中注册一个DatabaseInitializerDetector实现。

检测依赖于数据库初始化的 Bean

Spring Boot 将自动检测以下类型的依赖于数据库初始化的bean:

  • AbstractEntityManagerFactoryBean (除非 spring.jpa.defer-datasource-initialization 设置为 true)
  • DSLContext (jOOQ)
  • EntityManagerFactory (除非 spring.jpa.defer-datasource-initialization 设置为 true)
  • JdbcOperations
  • NamedParameterJdbcOperations

如果你正在使用第三方starter数据访问库,它可能会提供一个检测器,以便自动检测其他类型的 bean。要检测其他 bean,请在META-INF/spring-factories中注册一个DependsOnDatabaseInitializationDetector实现。或者,用@DependsOnDatabaseInitialization注解的 bean 的类或它的@Bean方法。