venerdì 25 marzo 2011

Hibernate load vs get

The load method returns a proxy and hits the database only when you access a filed of the entity. If any entity is found the load method throws an ObjectNotFounfException but only when you try to access the entity fields.

The get method hits the database as soon as it's called and if no entity is found returns null.

When to use get and when load?

The load method increases perfromance (hits the database only when necessary) so if you know your entity will be used only within the unit of work where it's retrieved than the load method is probably the best choice but if the retrieved entity is used outsite the transactional unit of work, e.g. it's displayed on a web page, using the get method is probably better.

giovedì 24 marzo 2011

Hibernate HQL and fetch="join"

Mapping a relationship between two entities with fetch="join" has two different behaviours depending on the type of query.

As an example if you have an entity Author

public class Author {

private long id;
private String name;
private Set books = new HashSet();

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Set getBooks() {
return books;
}

public void setBooks(Set books) {
this.books = books;
}

public long getId() {
return id;
}

void setId(long id) {
this.id = id;
}
public void addBook(Book b){
books.add(b);
}
}

with his mapping file Author.hbm.xml :
<hibernate-mapping package="org.dreborier.domain"> <class name="Author" table="AUTHOR"> <id name="id" column="AUTHOR_ID"> <generator class="native"></generator> </id> <property name="name" column="NAME"></property> <set name="books" table="BOOK" cascade="save-update" fetch="join"> <key column="AUTHOR_ID"></key> <one-to-many class="Book"/> </set> </class> </hibernate-mapping>  
 
and the entity Book :

public class Book {
private long id;
private String title;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public long getId() {
return id;
}

void setId(long id) {
this.id = id;
}

}
along with the mapping file Book.hbm.xml :
<hibernate-mapping package="org.dreborier.domain">
<class name="Book" table="BOOK"> 
<id name="id" column="BOOK_ID"> 
<generator class="native"></generator> 
</id> <property name="title" column="TITLE"></property> </class> 
</hibernate-mapping>
if you use a query by Criteria :
Criteria criteria = session.createCriteria(Author.class);
List authors = criteria.list();
with the fecth="join" attribute hibernate will execute the following query :
select this_.AUTHOR_ID as AUTHOR1_0_1_, this_.NAME as NAME0_1_, books2_.BOOK_ID as BOOK1_0_3_, books2_.BOOK_ID as BOOK1_3_, books2_.BOOK_ID as BOOK1_1_0_, books2_.TITLE as TITLE1_0_ from AUTHOR this_ left outer join BOOK books2_ on this_.AUTHOR_ID=books2_.BOOK_ID

so books are loaded along with authors.
while if you use HQL :
Query query = session.createQuery("from Author");
List authors = query.list();
hibernate will execute the following query:
select author0_.AUTHOR_ID as AUTHOR1_0_, author0_.NAME as NAME0_ from AUTHOR author0_
as you can see the fetch="join" do not affect the HQL query and a second query wiil be executed when an author's book is accessed
so if you want to fetch eagerly the Book using HQL you have to use left join fetch :
Query query = session.createQuery("from Author as author left join fetch author.books");
Hibernate Reference Documentation recommends
Usually, the mapping document is not used to customize fetching. Instead, we keep the defaultbehavior, and override it for a particular transaction, using left join fetch in HQL. This tellsHibernate to fetch the association eagerly in the first select, using an outer join. In the Criteriaquery API, you would use setFetchMode(FetchMode.JOIN).
The fetch="join" attibute defined in the mapping file affects get and load methods.
The following code:

Author author = (Author) session.get(Author.class, 1L);
Set books = author.getBooks();
books.size();
without the fetch attribute will produce two database hits
select author0_.AUTHOR_ID as AUTHOR1_0_0_, author0_.NAME as NAME0_0_ from AUTHOR author0_ where author0_.AUTHOR_ID=?
and
select books0_.AUTHOR_ID as AUTHOR3_0_1_, books0_.BOOK_ID as BOOK1_1_, books0_.BOOK_ID as BOOK1_1_0_, books0_.TITLE as TITLE1_0_ from BOOK books0_ where books0_.AUTHOR_ID=?
while with fetch="join" the database is hit only one time
select author0_.AUTHOR_ID as AUTHOR1_0_1_, author0_.NAME as NAME0_1_, books1_.AUTHOR_ID as AUTHOR3_0_3_, books1_.BOOK_ID as BOOK1_3_, books1_.BOOK_ID as BOOK1_1_0_, books1_.TITLE as TITLE1_0_ from AUTHOR author0_ left outer join BOOK books1_ on author0_.AUTHOR_ID=books1_.AUTHOR_ID where author0_.AUTHOR_ID=?