Quantcast
Channel: Hibernate – Catalysts
Viewing all articles
Browse latest Browse all 4

EJB 3.0 Annotations mit Hibernate Lazy Loading

$
0
0

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.

 


Viewing all articles
Browse latest Browse all 4

Latest Images





Latest Images