Hibernate ORM 中文操作指南

2. Configuration and bootstrap

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

We would love to make this section short. Unfortunately, there’s several distinct ways to configure and bootstrap Hibernate, and we’re going to have to describe at least two of them in detail.

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

The four basic ways to obtain an instance of Hibernate are shown in the following table:

Using the standard JPA-defined XML, and the operation Persistence.createEntityManagerFactory()

Usually chosen when portability between JPA implementations is important.

Using the Configuration class to construct a SessionFactory

When portability between JPA implementations is not important, this option is quicker, adds some flexibility and saves a typecast.

Using the more complex APIs defined in org.hibernate.boot

Used primarily by framework integrators, this option is outside the scope of this document.

By letting the container take care of the bootstrap process and of injecting the SessionFactory or EntityManagerFactory

Used in a container environment like WildFly or Quarkus.

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

Here we’ll focus on the first two options.

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

If you’re using Hibernate outside of a container environment, you’ll need to:

  1. include Hibernate ORM itself, along with the appropriate JDBC driver, as dependencies of your project, and

  2. configure Hibernate with information about your database, by specifying configuration properties.

2.1. Including Hibernate in your project build

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

First, add the following dependency to your project:

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

Where {version} is the version of Hibernate you’re using.

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

You’ll also need to add a dependency for the JDBC driver for your database.

表 2. JDBC 驱动程序依赖项

Table 2. JDBC driver dependencies

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 驱动程序的最新版本。

Where {version} is the latest version of the JDBC driver for your databse.

2.2. Optional dependencies

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

Optionally, you might also add any of the following additional features:

表 3. 可选依赖项

Table 3. Optional dependencies

Optional feature

Dependencies

An SLF4J logging implementation

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

A JDBC connection pool, for example, Agroal

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

The Hibernate Metamodel Generator, especially if you’re using the JPA criteria query API

org.hibernate.orm:hibernate-processor

The Query Validator, for compile-time checking of HQL

org.hibernate:query-validator

Hibernate Validator, an implementation of Bean Validation

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

Local second-level cache support via JCache and EHCache

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

Local second-level cache support via JCache and Caffeine

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

Distributed second-level cache support via Infinispan

org.infinispan:infinispan-hibernate-cache-v60

A JSON serialization library for working with JSON datatypes, for example, Jackson or Yasson

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

Hibernate Spatial

org.hibernate.orm:hibernate-spatial

Envers, for auditing historical data

org.hibernate.orm:hibernate-envers

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

You might also add the Hibernate bytecode enhancer to your Gradle build if you want to use field-level lazy fetching.

2.3. Configuration using JPA XML

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

Sticking to the JPA-standard approach, we would provide a file named persistence.xml, which we usually place in the META-INF directory of a persistence archive, that is, of the .jar file or directory which contains our entity classes.

<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,即:

The <persistence-unit> element defines a named persistence unit, that is:

  1. a collection of associated entity types, along with

  2. a set of default configuration settings, which may be augmented or overridden at runtime.

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

Each <class> element specifies the fully-qualified name of an entity class.

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

Each <property> element specifies a configuration property and its value. Note that:

  1. the configuration properties in the jakarta.persistence namespace are standard properties defined by the JPA spec, and

  2. properties in the hibernate namespace are specific to Hibernate.

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

We may obtain an EntityManagerFactory by calling Persistence.createEntityManagerFactory():

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

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

If necessary, we may override configuration properties specified in 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 实例。

Alternatively, the venerable class Configuration allows an instance of Hibernate to be configured in Java code.

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 不支持的选项。

The Configuration class has survived almost unchanged since the very earliest (pre-1.0) versions of Hibernate, and so it doesn’t look particularly modern. On the other hand, it’s very easy to use, and exposes some options that persistence.xml doesn’t support.

2.5. Configuration using Hibernate properties file

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

If we’re using the Hibernate Configuration API, but we don’t want to put certain configuration properties directly in the Java code, we can specify them in a file named hibernate.properties, and place the file in the root classpath.

# 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 所理解的所有配置属性。

The class AvailableSettings enumerates all the configuration properties understood by Hibernate.

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

Of course, we’re not going to cover every useful configuration setting in this chapter. Instead, we’ll mention the ones you need to get started, and come back to some other important settings later, especially when we talk about performance tuning.

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

Hibernate has many—too many—switches and toggles. Please don’t go crazy messing about with these settings; most of them are rarely needed, and many only exist to provide backward compatibility with older versions of Hibernate. With rare exception, the default behavior of every one of these settings was carefully chosen to be the behavior we recommend.

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

The properties you really do need to get started are these three:

表 4。JDBC 连接设置

Table 4. JDBC connection settings

Configuration property name

Purpose

jakarta.persistence.jdbc.url

JDBC URL of your database

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

Your database credentials

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

In Hibernate 6, you don’t need to specify hibernate.dialect. The correct Hibernate SQL Dialect will be determined for you automatically. The only reason to specify this property is if you’re using a custom user-written Dialect class.

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

Similarly, neither hibernate.connection.driver_class nor jakarta.persistence.jdbc.driver is needed when working with one of the supported databases.

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

In some environments it’s useful to be able to start Hibernate without accessing the database. In this case, we must explicitly specify not only the database platform, but also the version of the database, using the standard JPA configuration properties.

# 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

The product name is the value returned by java.sql.DatabaseMetaData.getDatabaseProductName(), for example, PostgreSQL, MySQL H2, Oracle, EnterpriseDB, MariaDB, or Microsoft SQL Server.

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

Table 5. Settings needed when database is inaccessible at startup

