Hibernate ORM 中文操作指南

2. Configuration and bootstrap

我们想让此部分简短。不幸的是,有多种不同的方法来配置和引导 Hibernate,并且我们不得不详细描述至少其中两种方法。

获取 Hibernate 实例的四种基本方法显示在下表中:

使用标准 JPA 定义的 XML 和操作 Persistence.createEntityManagerFactory()

通常在 JPA 实现之间的可移植性很重要时选择。

使用 Configuration 类构造 SessionFactory

如果跨 JPA 实现的可移植性不重要,此选项则更快、增加了些许灵活性并节省类型转换。

使用 org.hibernate.boot 中定义的更复杂的 API

此选项主要由框架集成器使用,不在本文档的讨论范围之内。

让容器负责引导过程并注入 SessionFactoryEntityManagerFactory

用在 WildFly 或 Quarkus 等容器环境中。

在这里,我们将重点关注前两种方法。

如果您在容器环境外使用 Hibernate,则需要:

  1. 在你的项目中包含 Hibernate ORM 本身和合适的 JDBC 驱动程序作为依赖项,并且

  2. 通过指定配置属性,使用有关你的数据库的信息配置 Hibernate。

2.1. Including Hibernate in your project build

首先,将以下依赖项添加到您的项目中:

其中 {version} 是您使用的 Hibernate 版本。

您还需要为您的数据库添加 JDBC 驱动程序的依赖项。

表 2. JDBC 驱动程序依赖项

Database

Driver dependency

PostgreSQL or CockroachDB

org.postgresql:postgresql:{version}

MySQL or TiDB

com.mysql:mysql-connector-j:{version}

MariaDB

org.mariadb.jdbc:mariadb-java-client:{version}

DB2

com.ibm.db2:jcc:{version}

SQL Server

com.microsoft.sqlserver:mssql-jdbc:${version}

Oracle

com.oracle.database.jdbc:ojdbc11:${version}

H2

com.h2database:h2:{version}

HSQLDB

org.hsqldb:hsqldb:{version}

其中 {version} 是您数据库的 JDBC 驱动程序的最新版本。

2.2. Optional dependencies

您还可以选择添加以下任何附加功能:

表 3. 可选依赖项

Optional feature

Dependencies

An SLF4J logging implementation

org.apache.logging.log4j:log4j-core or org.slf4j:slf4j-jdk14

JDBC 连接池,例如 Agroal

org.hibernate.orm:hibernate-agroal and io.agroal:agroal-pool

Hibernate Metamodel Generator,尤其是当您使用 JPA 标准查询 API 时

org.hibernate.orm:hibernate-processor

Query Validator,用于对 HQL 进行编译时检查

org.hibernate:query-validator

Hibernate ValidatorBean Validation的实现

org.hibernate.validator:hibernate-validator and org.glassfish:jakarta.el

通过 JCache 和 EHCache提供本地二级缓存支持

org.hibernate.orm:hibernate-jcache and org.ehcache:ehcache

通过 JCache 和 Caffeine提供本地二级缓存支持

org.hibernate.orm:hibernate-jcache and com.github.ben-manes.caffeine:jcache

通过 Infinispan提供分布式二级缓存支持

org.infinispan:infinispan-hibernate-cache-v60

用于使用 JSON 数据类型的 JSON 序列化库,例如 JacksonYasson

com.fasterxml.jackson.core:jackson-databind or org.eclipse:yasson

Hibernate Spatial

org.hibernate.orm:hibernate-spatial

Envers,用于审计历史数据

org.hibernate.orm:hibernate-envers

如果您希望使用 field-level lazy fetching,您还可以将 Hibernate bytecode enhancer 添加到 Gradle 构建中。

2.3. Configuration using JPA XML

