PDA

View Full Version : How to resolve LazyInitializationException's by always fetching EAGER?



Dr.Drane
Jun 17th, 2009, 05:33 AM
Hi all,

I'm trying to resolve my LazyInitializationException's by always fetching eagerly.

I've got a bidirectional relationship between my Person and Vehicle classes:

Person with OneToMany to Vehicle:


@Entity
@Table(name = "person")
public class Person {

//Fields
private Long id;
private String name;
private Date birthDate;
private Set<Vehicle> vehicles = new HashSet<Vehicle>();


//Property accessors
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}

public void setId(Long id) {
System.out.println("in java Person-> setId = "+id);
this.id = id;
}

@Column(name = "Name", nullable = false, length = 35)
public String getName() {
return this.name;
}

public void setName(String name) {
System.out.println("in java Person-> setName = "+name);
this.name = name;
}

@Temporal(TemporalType.DATE)
@Column(name = "BirthDate", nullable = true, length = 10)
public Date getBirthDate() {
return this.birthDate;
}

public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}

@CollectionOfElements(targetElement = be.tradelec.model.Vehicle.class)
@OneToMany(mappedBy = "person_id", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
public Set<Vehicle> getVehicles() {
return vehicles;
}

public void setVehicles(Set<Vehicle> vehicles) {
System.out.println("in java Person-> setVehicles = "+vehicles);
this.vehicles = vehicles;
}

public void addVehicle(Vehicle vehicle) {
vehicles.add(vehicle);
vehicle.setPerson(this);
}

}


Vehicle with ManyToOne to Peron:


@Entity
@Table(name = "vehicle")
public class Vehicle {

// Fields
private Long id;
private Long person_id;
private Person person;
private String licensePlate;
private String brand;
private String type;

// Constructors
/** default constructor */
public Vehicle() {
}

/** full constructor */
public Vehicle(Long id, Person person,
String licensePlate, String brand, String type) {
this.id = id;
//this.person = person;
this.licensePlate = licensePlate;
this.brand = brand;
this.type = type;
}

// Property accessors
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}

public void setId(Long id) {
System.out.println("in java Vehicle-> setIdVehicle = "+id);
this.id = id;
}

@CollectionOfElements(targetElement = be.tradelec.model.Person.class)
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name = "person_id")
public Person getPerson() {
return this.person;
}

public void setPerson(Person person) {
this.person = person;
}

@Column(name = "LicensePlate", nullable = true, length = 10)
public String getLicensePlate() {
return this.licensePlate;
}

public void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}

@Column(updatable=false, insertable=false)
public Long getPerson_id() {
return person_id;
}

public void setPerson_id(Long person_id) {
this.person_id = person_id;
}

@Column(name = "Brand", nullable = false, length = 45)
public String getBrand() {
return this.brand;
}

public void setBrand(String brand) {
this.brand = brand;
}

@Column(name = "Type", nullable = false, length = 45)
public String getType() {
return this.type;
}

public void setType(String type) {
this.type = type;
}

}

Saving a Person with added Vehicles works nicely. And with standard fetchtypes I can load my Person (with his vehicles) perfectly.

Now when I try to switch both my mappings to EAGER fetchtypes so I can send the whole Object graph through BlazeDS, I get an error while loading:


Hibernate: select person0_.id as id4_1_, person0_.BirthDate as BirthDate4_1_, person0_.Name as Name4_1_, vehicles1_.person_id as person4_3_, vehicles1_.id as id3_, vehicles1_.id as id5_0_, vehicles1_.Brand as Brand5_0_, vehicles1_.LicensePlate as LicenseP3_5_0_, vehicles1_.person_id as person4_5_0_, vehicles1_.Type as Type5_0_ from person person0_ left outer join vehicle vehicles1_ on person0_.id=vehicles1_.person_id where person0_.id=?
in java Vehicle-> setIdVehicle = 1
in java Person-> setId = 1
in java Vehicle-> setIdVehicle = 2
in java Person-> setName = Jochen
17-jun-2009 10:30:06 org.hibernate.LazyInitializationException <init>
SEVERE: illegal access to loading collection
org.hibernate.LazyInitializationException: illegal access to loading collection
at org.hibernate.collection.AbstractPersistentCollect ion.initialize(AbstractPersistentCollection.java:3 63)
...

vlad2005
Jun 18th, 2009, 04:50 PM
I don't know if this can be, but why use both annotation, one-to-many and CollectionOfElements. Documentation say that CollectionOfElements it's replacement for one-to-many.
Second, in relation many-to-one, i don't think that it's right to use CollectionOfElements, because in class Vehicle, person it's an object not an collection.
Try to change that, and see how it's work.

Dr.Drane
Jun 18th, 2009, 04:54 PM
I don't know if this can be, but why use both annotation, one-to-many and CollectionOfElements. Documentation say that CollectionOfElements it's replacement for one-to-many.
Second, in relation many-to-one, i don't think that it's right to use CollectionOfElements, because in class Vehicle, person it's an object not an collection.
Try to change that, and see how it's work.

Oh I see. Makes a lot of sense!
I used collection of elements because I had some kind of problem that seemed like Hibernate not knowing what kind of object I was mapping to. So I used it to make it clear what kind of object I was mapping to. But I think you might be right since it normally is used for non entity to entity mapping I guess.

Will try it tomorrow. Thank you very very much for being so kind to give me some advice! :)

Have a nice day.

Kind regards,

Jochen

vlad2005
Jun 18th, 2009, 05:15 PM
Probably, u have an bean that instantiate sessionFactory, where u set all hibernate parameters. There need to map and java classes. Personally, i use annotation, because it's more convenient, and i have an tag like this inside sessionFactory bean:


<property name="packagesToScan" value="spa.domain"/>

In this mode, i don't need to map every class. Spring scan for me :).

vlad2005
Jun 18th, 2009, 05:22 PM
I forgot, but if u want to specify what entity are mapped, u can use "targetEntity" property. Declaration for for @OneToMany it's:


public @interface OneToMany {
Class targetEntity() default void.class;
CascadeType []cascade() default {};
FetchType []fetch() default EAGER;
String mappedBy() default "";
}

Same idea for ManyToOne


public @interface ManyToOne {
Class targetEntity() default void.class;
CascadeType []cascade() default {};
FetchType []fetch() default EAGER;
boolean optional() default true;
}

Dr.Drane
Jun 19th, 2009, 03:42 AM
w00h00w! Thanks a lot vlad2005!

With you response I managed to solve the issue. :)
Apparently it was associated with me printing out the value of my vehicles variable:


public void setVehicles(Set<Vehicle> vehicles) {
//System.out.println("in java Person-> setVehicles = "+vehicles);

That also explains the "SEVERE: illegal access to loading collection"...i was trying to print out something before it was loaded actually.

And also many thanks for the "packagesToScan" tip!! Very handy since I still was using the annoying-to-manage "annotatedClasses" property:


<property name="annotatedClasses">
<list>
<value>com.prefabsoft.model.Article</value>

So big thumbs up for your help! You made my (fri)day feeling like already being weekend. :)

Greets,

Jochen

vlad2005
Jun 19th, 2009, 04:07 AM
you're welcome!