Hibernate StatelessSession Upsert
Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
Introduction
In this article, we are going to see how the Hibernate StatelessSession Upsert method works.
As I explained in this article, the UPSERT operation allows you to INSERT a record if there is no such record matching the filtering criteria or to UPDATE it in case the record exists.
While many relational database systems offer the standard MERGE command to execute the UPSERT command, the syntax may differ slightly from one database to the other. Therefore, it’s great that Hibernate provides a portable way to achieve this goal.
Domain Model
Let’s assume we are using the following Book entity:
@Entity
@Table(name = "book")
public class Book {
⠀
@Id
private Long id;
⠀
private String title;
⠀
@NaturalId
private String isbn;
⠀
public Long getId() {
return id;
}
⠀
public Book setId(Long id) {
this.id = id;
return this;
}
⠀
public String getTitle() {
return title;
}
⠀
public Book setTitle(String title) {
this.title = title;
return this;
}
⠀
public String getIsbn() {
return isbn;
}
⠀
public Book setIsbn(String isbn) {
this.isbn = isbn;
return this;
}
}
Hibernate StatelessSession Upsert
Since version 6.3, the Hibernate StatelessSession utility provides an Upsert method we can use to either INSERT or UPDATE a JPA entity.
Executing the Upsert on PostgreSQL
When executing the following test case on PostreSQL:
doInStatelessHibernate(session -> {
session.upsert(
new Book()
.setId(1L)
.setTitle("High-Performance Hibernate")
.setIsbn("978-9730228236")
);
⠀
session.upsert(
new Book()
.setId(1L)
.setTitle("High-Performance Hibernate 2nd edition")
.setIsbn("978-9730228236")
);
});
Hibernate generates the following SQL statements:
Query:["
MERGE INTO
book AS t
USING (
SELECT
CAST(? as bigint) id,
CAST(? as text) title
) AS s
ON (t.id = s.id)
WHEN NOT MATCHED THEN
INSERT (id, title) VALUES (s.id, s.title)
WHEN MATCHED THEN
UPDATE SET title = s.title
"],
Params:[(1, High-Performance Hibernate)]
Query:["
MERGE INTO
book AS t
USING (
SELECT
CAST(? as bigint) id,
CAST(? as text) title
) AS s
ON (t.id = s.id)
WHEN NOT MATCHED THEN
INSERT (id, title) VALUES (s.id, s.title)
WHEN MATCHED THEN
UPDATE SET title = s.title
"],
Params:[(1, High-Performance Hibernate 2nd edition)]
Executing the Upsert on Oracle
When executing the same test case on Oracle, Hibernate generates the following SQL statements:
Query:["
MERGE INTO
book t
USING (
SELECT
CAST(? as number(19,0)) id,
CAST(? as varchar2(4000 char)) title
FROM
dual
) s ON (t.id=s.id)
WHEN NOT MATCHED THEN
INSERT (id, title) VALUES (s.id, s.title)
WHEN MATCHED THEN
UPDATE SET t.title = s.title
"],
Params:[(1, High-Performance Hibernate)]
Query:["
MERGE INTO
book t
USING (
SELECT
CAST(? as number(19,0)) id,
CAST(? as varchar2(4000 char)) title
FROM
dual
) s ON (t.id=s.id)
WHEN NOT MATCHED THEN
INSERT (id, title) VALUES (s.id, s.title)
WHEN MATCHED THEN
UPDATE SET t.title = s.title
"],
Params:[(1, High-Performance Hibernate 2nd edition)]
Notice the FROM dual part that’s specific to Oracle.
Executing the Upsert on SQL Server
When executing the test case on SQL Server, Hibernate generates the MERGE SQL statement as well:
Query:["
MERGE INTO
book AS t USING (
SELECT
CAST(? as bigint) id,
CAST(? as varchar(max)) title
) AS s ON (t.id=s.id)
WHEN NOT MATCHED THEN
INSERT (id, title) VALUES (s.id, s.title)
WHEN MATCHED THEN
UPDATE SET title = s.title;
"],
Params:[(1, High-Performance Hibernate)]
Query:["
MERGE INTO
book AS t USING (
SELECT
CAST(? as bigint) id,
CAST(? as varchar(max)) title
) AS s ON (t.id=s.id)
WHEN NOT MATCHED THEN
INSERT (id, title) VALUES (s.id, s.title)
WHEN MATCHED THEN
UPDATE SET title = s.title;"],
Params:[(1, High-Performance Hibernate 2nd edition)]
Since SQL Server supports the MERGE statement, Hibernate uses just like on PostgreSQL and Oracle.
Executing the Upsert on MySQL
When executing the same test case on MySQL, Hibernate generates a pair of UPDATE and INSERT SQL statements:
Query:["
UPDATE
book
SET
title = ?
WHERE
id = ?
"],
Params:[(High-Performance Hibernate, 1)]
Query:["
INSERT INTO
book (title,id)
VALUES
(?,?)
"],
Params:[(High-Performance Hibernate, 1)]
Query:["
UPDATE
book
SET
title = ?
WHERE
id = ?
"],
Params:[(High-Performance Hibernate 2nd edition, 1)]
In the case of MySQL, since the MERGE is not supported, the upsert method will simply render an UPDATE followed by an INSERT instead of using the ON DUPLICATE KEY clause.
The reason why the second UPDATE is not followed by an INSERT is because the UPDATE call returned the updateCount value of 1, so Hibernate knows that the UPDATE went through.
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
The Hibernate StatelessSession Upsert method can use the MERGE SQL command if the database supports it, like it’s the case with PostgreSQL, Oracle, or SQL Server.





