前言

Hibernate 是使用 Java 和其他 JVM 语言编写的程序的对象/关系映射 (ORM) 解决方案。

虽然使用 Hibernate 不需要有扎实的 SQL 背景,但对它的概念有一个基本的了解很有用 - 尤其是数据建模的原理。理解事务和工作单元等设计模式的基础知识也很重要。

1. 获取 Hibernate

Hibernate 被拆分为 org.hibernate.orm 组下的多个模块/工件。主体工件被命名为 hibernate-core

本指南使用 6.6.0.Final 作为演示的 Hibernate 版本。如果有需要,确保将此版本更改为您要使用的版本。

我们可以使用 Gradle

dependencies {
  implementation "org.hibernate.orm:hibernate-core:6.6.0.Final"
}

Maven

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.6.0.Final</version>
</dependency>

1.1. Hibernate ORM 模块

声明对该工件的依赖关系,如前所述,Hibernate ORM 被拆分为多个模块,目的是根据正在使用的功能或未使用功能来隔离传递性依赖项。

表 1. 面向 API 的模块

hibernate-core

核心对象/关系映射引擎

hibernate-envers

实体版本控制和审计

hibernate-spatial

使用 GeoLatte 支持空间/GIS 数据类型

hibernate-jpamodelgen

生成 JPA 兼容元模型的注释处理器,以及可选的 Hibernate 附加功能

hibernate-vector

支持数学向量类型和函数,这些对于 AI/ML 主题(例如向量相似性搜索和检索增强生成 (RAG))很有用

表 2. 面向集成的模块

hibernate-agroal

支持 Agroal 连接池

hibernate-c3p0

支持 C3P0 连接池

hibernate-hikaricp

支持 HikariCP 连接池

hibernate-vibur

支持 Vibur DBCP 连接池

hibernate-proxool

支持 Proxool 连接池

hibernate-ucp

支持 Universal Connection Pool 连接池

hibernate-jcache

JCache 的集成,允许将任何兼容的实现用作二级缓存提供程序

hibernate-graalvm

实验性扩展,使其可以更轻松地将应用程序编译为 GraalVM 原生映像

hibernate-micrometer

Micrometer 指标集成

hibernate-community-dialects

社区支持的其他 SQL 方言

表 3. 面向测试的模块

hibernate-testing

用于测试 Hibernate ORM 功能的一系列 JUnit 扩展

1.2. 平台/BOM

Hibernate 还提供了一个平台(Maven 术语中的 BOM)模块,可用于将 Hibernate 模块的版本与其实现的版本保持一致。平台制品命名为 hibernate-platform

在 Gradle 中应用该平台

dependencies {
  implementation platform "org.hibernate.orm:hibernate-platform:6.6.0.Final"

  // use the versions from the platform
  implementation "org.hibernate.orm:hibernate-core"
  implementation "jakarta.transaction:jakarta.transaction-api"
}

请参阅 Gradle 文档,以了解应用平台的功能。

在 Maven 中应用平台 (BOM)

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
    <groupId>jakarta.transaction</groupId>
    <artifactId>jakarta.transaction-api</artifactId>
</dependency>

<dependencyManagement>
  <dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-platform</artifactId>
    <version>6.6.0.Final</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencyManagement>

1.3. 示例源代码

本教程中提到的捆绑示例可从 此处 下载。

或者,还可以从 Github 获取示例源代码

2. 使用原生 Hibernate API 的教程

目标
  • 使用 hibernate.properties 配置 Hibernate

  • 使用 本机引导 创建 SessionFactory

  • 使用注解提供映射信息

  • 使用 Session 持久化和查询数据

本教程位于下载包中的 annotations/ 下。

2.1. 通过属性文件进行配置

在此示例中,配置属性指定在名为 hibernate.properties 的文件中。

通过 hibernate.properties 配置
# Database connection settings
hibernate.connection.url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1
hibernate.connection.username=sa
hibernate.connection.password=

# Echo all executed SQL to console
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.highlight_sql=true

# Automatically export the schema
hibernate.hbm2ddl.auto=create

以下属性指定 JDBC 连接信息

表 4. JDBC 连接设置
配置属性名称 目的

jakarta.persistence.jdbc.url

数据库的 JDBC URL

jakarta.persistence.jdbc.userjakarta.persistence.jdbc.password

数据库凭据

这些教程使用 H2 嵌入式数据库,因此这些属性的值特定于在内存模式下运行 H2。

这些属性使在执行时以美观一致的格式将 SQL 记录到控制台

表 5. 控制台 SQL 记录设置
配置属性名称 目的

hibernate.show_sql

如果为 true,则直接将 SQL 记录到控制台

hibernate.format_sql

如果为 true,则以多行缩进格式记录 SQL

hibernate.highlight_sql

如果为 true,则通过 ANSI 转义码对 SQL 以语法高亮方式记录

在使用 Hibernate 开发持久性逻辑时,了解确切执行了哪些 SQL 非常重要。

2.2. 带注解的实体 Java 类