Configuration property name

Purpose

hibernate.boot.allow_jdbc_metadata_access

Set to false to disallow access to the database at startup

jakarta.persistence.database-product-name

The database product name, according to the JDBC driver

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

The major and minor versions of the database

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

Pooling JDBC connections is an extremely important performance optimization. You can set the size of Hibernate’s built-in connection pool using this property:

表 6。内置连接池大小

Table 6. Built-in connection pool size

Configuration property name

Purpose

hibernate.connection.pool_size

The size of the built-in connection pool

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

By default, Hibernate uses a simplistic built-in connection pool. This pool is not meant for use in production, and later, when we discuss performance, we’ll see how to select a more robust implementation.

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

Alternatively, in a container environment, you’ll need at least one of these properties:

表 7。事务管理设置

Table 7. Transaction management settings

Configuration property name

Purpose

jakarta.persistence.transactionType

(Optional, defaults to JTA) Determines if transaction management is via JTA or resource-local transactions. Specify RESOURCE_LOCAL if JTA should not be used.

jakarta.persistence.jtaDataSource

JNDI name of a JTA datasource

jakarta.persistence.nonJtaDataSource

JNDI name of a non-JTA datasource

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

In this case, Hibernate obtains pooled JDBC database connections from a container-managed DataSource.

2.7. Automatic schema export

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

You can have Hibernate infer your database schema from the mapping annotations you’ve specified in your Java code, and export the schema at initialization time by specifying one or more of the following configuration properties:

表 8。架构管理设置

Table 8. Schema management settings

Configuration property name

Purpose

jakarta.persistence.schema-generation.database.action

If drop-and-create, first drop the schema and then export tables, sequences, and constraintsIf create, export tables, sequences, and constraints, without attempting to drop them firstIf create-drop, drop the schema and recreate it on SessionFactory startup Additionally, drop the schema on SessionFactory shutdownIf drop, drop the schema on SessionFactory shutdownIf validate, validate the database schema without changing itIf update, only export what’s missing in the schema

jakarta.persistence.create-database-schemas

(Optional) If true, automatically create schemas and catalogs

jakarta.persistence.schema-generation.create-source

(Optional) If metadata-then-script or script-then-metadata, execute an additional SQL script when exported tables and sequences

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

(Optional) The name of a SQL DDL script to be executed

jakarta.persistence.sql-load-script-source

(Optional) The name of a SQL DML script to be executed

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

This feature is extremely useful for testing.

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

The easiest way to pre-initialize a database with test or "reference" data is to place a list of SQL insert statements in a file named, for example, import.sql, and specify the path to this file using the property jakarta.persistence.sql-load-script-source. We’ve already seen an example of this approach, which is cleaner than writing Java code to instantiate entity instances and calling persist() on each of them.

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

As we mentioned earlier, it can also be useful to control schema export programmatically.

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

The SchemaManager API allows programmatic control over schema export:

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

sessionFactory.getSchemaManager().exportMappedObjects(true); sessionFactory.getSchemaManager().exportMappedObjects(true); JPA has a more limited and less ergonomic 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))

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,你有两个选择。

To see the generated SQL as it’s sent to the database, you have two options.

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

One way is to set the property hibernate.show_sql to true, and Hibernate will log SQL direct to the console. You can make the output much more readable by enabling formatting or highlighting. These settings really help when troubleshooting the generated SQL statements.

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

Table 9. Settings for SQL logging to the console

Configuration property name

Purpose

hibernate.show_sql

If true, log SQL directly to the console

hibernate.format_sql

If true, log SQL in a multiline, indented format

hibernate.highlight_sql

If true, log SQL with syntax highlighting via ANSI escape codes

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

Alternatively, you can enable debug-level logging for the category org.hibernate.SQL using your preferred SLF4J logging implementation.

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

For example, if you’re using Log4J 2 (as above in Optional dependencies), add these lines to your log4j2.properties file:

# 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

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

But with this approach we miss out on the pretty highlighting.

2.9. Minimizing repetitive mapping information

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

The following properties are very useful for minimizing the amount of information you’ll need to explicitly specify in @Table and @Column annotations, which we’ll discuss below in Object/relational mapping:

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

Table 10. Settings for minimizing explicit mapping information

Configuration property name

Purpose

hibernate.default_schema

A default schema name for entities which do not explicitly declare one

hibernate.default_catalog

A default catalog name for entities which do not explicitly declare one

hibernate.physical_naming_strategy

A PhysicalNamingStrategy implementing your database naming standards

hibernate.implicit_naming_strategy

An ImplicitNamingStrategy which specifies how "logical" names of relational objects should be inferred when no name is specified in annotations

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

Writing your own PhysicalNamingStrategy and/or ImplicitNamingStrategy is an especially good way to reduce the clutter of annotations on your entity classes, and to implement your database naming conventions, and so we think you should do it for any nontrivial data model. We’ll have more to say about them in Naming strategies.

2.10. Nationalized character data in SQL Server

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

By default, SQL Server’s char and varchar types don’t accommodate Unicode data. But a Java string may contain any Unicode character. So, if you’re working with SQL Server, you might need to force Hibernate to use the nchar and nvarchar column types.

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

Table 11. Setting the use of nationalized character data

Configuration property name

Purpose

hibernate.use_nationalized_character_data

Use nchar and nvarchar instead of char and varchar

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

On the other hand, if only some columns store nationalized data, use the @Nationalized annotation to indicate fields of your entities which map these columns.

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

Alternatively, you can configure SQL Server to use the UTF-8 enabled collation _UTF8.