Warm-up

Find the queries you did not ask for

10 minjunior015 yrs

One concept, guided. Near-impossible to fail.

What this teaches

  • To-one associations default to EAGER and collections default to LAZY
  • Loading one entity and touching nothing can still issue several queries
  • LazyInitializationException means an entity outlived its session
  • open-in-view hides the exception by moving the queries into rendering

Starter

Starter.java
import java.util.*;

/**
 * Warm-up: count the queries nobody asked for.
 *
 * A model of a persistence context — a session that opens and closes, proxies
 * that throw when touched afterwards, and a counter. No Hibernate, because the
 * behaviour worth learning is which associations load and when.
 */
public class Starter {

    enum Fetch { LAZY, EAGER }

    public static void main(String[] args) {

        // TODO 1: write down, from memory, the default fetch type for
        // @ManyToOne, @OneToOne, @OneToMany and @ManyToMany. Then check.
        // Most people get two of the four wrong.

        // TODO 2: build a Session with an open flag and a query counter, and
        // an Association with a fetch type. EAGER records a query when it is
        // constructed. LAZY records one on first access, and throws if the
        // session has closed.

        // TODO 3: build an Order with a to-one and a to-many, each using the
        // JPA DEFAULT for its mapping. Load it. Touch nothing. Print the query
        // count and which associations are loaded.
        //
        // Predict the number first.

        // TODO 4: now load 100 orders the same way. What is the query count,
        // and which annotation caused it? Note that nothing in the mapping
        // says EAGER.

        // TODO 5: make everything LAZY. Close the session, then read the
        // collection. You should get your exception. Read the message — it
        // says "no Session", which is the whole diagnosis.

        // TODO 6: now keep the session open until after the read, the way
        // spring.jpa.open-in-view does. The exception disappears. Count the
        // queries issued BEFORE the session would normally have closed, and
        // the queries issued after. Say which of those two numbers a timer
        // around your service method would have seen.

        // TODO 7: someone "fixes" the exception by making the collection
        // EAGER. Compare the total query count against the open-in-view
        // version. Say what actually changed and what did not.

        // TODO 8: finally, load the collections deliberately before closing —
        // one extra query for all of them. Compare all four numbers and say
        // which is the fix and which two are hiding places.

        // TODO 9: check what spring.jpa.open-in-view defaults to in Spring
        // Boot, and find the warning it logs at startup.
    }
}

Run it locally:

cd exercises/java/jpa-hibernate/fetch-types/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out Starter

Done when

  • You showed a single entity load issuing more than one query
  • You produced LazyInitializationException deliberately
  • You showed the same code succeeding with the session kept open, and counted where the queries moved to
  • You can state the default for all four mapping annotations without looking

← Back to What are the fetch types, and which is the default for each mapping?