坚持使用 JPA 标准方法,我们将提供一个名为 persistence.xml 的文件,我们通常将其放在 persistence archiveMETA-INF 目录中,即包含我们的实体类的 .jar 文件或目录。

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="2.0">

    <persistence-unit name="org.hibernate.example">

        <class>org.hibernate.example.Book</class>
        <class>org.hibernate.example.Author</class>

        <properties>
            <!-- PostgreSQL -->
            <property name="jakarta.persistence.jdbc.url"
                      value="jdbc:postgresql://localhost/example"/>

            <!-- Credentials -->
            <property name="jakarta.persistence.jdbc.user"
                      value="gavin"/>
            <property name="jakarta.persistence.jdbc.password"
                      value="hibernate"/>

            <!-- Automatic schema export -->
            <property name="jakarta.persistence.schema-generation.database.action"
                      value="drop-and-create"/>

            <!-- SQL statement logging -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.highlight_sql" value="true"/>

        </properties>

    </persistence-unit>

</persistence>

<persistence-unit> 元素定义了一个命名的 persistence unit,即:

  1. 一组关联的实体类型,以及

  2. 一组默认配置设置,可在运行时增加或覆盖。

每个 <class> 元素都指定一个实体类的完全限定名称。

每个 <property> 元素指定一个 configuration property 及其值。请注意:

  1. jakarta.persistence 命名空间中的配置属性是 JPA 规范定义的标准属性,并且

  2. hibernate 命名空间中的属性是 Hibernate 特有的。

我们可以通过调用 Persistence.createEntityManagerFactory() 来获取 EntityManagerFactory

EntityManagerFactory entityManagerFactory =
    Persistence.createEntityManagerFactory("org.hibernate.example");

如有必要,我们可以覆盖 persistence.xml 中指定的配置属性:

EntityManagerFactory entityManagerFactory =
    Persistence.createEntityManagerFactory("org.hibernate.example",
            Map.of(AvailableSettings.JAKARTA_JDBC_PASSWORD, password));

2.4. Configuration using Hibernate API

或者,它可以尊敬的类 Configuration 允许在 Java 代码中配置 Hibernate 实例。

SessionFactory sessionFactory =
        new Configuration()
            .addAnnotatedClass(Book.class)
            .addAnnotatedClass(Author.class)
            // PostgreSQL
            .setProperty(AvailableSettings.JAKARTA_JDBC_URL, "jdbc:postgresql://localhost/example")
            // Credentials
            .setProperty(AvailableSettings.JAKARTA_JDBC_USER, user)
            .setProperty(AvailableSettings.JAKARTA_JDBC_PASSWORD, password)
            // Automatic schema export
            .setProperty(AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION,
                         Action.SPEC_ACTION_DROP_AND_CREATE)
            // SQL statement logging
            .setProperty(AvailableSettings.SHOW_SQL, true)
            .setProperty(AvailableSettings.FORMAT_SQL, true)
            .setProperty(AvailableSettings.HIGHLIGHT_SQL, true)
            // Create a new SessionFactory
            .buildSessionFactory();

自最初(1.0 之前的版本)的 Hibernate 版本起,Configuration 类几乎保持不变,所以它看起来不太现代。另一方面,它非常易于使用,并展示了一些 persistence.xml 不支持的选项。

2.5. Configuration using Hibernate properties file

如果我们使用 Hibernate Configuration API,但不想直接在 Java 代码中设置某些配置属性,可以在一个名为 hibernate.properties 的文件中指定它们,并将文件放入根类路径中。

# PostgreSQL
jakarta.persistence.jdbc.url=jdbc:postgresql://localhost/example
# Credentials
jakarta.persistence.jdbc.user=hibernate
jakarta.persistence.jdbc.password=zAh7mY$2MNshzAQ5

# SQL statement logging
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.highlight_sql=true

2.6. Basic configuration settings

AvailableSettings 枚举了 Hibernate 所理解的所有配置属性。

当然,我们不会在本教程中介绍所有有用的配置设置。我们将提一下你刚开始使用时需要的设置,稍后我们会回到其他重要设置,特别是在讨论性能调优的时候。

Hibernate 有很多(太多)开关和切换。请不要沉迷于调整这些设置;大多数设置很少需要,许多设置仅用于提供与较早版本的 Hibernate 的向后兼容性。除了少数例外情况,这些设置中的每一个的默认行为都被仔细选择以 the behavior we recommend

