12 global fetching strategies

21
© JBoss, Inc. 2003, 2004. 1 Professional Open Source™ Global Fetching strategies

description

 

Transcript of 12 global fetching strategies

Page 1: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 1

Professional Open Source™

Global Fetching strategies

Page 2: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 2

Professional Open Source™

Lazy Default Fetch Plan

Hibernate defaults to a lazy fetching strategy for all entities and collections.

Item item = (Item) session.load(Item.class, new Long(123));

load() will return a proxy and the sql that loads an Item isn’t executed .

A proxy is a placeholder that triggers the loading of the real object when it’s accessed for the first time .

Item item = (Item) session.load(Item.class, new Long(123)); item.getId(); item.getDescription(); // Initialize the proxy

As long as you access only the database identifier property, no initialization of the proxy is necessary.

Page 3: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 3

Professional Open Source™

What is the need of a proxy ?

A proxy is useful if you need the Item only to create a reference, for example:

Item item = (Item) session.load(Item.class, new Long(123)); User user = (User) session.load(User.class, new Long(1234)); Bid newBid = new Bid("99.99"); newBid.setItem(item); newBid.setBidder(user); session.save(newBid);

Page 4: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 4

Professional Open Source™

After initializing the proxy ….

A Fully initialized Item proxy Associated entity objects and collections are not loaded right away; the proxies carry the identifier values only.

Collections also aren’t loaded right away, but we use the term collection wrapper to describe this kind of placeholder. Internally, Hibernate has a set of smart collections that can initialize themselves on demand. Hibernate replaces your collections with these; that is why you should use collection interfaces only in your domain model.

By default, Hibernate creates placeholders for all associations and collections, and only retrieves value-typed properties and components right away.

Page 5: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 5

Professional Open Source™

When are collections initialized

a collection is initialized if you start iterating through its elements or if you call any of the collection-management operations, such as size()

and contains(). Hibernate provides an additional setting that is mostly useful for large

collections; they can be mapped as extra lazy.

The collection wrapper is now smarter than before.

The collection is no longerinitialized if you call size(), contains(), or isEmpty()—the database is queriedto retrieve the necessary information.

Page 6: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 6

Professional Open Source™

Disabling proxy generation

You can disable proxy generation for a particular entity class with the lazy="false" attribute <class name="User“ table="USERS“ lazy="false"> ... </class>

Disabling proxy generation on a global level is often too coarse-grained.

Usually, you only want to disable the lazy loading behavior of a particular entity association or collection to define a fine-grained fetch plan.

Page 7: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 7

Professional Open Source™

Eager loading of associations and collections

Let’s assume that you always require the seller of an Item. In Hibernate XML mapping metadata you’d map the association from Item to User as lazy="false":

For eager loading collections …..

After eager loading of bids , seller and successful Bid

Page 8: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 8

Professional Open Source™

Selecting a fetching Strategy

Item item = (Item) session.get(Item.class, new Long(123));

You didn’t configure any association or collection to be nonlazy, and that proxies can be generated for all associations. Hence, this operation results in the following SQL SELECT:

select item.* from ITEM item where item.ITEM_ID = ?

If you access any proxied association or uninitialized collection, a second SELECT is executed to retrieve the data on demand.

By selecting a good fetching strategy, we have to reduce the number of on-demand SELECTS

Page 9: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 9

Professional Open Source™

Prefetching data in batches

If every entity association and collection is fetched only on demand, many additional SQL SELECT statements may be necessary to complete a particular procedure .

List allItems = session.createQuery("from Item").list(); processSeller( (Item)allItems.get(0) ); processSeller( (Item)allItems.get(1) ); processSeller( (Item)allItems.get(2) );

You see one SQL SELECT to retrieve all the Item objects, and an additional SELECT for every seller of an Item as soon as you process

it. All associated User objects are proxies. select items... select u.* from USERS u where u.USER_ID = ? select u.* from USERS u where u.USER_ID = ? select u.* from USERS u where u.USER_ID = ?

Page 10: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 10

Professional Open Source™

Prefetching data in batches

Batch fetching is often called a blind-guess optimization . You make a guess and apply a batch-size fetching strategy to your

User class mapping: <class name="User“ table="USERS“ batch-size="10"> ... </class>

You’re telling Hibernate to prefetch up to 10 uninitialized proxies in a single SQL SELECT, if one proxy must be initialized. The resulting SQL for the earlier query and procedure may now look as follows:

select items... select u.* from USERS u where u.USER_ID in (?, ?, ?,?... 10 times)

Page 11: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 11

Professional Open Source™

Batch Fetching for collections

Batch fetching is also available for collections: <set name="bids“ inverse="true“ batch-size="10"> <key column="ITEM_ID"/> <one-to-many class="Bid"/> </set>

Page 12: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 12

Professional Open Source™

Prefetching collections with subselects

<set name="bids“ inverse="true“ fetch="subselect"> <key column="ITEM_ID"/> <one-to-many class="Bid"/> </set>

Hibernate now initializes all bids collections for all loaded Item objects, as soon as you force the initialization of one bids collection.

