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