真正需要了解的属性有以下三个:

表 4。JDBC 连接设置

Configuration property name

Purpose

jakarta.persistence.jdbc.url

数据库的 JDBC URL

jakarta.persistence.jdbc.user and jakarta.persistence.jdbc.password

Your database credentials

在 Hibernate 6 中,不必指定 hibernate.dialect 。将自动为您确定正确的 Hibernate SQL Dialect 。指定此属性的唯一原因是您正在使用自定义的用户编写的 Dialect 类。

同样,在使用所支持数据库之一时,也不需要 hibernate.connection.driver_classjakarta.persistence.jdbc.driver

某些环境下,能够不访问数据库而启动 Hibernate 会很有用。在这种情况下,我们不仅必须明确指定数据库平台,还必须使用标准 JPA 配置属性指定数据库版本。

# disable use of JDBC database metadata
hibernate.boot.allow_jdbc_metadata_access=false

# explicitly specify database and version
jakarta.persistence.database-product-name=PostgreSQL
jakarta.persistence.database-major-version=15
jakarta.persistence.database-minor-version=7

产品名是 java.sql.DatabaseMetaData.getDatabaseProductName() 返回的值,例如 PostgreSQLMySQLH2OracleEnterpriseDBMariaDBMicrosoft SQL Server

表 5。数据库在启动时不可访问时需要的设置

Configuration property name

Purpose

hibernate.boot.allow_jdbc_metadata_access

设置为 false 在启动时禁止访问数据库

jakarta.persistence.database-product-name

根据 JDBC 驱动程序,数据库的产品名称

jakarta.persistence.database-major-version and jakarta.persistence.database-minor-version

数据库的主次版本

池化 JDBC 连接是极其重要的一个性能优化设置。你可以使用此属性设置 Hibernate 内置连接池的大小:

表 6。内置连接池大小

Configuration property name

Purpose

hibernate.connection.pool_size

内置连接池的大小

默认情况下,Hibernate 使用了功能简单的内置连接池。此池不是为产品用途而设计的,后面讨论性能时,我们将会介绍如何 select a more robust implementation

或者,在容器环境中,至少需要以下某个属性:

表 7。事务管理设置

Configuration property name

Purpose

jakarta.persistence.transactionType

(可选的,默认为 JTA)确定事务管理是通过 JTA 还是资源本地事务。如果 JTA 不应使用,则指定 RESOURCE_LOCAL

jakarta.persistence.jtaDataSource

JTA 数据源的 JNDI 名称

jakarta.persistence.nonJtaDataSource

非 JTA 数据源的 JNDI 名称

在这种情况下,Hibernate 从一个容器管理的 DataSource 获取池化的 JDBC 数据库连接。

2.7. Automatic schema export

你可以让 Hibernate 从你在 Java 代码中指定的映射注释推断出你的数据库架构,并通过指定以下一个或多个配置属性在初始化时导出架构:

表 8。架构管理设置

Configuration property name

Purpose

jakarta.persistence.schema-generation.database.action

如果 drop-and-create,首先放弃架构,然后导出表格、序列和其他约束如果 create,导出表格、序列和其他约束,而没有尝试在首先放弃它们如果 create-drop,放弃架构,并在 SessionFactory 启动时重新创建它另外,在 SessionFactory 关机时放弃架构如果 drop,在 SessionFactory 关机时放弃架构如果 validate,验证数据库架构,而无需更改如果 update,仅导出架构中的缺失内容

jakarta.persistence.create-database-schemas

(可选的)如果 true,自动创建架构和目录

jakarta.persistence.schema-generation.create-source

(可选的)如果 metadata-then-scriptscript-then-metadata,在导出的表格和序列时执行附加的 SQL 脚本

jakarta.persistence.schema-generation.create-script-source

(可选的)要执行的 SQL DDL 脚本的名称

jakarta.persistence.sql-load-script-source

(可选)要执行的 SQL DML 脚本的名称

此特性对于测试极有帮助。

