Card 01/ 07

ConceptDifficulty: Intermediate1 min

JPA Persistence Context and Dirty Checking

Change a loaded entity inside a transaction and JPA saves it, even though nothing called save.

JPA keeps a persistence context: a record of every entity (an object mapped to a table row) it has loaded inside the current transaction. When the transaction commits, it compares each of those entities with the state it loaded and writes any change to the database. That comparison is called dirty checking, and it means JPA does not need to be told what changed.

Load a fare inside a @Transactional method (a method that runs inside one database transaction), change one field, and call nothing else:

java
@Transactional
void bumpFareNoExplicitSave(Long id) {
    FareEntity fare = repo.findById(id).orElseThrow();
    fare.amount = 99.0;
    // no repo.save(fare) call anywhere
}
text
no explicit save() called, amount is now: 99.0
run in a container: reloaded from the database after the method returns

No save() was called, and the new amount reached the database anyway. The entity was part of the persistence context from the moment findById loaded it, so the commit at the end of the method found the changed field and wrote it.

Spring Data JDBC has neither. It does not keep the objects it loads, and it does not watch them for changes: nothing is written until you call save(). That is a different trade, not a missing feature. With no watching, there are no writes you did not ask for and no cost of checking every field of every loaded object.

So in a JPA application, changing an entity inside a transaction is a write, whether you meant it or not. In a Spring Data JDBC application, only save() writes.