Skip to content

Hibernate ​

Hibernate is a popular implementation (provider) of the JPA (Java Persistence API) specification.

JPA is only the interface.
Hibernate is one of the tools that implements that interface

Further information

Link: Hibernate

Non JPA Annotations ​

Some additional useful annotations for Hibernate

Formula ​

Let's define a computed property on an entity, based on a SQL expression. It’s a read-only virtual column whose value is computed when the entity is fetched from the database. The formula SQL will be included in the main query. So no additional query call.

  • Read-only: You can’t write to a @Formula field — it’s computed on the fly.
  • It executes in SQL, not in Java — so you can use DB functions, subqueries, CASE statements, etc.
  • Can be used for custom calculations, aggregations, or even pulling data from other tables.

Cons:

  • Because it's SQL-based, it can reduce portability across different databases.
  • It’s not lazy — the formula is always evaluated with the entity’s main query.
  • It doesn’t support parameters — so no dynamic content.
java
@Entity
public class Person {
...

    @Formula("CONCAT(first_name, ' ', last_name)")
    private String fullName;

}

Where ​

It is used to add a SQL clause to all queries involving an entity or a collection. It's especially helpful in things like soft deletes or filtering related entities.

java
@Entity
@Where(clause = "deleted = false")
public class Author {

    @Id
    private Long id;

    private String name;
    ...

}

Every time Hibernate runs, a query on Author WHERE delted = falsewill be added to the SQL. Like a global filter.

SQLDelete / SQLInsert ​

It allows you to override the default DELETE / INSERT SQL command used by Hibernate when you delete/adding an entity. It’s part of Hibernate-specific extensions (not standard JPA).

Soft Deletes
Instead of actually removing a row from the database, you might just want to mark it as deleted by setting a column like deleted = true.

java
@Entity
@SQLDelete(sql = "UPDATE user SET deleted = true WHERE id = ?")
@Where(clause = "deleted = false")
public class User {
  ...
}

Cache ​

Tells Hibernate which entities or associations to store in the second-level cache and how.

JPA (and Hibernate) has two levels of cache:

  • First-level cache: Built-in, per EntityManager session. Always enabled.
  • Second-level cache: Optional, shared across sessions, and can significantly improve performance by avoiding database hits for frequently accessed entities.
java
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {
  ...
}
  • It caches the entity.
  • Updates to it also update the cache to keep things in sync.
StrategyDescription
READ_ONLYBest for data that never changes. Fastest.
NONSTRICT_READ_WRITEAllows stale data; no strict guarantees, good for mostly-read data.
READ_WRITEEnsures cache is in sync with DB. Slightly slower, but safe.
TRANSACTIONALIntegrates with JTA transactions.

DynamicUpdate ​

It optimizes SQL UPDATE statements by generating them dynamically at runtime, ignoring all not modified columns. Normally, if only one property changes, JPA created an update with all values.

Pros ​

  • Performance boost.
  • Reduces unnecessary DB writes.
  • Helps in avoiding overwrite issues with DB triggers or audit fields.

Cons ​

  • Slight overhead: Hibernate must track dirty fields more carefully.
  • Only works with Hibernate (not portable JPA).
  • Doesn’t help with @Version fields in optimistic locking (those still get updated).

SelectBeforeUpdate ​

Issue a SELECT before doing an UPDATE to check whether the entity has actually changed.

Can be used with @DynamicUpdate

Pros ​

  • Prevents unnecessary updates
  • Useful with complex dirty checking
  • Avoids overwriting unchanged data

Cons ​

  • Adds extra SELECT overhead
  • Slower for large update batches
  • Not needed in most basic CRUD scenarios

Query Hints ​

Query hints are instructions to give the JPA provider information how to execute a query. This information will make some adjustments, which might improve the performance or memory usage.

ReadOnly ​

Using org.hibernate.readonly tells hibernate that the data will only be read. So there is no need for dirty checking. This will increase the speed of the query

FetchSize ​

org.hibernate.fetchSize Tells hibernate how many rows should get fetched with the query.

Cacheable ​

org.hibernate.cacheable turns on the 2nd-level cache. This will improve the speed for repeated queries.

To activate the cache, you have to turn it on in the application.properties

properties
spring.jpa.properties.hibernate.cache.use_second_level_cache=true

then activate it on the entity class

java
@Entity
@Table(name = "customer")
@Cacheable
public class Customer {}

Timeout ​

jakarta.persistence.query.timeout defines when to cancel a long-running query.

Example with all query hints ​

java
@Repository
public interface CustomerRepository extends JpaRepository<Product, Long> {

    @Query("SELECT c FROM Customer c WHERE ...")
    @QueryHints({
        @QueryHint(name = "org.hibernate.readOnly", value = "true"),
        @QueryHint(name = "org.hibernate.fetchSize", value = "50"),
        @QueryHint(name = "org.hibernate.cacheable", value = "true"),
        @QueryHint(name = "jakarta.persistence.query.timeout", value = "2000")
    })
    List<Product> findCustomers(@Param("type") String type);
}

Contact: M_Bergmann AT gmx.at