
1. Introduction
In this tutorial, we’ll explore one of Hibernate‘s features: the Dirty Checking mechanism.
This concept enables Hibernate to detect changes in the state of entities automatically, allowing it to update entities automatically. We’ll start by understanding dirty checking, the idea of dirty entities, and how the mechanism works. Then, we’ll move on to a code example. Finally, we’ll discuss how to control and, if necessary, disable this feature.
2. What Is Dirty Checking In Hibernate?
Dirty checking is a mechanism in Hibernate that automatically detects and synchronizes changes made to the entities without requiring explicit update queries. When an entity state is changed in memory, Hibernate identifies the changes and ensures they will be persisted in a database when the transaction is committed.
Before exploring the mechanism deeper, let’s first define dirty entities as entities whose state has changed compared to their last known database state.
It’s important to note that dirty checking only applies to entities in a persistent state of the Hibernate entity lifecycle. In other words, it works only for entities that are associated with an active session.
3. How Does Dirty Checking Work?
The dirty checking mechanism takes a snapshot of the last known entity state. This typically occurs when a persistent object is first loaded from the database. During the session, if any property of the entity is modified, that entity becomes dirty. Later, while flushing the persistence context, Hibernate checks each dirty entity associated with the session against the snapshot created earlier and updates it in the database.
We don’t need to explicitly invoke any methods for persisting the data. Hibernate automatically flushes the session before committing the transaction, therefore ensuring that dirty checking is performed.
The entire process is automatic. Hibernate creates SQL queries and updates corresponding rows in the database.
3.1. Performance Impacts
While dirty checking is convenient, it does come with some performance considerations and impacts.
Hibernate tracks changes to objects, which can increase memory usage and processing time, especially for large datasets. In most cases, the overhead would be minimal, as it’s already optimized for performance.
However, there is a feature to improve performance – enhanced dirty tracking. Enhanced dirty tracking is an optimization in which Hibernate uses bytecode enhancement to track entity changes at the attribute level instead of comparing entire objects during flush. Full snapshots would be compared without this enhancement, but with the enhancement enabled, Hibernate knows exactly which fields changed and updates only those.
To enable dirty tracking, we need to include and configure the Hibernate enhancer plugin in our build tool.
4. Code Example
Now that we understand how the mechanism works, let’s see it in practice.
As a first step, we’ll create a basic entity to work with:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private double price;
// omitted constructors, getters and setters
}
Let’s add a repository as well:
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
Optional<Product> findByCode(String code);
}
With the basic setup completed, let’s modify the state of an entity while it’s in a persistence context. To achieve that, we’ll first create an instance and persist it. After that, we change its state, flush the session, and observe changes applied without additional method invocations:
@Test
@Transactional
void givenProduct_whenModifiedWithoutSave_thenAssertChangesPersisted() {
Product product = new Product("LOREM", 100.00);
productRepository.save(product);
product.setPrice(80.00);
entityManager.flush();
entityManager.clear();
Product updatedProduct = productRepository.findByCode("LOREM").orElseThrow(RuntimeException::new);
assertEquals(80.00, updatedProduct.getPrice());
}
The test confirms that the entity state is indeed changed after the flush. When a transaction commit occurs, data will be updated to the corresponding row in the database, as we can examine in the logs as well:
Hibernate: insert into product (code,price,id) values (?,?,default)
Hibernate: update product set code=?,price=? where id=?
Hibernate: select p1_0.id,p1_0.code,p1_0.price from product p1_0 where p1_0.code=?
5. Disable the Dirty Checking Mechanism
As observed, dirty checking is fully automated and helpful, but what if we want to disable it? Unfortunately, disabling dirty checking isn’t possible out of the box, as it’s deeply integrated into the framework. However, in this section, we’ll discuss scenarios where dirty checking doesn’t apply.
5.1. Explicitly Detach Entity
As previously mentioned, dirty checking only works for entities in a persistent state. Hibernate stops tracking changes once an entity is detached either manually, by clearing the persistence context, or when a transaction ends, thus preventing automatic updates.
We can modify the previous test by detaching the entity and asserting that the value remains unchanged:
@Test
@Transactional
void givenDetachedProduct_whenModifiedWithoutSave_thenAssertChangesNotPersisted() {
Product product = new Product("LOREM", 100.00);
productRepository.save(product);
entityManager.detach(product);
product.setPrice(80.00);
entityManager.flush();
entityManager.clear();
Product updatedProduct = productRepository.findByCode("LOREM").orElseThrow(RuntimeException::new);
assertEquals(100.00, updatedProduct.getPrice());
}
5.2. Read-Only Transaction
When a transaction is marked as read-only with @Transactional(readOnly = true), Hibernate optimizes performance by skipping entity snapshots. Since no snapshots are created, Hibernate cannot detect modifications. When the managed entity is modified, those changes are ignored if not persisted manually.
In general, @Transactional(readOnly=true) doesn’t enforce read-only behavior at the database level, it only prevents Hibernate from taking snapshots, tracking modifications and automatically flushing the session. Changes will be applied if an explicit save() or persist() is called.
5.3. Interaction With @Immutable Entities
Entities annotated with @Immutable are treated as read-only by Hibernate. Since they’re considered unchangeable, Hibernate skips tracking modifications, preventing dirty checking. Even if a property of the entity is modified, Hibernate won’t track the change, and no updates will be submitted to the database.
6. Conclusion
In this article, we explored how the dirty checking mechanism in Hibernate works, when it applies, and how it automatically synchronizes entity changes with the database. While dirty checking cannot be fully disabled, we’ve seen specific scenarios where dirty checking doesn’t take effect.
As always, full code examples are available over on GitHub.
The post How Hibernate Dirty Checking Mechanism Works first appeared on Baeldung.