It does that by rerunning the first initial query (slightly modified) in a subselect:

select i.* from ITEM i select b.* from BID b where b.ITEM_ID in (select i.ITEM_ID from ITEM i)

Page 13: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 13

Professional Open Source™

Eager fetching with joins

<class name="Item" table="ITEM"> … <many-to-one name="seller“ class="User“ column="SELLER_ID" update="false“ fetch="join"/> </class>

Hibernate now loads both an Item and its seller in a single SQL statement. For example:

Item item = (Item) session.get(Item.class, new Long(123));

This operation triggers the following SQL SELECT: select i.*, u.* from ITEM i left outer join USERS u on i.SELLER_ID = u.USER_ID where i.ITEM_ID = ?

If you only enable eager fetching with lazy="false", you see an immediate second SELECT. With fetch="join", you get the seller loaded in the same single SELECT.

Page 14: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 14

Professional Open Source™

Eager fetching collections

You can also set the eager join fetching strategy on a collection: <class name="Item" table="ITEM"> ... <set name="bids“ inverse="true“ fetch="join"> <key column="ITEM_ID"/> <one-to-many class="Bid"/> </set> </class>

Following is the query executed when item is loaded :

select i.*, b.* from ITEM i left outer join BID b on i.ITEM_ID = b.ITEM_ID

Page 15: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 15

Professional Open Source™

Controlling the maximum number of joined entity associations

Let’s assume that Item has a successfulBid association, that Bid has a bidder, and that User has a shippingAddress. If all these associations are mapped with fetch="join", how many tables are joined and how much data is retrieved when you load an Item?

The number of tables joined in this case depends on the global hibernate. max_fetch_depth configuration property.

Reasonable settings are small, usually between 1 and 5.

You may even disable join fetching for many-to-one and one-to-one associations by setting the property to 0!

Page 16: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 16

Professional Open Source™

Outer joins for a table-per-subclass hierarchy

select b1.BILLING_DETAILS_ID, b1.OWNER,b1.USER_ID,b2.NUMBER, b2.EXP_MONTH,b2.EXP_YEAR,b3.ACCOUNT,b3.BANKNAME,b3.SWIFT, case when b2.CREDIT_CARD_ID is not null then 1 when b3.BANK_ACCOUNT_ID is not null then 2 when b1.BILLING_DETAILS_ID is not null then 0 end as clazz from BILLING_DETAILS b1 left outer join CREDIT_CARD b2 on b1.BILLING_DETAILS_ID = b2.CREDIT_CARD_ID left outer join BANK_ACCOUNT b3 on b1.BILLING_DETAILS_ID = b3.BANK_ACCOUNT_ID

It joins three tables . Many dbms limit the maximum number of tables that can be combined with an OUTER JOIN.

Page 17: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 17

Professional Open Source™

Switching to additional selects

The only way to enable this fetching strategy is to refactor the mapping slightly, as a mix of table-per-hierarchy (with a discriminator column) and table-per-subclass with the <join> mapping:

<class name="BillingDetails“ table="BILLING_DETAILS“ abstract="true"> <id name="id“ column="BILLING_DETAILS_ID“ .../> <discriminator column="BILLING_DETAILS_TYPE“ type="string"/> ... <subclass name="CreditCard" discriminator-value="CC"> <join table="CREDIT_CARD" fetch="select"> <key column="CREDIT_CARD_ID"/> ... </join> </subclass> <subclass name="BankAccount" discriminator-value="BA"> <join table="BANK_ACCOUNT" fetch="join"> <key column="BANK_ACCOUNT_ID"/> ... </join> </subclass> </class>

Page 18: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 18

Professional Open Source™

select b1.BILLING_DETAILS_ID, b1.OWNER, b1.USER_ID, b2.ACCOUNT,b2.BANKNAME,b2.SWIFT,b1.BILLING_DETAILS_TYPE as clazz from BILLING_DETAILS b1 left outer join BANK_ACCOUNT b2 on b1.BILLING_DETAILS_ID = b2.BANK_ACCOUNT_ID

select cc.NUMBER, cc.EXP_MONTH, cc.EXP_YEAR from CREDIT_CARD cc where cc.CREDIT_CARD_ID = ?

select cc.NUMBER, cc.EXP_MONTH, cc.EXP_YEAR from CREDIT_CARD cc where cc.CREDIT_CARD_ID = ?

Fetch=“select” tells hibernate to fire an immediate select

Page 19: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 19

Professional Open Source™

The n+1 selects problem

This code produces n+1 selects

Instead of n+1 selects, u will see n/10 +1 selects

Page 20: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 20

Professional Open Source™

With a subselect-based prefetch, you can reduce the number of selects to exactly two

This will turn off lazy loading which is not advised

If needed, u can enable dynamic fetching strategy to join as follows :

Page 21: 12 global fetching strategies

© JBoss, Inc. 2003, 2004. 21

Professional Open Source™

Forcing proxy and collection initialization

Hibernate.initialize( item.getSeller() );