本教程的实体类是org.hibernate.tutorial.annotations.Event。请注意,

  • 该类对 property getter 和 setter 方法采用标准 JavaBean 命名约定,并对字段采用私有可见性。这是推荐的,但不是必需的。

  • 无参数构造函数(也是一个 JavaBean 约定)对于所有持久化类来说是必需的。Hibernate 需要使用 Java 反射为你实例化对象。构造函数应该具有包私有或public可见性,以允许 Hibernate 为字段访问生成代理和优化代码。

用户指南的实体类型部分介绍了对实体类的完整需求集。

我们使用注释来将该类标识为实体,并将其映射到关系模式。

将类标识为实体
@Entity   (1)
@Table(name = "Events")   (2)
public class Event {
    ...
}
1 @jakarta.persistence.EntityEvent类标记为实体。
2 @jakarta.persistence.Table显式指定映射表的名称。如果没有此注释,表名称将默认为Event

每个实体类都必须有一个标识符。

标识符属性的标识
@Id   (1)
@GeneratedValue   (2)
private Long id;
1 @jakarta.persistence.Id将该字段标记为实体的标识符(主键)。
2 @jakarta.persistence.GeneratedValue指定这是一个合成 id,即系统生成的标识符(代理主键)。

实体的其他字段默认为持久。

映射基本属性
private String title;

@Column(name = "eventDate")   (1)
private LocalDateTime date;
1 @jakarta.persistence.Column显式指定映射列的名称。如果没有此注释,列名将默认为date,这是某些数据库中的关键字。

2.3. 示例代码

org.hibernate.tutorial.annotations.HibernateIllustrationTest说明了使用 Hibernate 的本机 API,包括

  • SessionSessionFactory,以及

  • 用于配置和引导的 org.hibernate.boot

有几种不同的方式来配置和启动 Hibernate,而这甚至不是最常见的方法。

本教程中的示例以 JUnit 测试的形式呈现。这种方法的好处是 setUp()tearDown()大致说明了当程序启动时如何创建org.hibernate.SessionFactory,以及当程序终止时如何关闭 org.hibernate.SessionFactory
获取 SessionFactory
protected void setUp() {
    // A SessionFactory is set up once for an application!
    final StandardServiceRegistry registry =
            new StandardServiceRegistryBuilder()
                    .build();    (1) (2)
    try {
        sessionFactory =
                new MetadataSources(registry)             (3)
                        .addAnnotatedClass(Event.class)   (4)
                        .buildMetadata()                  (5)
                        .buildSessionFactory();           (6)
    }
    catch (Exception e) {
        // The registry would be destroyed by the SessionFactory, but we
        // had trouble building the SessionFactory so destroy it manually.
        StandardServiceRegistryBuilder.destroy(registry);
    }
}
1 setUp()方法首先构建一个 StandardServiceRegistry实例,它将配置信息纳入 org.hibernate.SessionFactory可以使用的一组正在运行的 Services中。
2 在这里,我们将所有配置信息都放入hibernate.properties,因此没有什么值得关注的。
3 使用 StandardServiceRegistry,我们创建MetadataSources,它使我们能够向 Hibernate 说明我们的 domain 模型。
4 在这里,我们只有一个要注册的实体类。
5 Metadata 的实例代表对应用程序领域模型完全且经过部分验证的视图。
6 引导过程的最后一步是为已配置服务和经过验证的领域模型构建一个 SessionFactorySessionFactory 是一个线程安全对象,实例化一次即可为整个应用程序服务。

SessionFactory 生成 Session 的实例。每个会话应被视为代表一个工作单元

持久性实体
sessionFactory.inTransaction(session -> {   (1)
    session.persist(new Event("Our very first event!", now()));   (2)
    session.persist(new Event("A follow up event", now()));
});
1 inTransaction() 方法创建一个会话并开始一个新事务。
2 这里我们创建两个新的 Event 对象并将它们传递给 Hibernate,调用 persist() 方法使这些实例持久化。Hibernate 负责为每个 Event 执行一个 INSERT 语句。
获取 entities 列表
sessionFactory.inTransaction(session -> {
    session.createSelectionQuery("from Event", Event.class)   (1)
            .getResultList()   (2)
            .forEach(event -> out.println("Event (" + event.getDate() + ") : " + event.getTitle()));
});
1 这里我们使用一个非常简单的Hibernate Query Language (HQL) 语句从数据库中加载所有现有的 Event 对象。
2 Hibernate 生成并执行适当的 SELECT 语句,然后实例化并使用查询结果集中的数据填充 Event 对象。

2.4. 更进一步!

练习练习
  • 实际运行此示例以查看控制台中显示的 Hibernate 执行的 SQL。

  • 重新配置示例以连接到您自己的持久性关系数据库。

  • 添加一个与 Event 实体的关联,以对消息线程建模。

3. 使用 JPA 标准 API 的教程

目标
  • 使用 persistence.xml 配置 Hibernate

  • 引导 Jakarta Persistence EntityManagerFactory

  • 使用注解提供映射信息

  • 使用 EntityManager 来持久化和查询数据