为数据库预先初始化测试或“引用”数据的最简单方法是将一系列 SQL insert 语句放到一个文件中,例如,import.sql,并使用属性 jakarta.persistence.sql-load-script-source 指定此文件的路径。我们刚才已经看过这种方法的 example ,它比编写 Java 代码实例化实体实例并为每个实例调用 persist() 更简洁。

正如我们在 earlier 中提到的,控制架构导出也可以通过编程方式来实现。

SchemaManager API 允许以编程方式控制 schema 导出:

sessionFactory.getSchemaManager().exportMappedObjects(true); sessionFactory.getSchemaManager().exportMappedObjects(true); JPA 具有更有限且更不符合人体工程学的 API:

Persistence.generateSchema("org.hibernate.example", Map.of(JAKARTA_HBM2DDL_DATABASE_ACTION, CREATE)) Persistence.generateSchema("org.hibernate.example", Map.of(JAKARTA_HBM2DDL_DATABASE_ACTION, CREATE))

2.8. Logging the generated SQL

要查看发送到数据库的生成 SQL,你有两个选择。

一种方法是将属性 hibernate.show_sql 设置为 true,Hibernate 将把 SQL 直接记录到控制台。通过启用格式化或高亮显示,可以让输出更具可读性。这些设置在对生成 SQL 语句进行故障排除时非常有帮助。

表 9。记录 SQL 到控制台的设置

Configuration property name

Purpose

hibernate.show_sql

如果指定 true,将直接将 SQL 记录到控制台中

hibernate.format_sql

如果指定 true,将以多行缩进格式记录 SQL

hibernate.highlight_sql

如果指定 true,将通过 ANSI 转义代码将 SQL 与语法高亮一起记录

或者,你可以使用首选的 SLF4J 日志实现,为类别 org.hibernate.SQL 启用调试级别日志记录。

例如,如果您使用 Log4J 2(如 Optional dependencies中所示),请将以下行添加到您的 log4j2.properties 文件中:

# SQL execution
logger.hibernate.name = org.hibernate.SQL
logger.hibernate.level = debug

# JDBC parameter binding
logger.jdbc-bind.name=org.hibernate.orm.jdbc.bind
logger.jdbc-bind.level=trace
# JDBC result set extraction
logger.jdbc-extract.name=org.hibernate.orm.jdbc.extract
logger.jdbc-extract.level=trace

但采用这种方式,我们将错过漂亮的突出显示。

2.9. Minimizing repetitive mapping information

以下属性对于最大程度减少我们将在 Object/relational mapping 中讨论的 @Table@Column 注释中需要明确指定的信息量非常有用:

表 10. 最小化显式映射信息的设置

Configuration property name

Purpose

hibernate.default_schema

没有明确声明的实体的默认模式名称

hibernate.default_catalog

没有明确声明的实体的默认目录名称

hibernate.physical_naming_strategy

一个 PhysicalNamingStrategy,用于实施数据库命名标准

hibernate.implicit_naming_strategy

一个 ImplicitNamingStrategy,用于指定未在注释中指定名称时如何推断关系对象的“逻辑”名称

编写自己的 PhysicalNamingStrategy 和/或 ImplicitNamingStrategy 是一种特别有效的方法,它既可以减少实体类上的注解干扰,又可以实现数据库命名习惯,因此我们认为您应为每个非平凡数据模型执行此操作。我们将在 Naming strategies 中就此展开。

2.10. Nationalized character data in SQL Server

By default, SQL Server 的 charvarchar 类型不适应 Unicode 数据。但是 Java 字符串可能包含任何 Unicode 字符。因此,如果您处理的是 SQL Server,您可能需要强制 Hibernate 使用 ncharnvarchar 列类型。

表 11. 设置使用国际化字符数据

Configuration property name

Purpose

hibernate.use_nationalized_character_data

使用 ncharnvarchar,而不是 charvarchar

另一方面,如果只有 some 列存储国际化数据,则使用 @Nationalized 注释来表示实体的字段,这些字段映射这些列。

或者,您可以将 SQL Server 配置为使用启用了 UTF-8 的校对规则 _UTF8