ExerciseWarm-up
Warm-up
Write to the database without calling save
10 minjunior0–15 yrs
One concept, guided. Near-impossible to fail.
What this teaches
- A setter on a managed entity is a database write with a delay
- The mechanism is a snapshot taken at load and compared at flush
- Detached and read-only both silently do nothing, for different reasons
- The default UPDATE sets every column, not the one that changed
Starter
Starter.java
import java.util.*;
/**
* Warm-up: make the database change without asking it to.
*
* A model of a persistence context. The only thing it needs to do is what
* Hibernate does — remember what an entity looked like when it handed it over.
*/
public class Starter {
/** Deliberately mutable. A record would not exhibit any of this. */
static final class Product {
final long id;
String name;
int priceMinor;
int stock;
Product(long id, String name, int priceMinor, int stock) {
this.id = id; this.name = name; this.priceMinor = priceMinor; this.stock = stock;
}
}
public static void main(String[] args) {
// TODO 1: write Session.find(id). It returns a Product AND stores a
// snapshot of its fields. Use a LinkedHashMap in a fixed order, not
// Map.of — Map.of's iteration order is randomised per JVM run and your
// output will not be stable.
// TODO 2: write flush(). For each managed entity, compare its current
// fields against the snapshot and build an UPDATE for the ones that
// differ. Return the statements.
// TODO 3: now the demo. find() a product, change one field, and call
// flush(). Do not call save anywhere. Print the statement.
//
// Predict whether anything is issued before you run it.
// TODO 4: look at the statement you produced. How many columns does it
// set, and how many did you change? Say which is the default in
// Hibernate and what annotation changes it.
// TODO 5: detach the entity — remove it from the session — then change
// a field and flush. Nothing should happen. Say why this and TODO 3
// are the same question asked from two directions.
// TODO 6: add a readOnly flag that makes find() skip the snapshot.
// Change a field, flush, and observe that nothing is issued AND
// nothing is reported. Say when that is the behaviour you want and
// when it is a bug you will not find in testing.
// TODO 7: load 50,000 entities, change one field on one of them, and
// count how many field comparisons the flush performs. Then say what
// the session is holding in memory while it does that.
// TODO 8: flush does not only run at commit — Hibernate flushes before
// most queries so they see pending changes. Sketch what that does to a
// loop that loads a batch, runs a query, loads another batch.
}
}Run it locally:
cd exercises/java/jpa-hibernate/dirty-checking/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterDone when
- You issued an UPDATE from a method containing no save() call
- You showed the same mutation doing nothing on a detached entity
- You showed readOnly discarding a change with no error
- You measured snapshots kept and field comparisons for a large load
← Back to What is dirty checking, and why did my entity save without a save() call?