Hibernate 7.4 新特性
TL;DR · AI 摘要
Hibernate 7.4 引入了改进,优化了分页查询中的 fetch joins,使其避免加载不完整的集合,并添加了支持查询随时间变化的实体状态和历史数据的新功能。
核心要点
- Hibernate 7.4 优化了分页查询中的 fetch joins,使其避免加载不完整的集合。
- 新功能支持查询随时间变化的实体状态和历史数据。
- 内置支持时间历史和审计表,帮助跟踪实体数据的变化。
结构提纲
按章节快速跳转。
- §引言
Hibernate 7.4 的新特性
优化了分页查询中的 fetch joins
支持查询随时间变化的实体状态和历史数据
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- Hibernate 7.4新特性
- Limits and Fetch Joins
- 优化 paginated queries
- History and Audit Tables
- 查询实体状态随时间变化
- 记录历史数据
金句 / Highlights
值得收藏与分享的关键句。
在 Hibernate 7.4 之前,对使用集合 fetch joins 的查询应用分页不能安全地推到数据库。
IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin
Hibernate 7.4 New Features

May 29, 2026
Hibernate 7.4 introduced several improvements that simplify loading a page of data along with their associated child collection, historical data access, and audit logging.
The article will focus on the following features:
- Limits and Fetch Joins: How Hibernate 7.4 improves working with paginated queries that include fetched associations.
- History and Audit Tables: How the new capabilities support querying entity state across time and working with historical data.
You can check out the sample code for this article in this**GitHub repository**.
Limits and Fetch Joins
One common requirement in data-driven applications is loading a page of parent entities along with an associated child entity collection. For example, suppose an application has an Order entity with a Set<OrderItem> collection, and we want to load the first few orders together with their order items.
List<Order> orders = session .createSelectionQuery( "select o from Order o join fetch o.items order by o.id", Order.class ) .setMaxResults(10) .getResultList(); In Hibernate versions before 7.4, applying a limit to a query that used a collection fetch join could not be safely pushed down to the database. Because each Order may have multiple OrderItem rows, limiting the SQL result directly could cut off part of an order’s item collection. To avoid returning incomplete collections, Hibernate loaded all matching rows from the database and applied pagination in memory at the application layer.
That behavior was correct, but it could be expensive. A query intended to load only 10 orders might still read many more rows if the table contained a large number of orders and order items.
Before Hibernate 7.4, the generated SQL would look like the following:
select o1_0.id, i1_0.order_id, i1_0.id, i1_0.product_code, i1_0.quantity, o1_0.order_number, o1_0.status from orders o1_0 join order_items i1_0 on o1_0.id=i1_0.order_id As you can see, the limit(pagination) is not applied at the SQL query level. So, it will load all the orders and their associated order_items, which could be a very expensive operation and may result in _OutOfMemoryException_.
You can see a WARNING logged by Hibernate as follows:
[WARN] HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory One option to prevent Hibernate performing pagination in memory is by setting the following property:
hibernate.query.fail_on_pagination_over_collection_fetch=true By configuring this property, Hibernate throws an exception instead of performing pagination in memory.
Hibernate 7.4 fixes this problem by using nested queries. Instead of applying the limit directly to the joined result set, Hibernate first determines the limited set of parent entity identifiers and then fetches the associated collection for only those parent rows.
This allows pagination to happen in the database while still returning complete items collections for each selected Order.
With Hibernate 7.4, the SQL will be generated as follows:
select o1_0.id, i1_0.order_id, i1_0.id, i1_0.product_code, i1_0.quantity, o1_0.order_number,o1_0.status from (select o1_0.id, o1_0.order_number, o1_0.status from orders o1_0 where exists(select 1 from order_items i1_0 where o1_0.id=i1_0.order_id) offset ? rows fetch first ? rows only) o1_0(id, order_number, status) join order_items i1_0 on o1_0.id=i1_0.order_id This improvement makes fetch joins more practical for paginated screens, such as an order listing page that displays each order with its line items, without forcing the application to load the full result set first.
History and Audit Tables
Hibernate 7.4 adds built-in support for temporal history tables and audit tables. Both features help track changes to entity data, but they serve slightly different use cases: history tables let us query the state of an entity at a point in time, while audit tables record the sequence of changes that happened to an entity.
Consider the following Product entity:
@Entity @Table(name = "products") class Product { //fields id, code, name, price }
History Tables
To enable temporal history for Product, annotate the entity with @Temporal and optionally specify the history table name using @Temporal.HistoryTable.
@Entity @Table(name = "products") @Temporal @Temporal.HistoryTable(name="products_history") class Product { //fields id, code, name, price } With this mapping, Hibernate stores previous versions of product rows in the products_history table. The table includes the entity columns plus two temporal columns: _effective_, which marks when a version became active, and _superseded_, which marks when that version was replaced.
products_history table:
| id | code | name | price | effective | superseded | | --- | --- | --- | --- | --- | --- | | 2251 | P1000 | Product-1000 | 40.00 | 2026-05-15 08:21:39.949001 +00:00 | null | | 2301 | P1001 | Product-1001 | 90.00 | 2026-05-15 08:22:24.765883 +00:00 | 2026-05-15 08:22:24.778067 +00:00 | | 2301 | P1001 | Product-1001 | 100.00 | 2026-05-15 08:22:24.778067 +00:00 | null |
We can get the Product entity data at a given point of time as follows:
Instant someTime = ... try (var session = sessionFactory.withOptions().asOf(someTime).open()) { var product = session.find(Product.class, productId);
} This makes temporal queries feel like normal entity lookups while Hibernate resolves the correct historical row behind the scenes.
Hibernate offers several different strategies(NATIVE, SINGLE_TABLE, HISTORY_TABLE) for mapping temporal entities. For more info check out the Temporal data section.
Audit Tables
Previously, Hibernate-based applications typically used the separate Hibernate Envers library for auditing entity changes. Hibernate 7.4 brings audit table support into Hibernate ORM itself, so applications can use auditing features natively without adding Envers for this use case.
Audit support is enabled by adding @Audited and can be mapped to a custom table using @Audited.Table.
@Entity @Table(name = "products") @Audited @Audited.Table(name="products_aud_log") class Product { //fields id, code, name, price } When auditing is enabled, Hibernate writes one row per change into the audit table. Unlike the history table, the audit table focuses on recording what operation happened and when.
| id | code | name | price | rev | revtype | | --- | --- | --- | --- | --- | --- | | 2001 | P1002 | Product-1002 | 90.00 | 2026-05-13 14:58:17.505775 +00:00 | 0 | | 2001 | P1002 | Product-1002 | 100.00 | 2026-05-13 14:58:17.518194 +00:00 | 1 |
The rev values are the timestamps at which the change happened. The revtype values are represented using ModificationType enum as follows:
public enum ModificationType { /**
- Creation, encoded as 0
*/ ADD, /**
- Modification, encoded as 1
*/ MOD, /**
- Deletion, encoded as 2
*/ DEL } For more info check out the Audit logs section.
Summary
Most of the applications use pagination to show a list of resources, and we used to write custom logic to load paginated data along with the associated child collection. Now this is being handled at the framework level itself. Also, we used to rely on external libraries like Envers to implement auditing, which is now provided by Hibernate itself.
Hibernate 7.4 brings practical improvements that address real problems in JPA/ Hibernate-based applications. Whether we are optimizing pagination query behavior or tracking historical data, Hibernate 7.4 reduces the amount of custom infrastructure needed and provides better support out of the box without requiring additional libraries.
Go ahead and explore these new features using this **GitHub repository**.
[](https://blog.jetbrains.com/idea/2026/05/hibernate-7-4-new-features/#)