Hibernate ORM version 6.5
already received a couple candidate release versions, and a final release will follow shortly. This post highlights one of the improvements that comes with this version: efficient retrieval of non-identifier database generated values.
What happened before
Hibernate ORM already supported retrieving GenerationType.IDENTITY
within the executed insert
statement through the SQL RETURNING
clause for mutation statements (see for example this page of PostgreSQL’s documentation) or through JDBC’s Statement#getGeneratedKeys()
API, for databases that support either of these features.
An entity mapping, though, could have multiple on-execution generated properties (i.e. columns that are populated on the database side during or after mutation statement execution) that are not part of the primary key. Furthermore, dynamic value generation is not restricted to insert operations but could also take place when updating managed entity instances. Your mapping’s values might be relying on the execution of database functions, or columns dynamically updated on statement execution through triggers.
Hibernate needs to read these values when storing entities in its persistence context (aka the first-level cache), to ensure consistency with the state on the database. Until now, a subsequent select
query would run after each insert
or update
statement was emitted for mappings that included on-execution generated properties to retrieve their values and populate the corresponding entity instance accordingly.
Take, for example, this simple mapping:
@Entity
class TestEntity {
@Id
Long id;
@Generated( event = EventType.INSERT )
String name;
@UpdateTimestamp( source = SourceType.DB )
Date updateDate;
}
Here, the name
property might be generated on insert by a database trigger, and updateDate
could for example be populated through the localtimestamp
function when running the statement. When persisting an entity of this type, Hibernate will need to run 2 statements:
-- insert the row on the database
insert
into
TestEntity
(id, updateDate)
values
(?, localtimestamp)
-- select back the generated values
select
name,
updateDate
from
TestEntity
What’s new
With Hibernate 6.5, arbitrary database-generated values will also be retrieved within the execution of insert
and update
statements efficiently, i.e. through the insert … returning
syntax or getGeneratedKeys()
API that was already leveraged for IDENTITY
. This effectively halves the number of round-trips to the database necessary when inserting or updating managed entities containing generated values, replacing the need of running an additional select
query after the mutation was applied.
This improvement is completely transparent from the user’s perspective: you will not need to change any of your entity mappings or configure additional properties; Hibernate will automatically take care of generated values for you more efficiently from now on. |
For example, an insert statement created for the previous example on a database supporting the insert … returning
syntax would now look something like this:
-- insert the row on the database and retrieve the values in one go
insert
into
TestEntity
(id, updateDate)
values
(?, localtimestamp)
returning name, updateDate
Performance of mutation operations concerning mappings that have on-execution generated values should be greatly improved. Remember that Hibernate’s statement batching feature cannot be used in combination with generated values, so previously 2 different statements had to be emitted for each mutated row (the mutation itself and the subsequent select).
We leveraged this new ability to retrieve arbitrary properties from the database when executing mutation statements to also retrieve rowid-like values efficiently when requested through the @RowId
annotation, to then utilize them as usual for CRUD operations.
This new feature might not work on all databases, especially for older versions. The Hibernate team has tested and identified how each of the officially supported Dialects can take advantage of the new functionality, most modern databases have support some form of efficient generated-value retrieval. |
If you want to find our more about this feel free to refer to the original Improvement Proposal you can find on Hibernate’s Jira.
Outlook
Thanks to this improvement we now have the ability to return arbitrary on-execution generated values for both insert
and update
mutation statements. This potentially opens the door for a user-facing feature, allowing for example a custom HQL mutation query syntax through which one can specify a custom list of properties to return as a result of running the update.
If you want to let us know what you think of this new feature or if you have any questions about it please reach us through the usual channels.