Warm-up

Write to the database without calling save

10 minjunior015 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 Starter

Done 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?