本教程位于 entitymanager/ 下的下载包内。

3.1. persistence.xml

JPA 定义了一个不同的引导过程,以及一个名为 persistence.xml 的标准配置文件格式。在 Java™ SE 环境中,持久性提供程序(Hibernate)需要在类路径的 META-INF/persistence.xml 路径中找到每个 JPA 配置文件。

通过 persistence.xml 配置
<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 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="org.hibernate.tutorial.jpa">   (1)
        <description>
            Persistence unit for the Jakarta Persistence tutorial of the Hibernate Getting Started Guide
        </description>

        <class>org.hibernate.tutorial.em.Event</class>     (2)

        <properties>    (3)
            <!-- Database connection settings -->
            <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1" />
            <property name="jakarta.persistence.jdbc.user" value="sa" />
            <property name="jakarta.persistence.jdbc.password" value="" />

            <!-- Automatically export the schema -->
            <property name="jakarta.persistence.schema-generation.database.action" value="create" />

            <!-- Echo all executed SQL to console -->
            <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>
1 persistence.xml 文件应为它声明的每个持久性单元提供唯一名称。应用程序在获取 EntityManagerFactory 时使用此名称来引用配置,稍后我们将看到此名称。
2 <class/> 元素注册我们的注释实体类。
3 作为 <properties/> 元素指定的设置已在 通过属性文件进行配置 中进行了讨论。在此,尽可能使用 JPA 标准属性名称。

前缀带有旧版 Java EE 命名空间 javax.persistence 的配置属性仍能得到识别,但建议使用 Jakarta EE 命名空间 jakarta.persistence

3.2. 带注解的实体 Java 类

实体类与带有注释的实体 Java 类中的实体类完全相同。

3.3. 示例代码

以前的教程使用 Hibernate 原生 API。本教程使用标准 Jakarta Persistence API。

获取 JPA EntityManagerFactory
protected void setUp() {
    entityManagerFactory = Persistence.createEntityManagerFactory("org.hibernate.tutorial.jpa");   (1)
}
1 再次注意,持久单元名称为 org.hibernate.tutorial.jpa,它与我们的persistence.xml中的名称相匹配。

持久化和查询实体的代码与持久化实体的代码几乎相同。很不幸的是,EntityManagerFactory 没有像 SessionFactory 一样的好的 inTransaction() 方法,所以我们必须自己编写

在 JPA 中管理事务
void inTransaction(Consumer<EntityManager> work) {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction transaction = entityManager.getTransaction();
    try {
        transaction.begin();
        work.accept(entityManager);
        transaction.commit();
    }
    catch (Exception e) {
        if (transaction.isActive()) {
            transaction.rollback();
        }
        throw e;
    }
    finally {
        entityManager.close();
    }
}
如果你在 Java SE 中使用 JPA,则需要将此函数复制/粘贴到你的项目中。或者,你可以将 EntityManagerFactory 展开为 SessionFactory

3.4. 更进一步!

练习练习
  • 了解如何使用 CDI 在 Quarkus 中注入一个容器管理的 EntityManager。请参阅 Quarkus Website 以获取说明。

4. 使用 Envers 的教程

目标
  • 将一个实体标注为历史记录

  • 配置 Envers

  • 使用 Envers API 查看和分析历史数据

本教程位于下载包中的 envers/

4.1. persistence.xml

此文件未经之前的更改。

4.2. 带注解的实体 Java 类

此实体类也与我们之前几乎相同。最大的不同之处是添加了注释 @org.hibernate.envers.Audited,它告知 Envers 自动追踪对此实体的更改。

4.3. 示例代码

此代码保存了某些实体,对其中一个实体进行更改,然后使用 Envers API 提取初始修订以及更新的修订。修订指的是实体的历史记录快照。

使用 org.hibernate.envers.AuditReader
public void testBasicUsage() {
    ...
    AuditReader reader = AuditReaderFactory.get( entityManager );   (1)
    Event firstRevision = reader.find( Event.class, 2L, 1 );        (2)
    ...
    Event secondRevision = reader.find( Event.class, 2L, 2 );       (3)
    ...
}
1 从包装了 JPA EntityManagerorg.hibernate.envers.AuditReaderFactory 中获取 org.hibernate.envers.AuditReader
2 find 方法会检索实体的特定修订。第一次调用会检索 id 为 2 的 Event 的修订号 1。
3 稍后,第二次调用会请求 id 为 2 的 Event 的修订号 2。

4.4. 更进一步!

练习练习
  • 提供一个自定义修订实体来额外捕获是谁做出了更改。

  • 编写一个查询来仅检索满足某些标准的历史数据。参阅用户指南以查看 Envers 查询如何构建。

  • 尝试审计具有各种形式的关系(多对一、多对多等)的实体。尝试检索此类实体的历史版本(修订)并导航对象树。

5. 致谢

有关 Hibernate ORM 贡献者的完整列表,请访问 GitHub 仓库

以下贡献者参与了本篇文档的编写

  • Steve Ebersole