com.avaje.ebean
Class JoinConfig

java.lang.Object
  extended by com.avaje.ebean.JoinConfig

public class JoinConfig
extends Object

Defines the configuration options for a "query join" or a "lazy loading join". This gives you the ability to use "query joins" as opposed to "fetch joins" and also the ability to control the lazy loading queries (batch size, selected properties and joins).

We can use this to optimise the performance/behaviour of object graph loading for a given use case. That is, there are cases where using multiple queries is more efficient that a single query and or we want to optimise the lazy loading behaviour (avoid N+1 queries etc).

Any time you want to load multiple OneToMany associations it will likely be more performant as multiple SQL queries. If a single SQL query was used that would result in a Cartesian product.

There can also be cases loading across a single OneToMany where 2 SQL queries (using Ebean "query join") can be more efficient than one SQL query (using Ebean "fetch join"). When the "One" side is wide (lots of columns) and the cardinality difference is high (a lot of "Many" beans per "One" bean) then this can be more efficient loaded as 2 SQL queries.

The reason you would want to control the lazy loading query is to optimise performance for further lazy loading (avoid N+1 queries, load what is required and no more etc).

A join/association in Ebean can be loaded in 3 ways:

Note: "lazy loading" occurs on demand when the application navigates to part of the object graph that is not yet loaded. Ebean will automatically fetch the data via a "lazy loading query".

You use JoinConfig to mark a join as a "query join" or a "lazy load join".

 // Normal fetch join results in a single SQL query 
 List<Order> list = Ebean.find(Order.class)
   .join("details")
   .findList();
 
 // Find Orders join details using a single SQL query
 

Example: Using a "query join" instead of a "fetch join" we instead use 2 SQL queries

 // This will use 2 SQL queries to build this object graph
 List<Order> list = Ebean.find(Order.class)
   .join("details",new JoinConfig().query())
   .findList();
 
 // query 1)  find order 
 // query 2)  find orderDetails where order.id in (?,?...) // first 100 order id's
 

Example: Using 2 "query joins"

 // This will use 3 SQL queries to build this object graph
 List<Order> list = Ebean.find(Order.class)
   .join("details", new JoinConfig().query())
   .join("customer", new JoinConfig().query(5))
   .findList();
 
 // query 1) find order 
 // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
 // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
 

Example: Using "query joins" and partial objects

 // This will use 3 SQL queries to build this object graph
 List<Order> list = Ebean.find(Order.class)
   .select("status, shipDate")
   
   .join("details", "quantity, price", new JoinConfig().query())
   .join("details.product", "sku, name")
   
   .join("customer", "name", new JoinConfig().query(10))
   .join("customer.contacts")
   .join("customer.shippingAddress")
   .findList();
 
 // query 1) find order (status, shipDate)
 // query 2) find orderDetail (quantity, price) join product (sku, name) where order.id in (?,? ...)
 // query 3) find customer (name) join contacts (*) join shippingAddress (*) where id in (?,?,?,?,?)
 
 
 // Note: the join to "details.product" is automatically included into the 
 //       "details" query join
 //
 // Note: the joins to "customer.contacts" and "customer.shippingAddress" 
 //       are automatically included in the "customer" query join
 

You can use query() and lazy together on a single join. The query is executed immediately and the lazy defines the batch size to use for further lazy loading (if lazy loading is invoked).

 List<Order> list = Ebean.find(Order.class)
   .join("customer",new JoinConfig().query(3).lazy(10))
   .findList();
 
 // query 1) find order 
 // query 2) find customer where id in (?,?,?) // first 3 customers 
 // .. then if lazy loading of customers is invoked 
 // .. use a batch size of 10 to load the customers
 
 

Example of controlling the lazy loading query:

This gives us the ability to optimise the lazy loading query for a given use case.

 List<Order> list = Ebean.find(Order.class)
   .join("customer","name", new JoinConfig().lazy(5))
   .join("customer.contacts","contactName, phone, email")
   .join("customer.shippingAddress")
   .where().eq("status",Order.Status.NEW)
   .findList();
 
 // query 1) find order where status = Order.Status.NEW
 //  
 // .. if lazy loading of customers is invoked 
 // .. use a batch size of 5 to load the customers 
  
       find customer (name) 
       join contact (contactName, phone, email) 
       join shippingAddress (*) 
       where id in (?,?,?,?,?)
 
 

Author:
mario, rbygrave

Constructor Summary
JoinConfig()
          Construct the join configuration object.
 
Method Summary
 int getLazyBatchSize()
          Return the batch size for lazy loading.
 int getQueryBatchSize()
          Return the batch size for "query join".
 boolean isQueryAll()
          Return true if the query fetch should fetch 'all' rather than just the 'first' batch.
 JoinConfig lazy()
          Specify that this join should be lazy loaded using the default batch load size.
 JoinConfig lazy(int lazyBatchSize)
          Specify that this join should be lazy loaded with a specified batch size.
 JoinConfig query()
          Specify that this join should instead be invoked as a "query join" (rather than a fetch join).
 JoinConfig query(int queryBatchSize)
          Specify that this path should be loaded as a separate query (rather than as part of the main query).
 JoinConfig queryFirst(int queryBatchSize)
          Similar to query(int) but only fetches the first batch.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

JoinConfig

public JoinConfig()
Construct the join configuration object.

Method Detail

lazy

public JoinConfig lazy()
Specify that this join should be lazy loaded using the default batch load size.


lazy

public JoinConfig lazy(int lazyBatchSize)
Specify that this join should be lazy loaded with a specified batch size.

Parameters:
lazyBatchSize - the batch size for lazy loading

query

public JoinConfig query()
Specify that this join should instead be invoked as a "query join" (rather than a fetch join).

This will use the default batch size for "query join" which is 100.


query

public JoinConfig query(int queryBatchSize)
Specify that this path should be loaded as a separate query (rather than as part of the main query).

The queryBatchSize is the number of parent id's that this separate query will load per batch.

This will load all beans on this path eagerly.

Parameters:
queryBatchSize - the batch size used to load beans on this path

queryFirst

public JoinConfig queryFirst(int queryBatchSize)
Similar to query(int) but only fetches the first batch.

If there are more parent beans than the batch size then they will not be loaded eagerly but instead use lazy loading.

Parameters:
queryBatchSize - the number of parent beans this path is populated for

getLazyBatchSize

public int getLazyBatchSize()
Return the batch size for lazy loading.


getQueryBatchSize

public int getQueryBatchSize()
Return the batch size for "query join".


isQueryAll

public boolean isQueryAll()
Return true if the query fetch should fetch 'all' rather than just the 'first' batch.



Copyright © 2010. All Rights Reserved.