Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve a graph structure in Hibernate

Object structure is like this

  • Invoice
    • Customer
    • Date
    • Number
    • has many ProductLines (Product ,Quantity ,Price)
    • has many ServiceLines (Service , Quantity,Price)
    • has many PaymentOptions (PaymentType(Check,Receipt,etc.),Date,Sum)

If I need to retrieve a list of invoices for a given period with Hibernate is very easy to do with lazy loading without writing any code just call get...BUT there is the drawback of too many DB calls, so in a multiuser environment this solution is not ok.

With plain JDBC I solved this using 3 queries : 3 joins between Invoice and ProductLines, Invoice and ServiceLines and Invoice and Payment Options. After that I constructed the object in the memory.

Same can be done with Hibernate I know BUT my question is there is no such thing as load graph or so I can pass a list of invoices and in minimum number of calls (optimum one) to retrieve the data ?

like image 878
Cris Avatar asked Jan 27 '11 10:01

Cris


2 Answers

You can use queries with join fetching to get the entire object graph into memory:

Query query = entityManager.createQuery("select distinct invoice from Invoice as invoice " 
            + "left join fetch invoice.productLines "                
            + "left join fetch invoice.serviceLines "
            + "left join fetch invoice.paymentOptions");
for (Object object : query.getResultList()) {
    // Code here
}
like image 71
Russ Hayward Avatar answered Nov 14 '22 11:11

Russ Hayward


You may like to consider batch fetching for your invoice lines and eager fetching for many to one associations such as Customer.

This will not get you down to one DB call, but if you tune your batch sizes to slightly exceed the average number of lines of each type in an invoice, then you could get down to one per line type.

You could also query using JPQL / HQL and explicitly fetch-join the lines (as Russ decribes), but you get a larger result set than you would with a batch fetching approach:

Query query = entityManager.createQuery("select distinct invoice from Invoice as invoice " 
            + "left join fetch invoice.productLines "                
            + "left join fetch invoice.serviceLines "
            + "left join fetch invoice.paymentOptions");
for (Object object : query.getResultList()) {
    // Code here
}

If the size of the raw data transferred proves to be too high, you can disable fetching explicitly by removing "fetch" from the query in the relevant places.

Note that there is a maintenance issue using JPQL because the compiler cannot check all the property names for you, and if fetching is off for all associations then the query is a net cost. You may like to start with batch fetching if performance is not an immediate issue.

like image 4
Simon Gibbs Avatar answered Nov 14 '22 10:11

Simon Gibbs