Tuesday, March 18, 2008

Hibernate N+1

By default, Hibernate never loads data that you didn’t ask for, which reduces the memory consumption of your persistence context.

Instead of executing too many SQL statements (N+1), you may now (often as a side effect) create statements that retrieve too much data (always using one query to initialize all list).

<class name="Item">
...
<set name="bids" inverse="true">
<key column="ITEM_ID">
<one-to-many class="Bid">
</set>
</class>
To avoid n+1 selects, a first solution could be a change of your global mapping metadata for the collection,enabling prefetching in batches:
<set name="bids" inverse="true" batch-size="10">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>
Instead of n+1 selects, you now see n/10+1 selects to retrieve the required collections into memory.

With a subselect-based prefetch, you can reduce the number of selects to exactly two:
<set name="bids" inverse="true" fetch="subselect">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>
The first query in the procedure now executes a single SQL SELECT to retrieve all Item instances. Hibernate remembers this statement and applies it again when you hit the first uninitialized collection. All collections are initialized with the second query.

Finally, you can effectively turn off lazy loading of the bids collection and switch to an eager fetching strategy that results in only a single SQL SELECT.
<set name="bids" inverse="true" fetch="join">
<key column="ITEM_ID"/>
<one-to-many class="Bid"/>
</set>

In practice, you’ll most likely enable a batch or subselect strategy in your mapping metadata for the bids collection. If a particular procedure, such as this,requires all the bids for each Item in-memory, you modify the initial HQL or Criteria query and apply a dynamic fetching strategy:

List<Item> allItems = session.createQuery("from Item i left join fetch i.bids").list();
List<Item> allItems = session.createCriteria(Item.class)
.setFetchMode("bids", FetchMode.JOIN).list();
// Iterate through the collections...

Both queries result in a single SELECT that retrieves the bids for all Item instances with an OUTER JOIN (as it would if you have mapped the collection with join="fetch").

-- excerpted from Java.Persistence.with.Hibernate

No comments:

Post a Comment