EJB 3.0 Annotations mit Hibernate Lazy Loading

Wissenswertes
2 Comments

By means of the EJB 3.0 annotations from the package java.persistence it’s very easy to map java classes to database tables without writing any XML. These annotations can be directly put onto the fields or on the corresponding getter methods. In the following example we use the fields:

import javax.persistence.*
 
@Entity
public class Person {
 
  @Id @GeneratedValue
  private long id;
 
  private String name;
 
  public String getId() {
    return this.id;
  }
 
  public String getName() {
    return this.name;
  }
}

Anyways, putting the annotations to the fields has a painful – although non obvious – side effect in connection with hibernate proxies created by lazy loading.

Let’s have a look at the following example of a 1:1 relationship (constructors have been omitted to keep the code short):

@Entity public class Person {   @Id @GeneratedValue private long id;   private String name;   @OneToOne(fetch = FetchType.LAZY) private Address address;   public String getId() { return this.id; }   public String getName() { return this.name; } }   @Entity public class Address { @Id @GeneratedValue private long id;   private String town;   public long getId() { return this.id; }   public String getTown() { return this.town; } }

If we now load a Person object in a Hibernate session, then the Address is not loaded initially (due to the FetchType.LAZY); Hibernate creates a proxy for it:

Person person = (Person) session.load(Person.class, Long.valueOf(1)); Address address = person.getAddress(); // Hibernate returns a proxy here

Those Hibernate proxies access the database as soon as the field is first accessed:

String town = address.getTown(); // implicit db access by Hibernate

This is true for all fields except the identifier property (the field with the @Id Annotation) as this value is already fetched with the SELECT on the table Person.

long addressId = address.getId();

And that’s the trap: Although we only access the ID, Hibernate initializes the Proxy and fetches the whole record. The reason is that Hibernate doesn’t know anything about the method getId() if the @Id-Annotation is placed on the field. Hibernate therefore thinks that getId() is a standard field access and loads the whole record. If you access that field outside the Hibernate session, you therefore get a LazyInitializationException.

To fix that problem, the @Id annotation needs to be put to the getter method. That in turn makes it necessary to put all other annotations to the getters as Hibernate does not allow to mix field and getter annoations. And as we’re now using getters for our annotations, we also need to provide setters as well (those can be private or protected).

Our code now looks as follows:

@Entity public class Person {   private long id; private String name; private Address address;   @Id @GeneratedValue public String getId() { return this.id; }   public String getName() { return this.name; }   @OneToOne(fetch = FetchType.LAZY) public Address getAddress() { return this.address; } }   @Entity public class Address { private long id;   private String town;   @Id @GeneratedValue public long getId() { return this.id; }   public String getTown() { return this.town; } }

Lazy loading is working as expected:

Person person = (Person) session.load(Person.class, Long.valueOf(1)); // DB-Select Address address = person.getAddress(); long addressId = address.getId(); // no DB-Select String town = address.getTown(); // Proxy gets initialized, DB-Select

http://blog.xebia.com/2009/06/13/jpa-implementation-patterns-field-access-vs-property-access/ describes a little workaround for accessing the id value without initializing the proxy.

 

Previous Post
Akzeptanztests und exploratives Testen
Next Post
Continuous Integration mit FlexUnit und TeamCity

Related Posts

JPF Part 1 – A lightweight alternative to OSGi?

When developing web applications it has always been a challenge how to plug in customer specific extensions and integrations without invading the inner guts of the web application too much. This not only concerns the source code, it is quite easy to keep customer specific sources out of the application’s trunk, the main problems usually arise when thinking about deployment strategies

Read More

2 Comments. Leave new

This is a known bug in Hibernate 3.3.1 that is hopefully addressed in one of the next releases. See http://opensource.atlassian.com/projects/hibernate/browse/HHH-3718

There are other pitfalls one might stumble upon when using Hibernate proxies, further information can be found at http://blog.xebia.com/2008/03/08/advanced-hibernate-proxy-pitfalls/

At http://www.fnogol.de/media/ejb3.0-anno-cheat-1.2.pdf there is a handy cheat sheet for EJB annotations i found helpful.

Reply

EJB 3.0 Annotations with Hibernate Lazy Loading — Catalysts for Agile Software Developers…

By means of the EJB 3.0 annotations from the package java.persistence it’s very easy to map java classes to database tables without writing any XML. These annotations can be directly put onto the fields or on the corresponding getter methods. In the …

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.
You need to agree with the terms to proceed

Menu