Why Hibernate Eager Fetch Does Not Work?

2k views Asked by At

I was trying to write a simple testing program to simulate two objects in my project and run into trouble. Basically I have two simple object, Parent and Child with one to many relationship. Following are two objects:

import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.fhp.ems.common.data.dao.HibernateInitializer;


@Entity
@Table(name = "Parent")
public class Parent {

  private Integer id;
  private String data;
  private List<Child> child;

  public Parent() {}


  @Id
  @Column(name = "ID", nullable = false)
  public Integer getId() {
    return this.id;
  }

  public void setId(Integer id) {
    this.id = id;
  }


  @Column(name = "DATA", length = 128)
  public String getData() {
    return this.data;
  }


  public void setData(String data) {
    this.data = data;
  }


  @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, orphanRemoval = true)
  @JoinColumn(name = "PID")
  //@Fetch(value=FetchMode.SELECT)
  public List<Child> getChild() {
    return child;
  }


  public void setChild(List<Child> child) {
    this.child = child;
    if(child != null && child.size() > 0) {
      for(Child c : child) {
        this.data = c.getValue();
      }
    }
  }



  public static void main(String[] args) {
    Session session = null;
    Transaction tx = null;
    try {
      HibernateInitializer.initLocal();
      session = HibernateInitializer.getSession(null);
      int id = 101;
      tx = session.beginTransaction();
      Parent p = (Parent)session.get(Parent.class, 101);
      //Parent p = new Parent();
      //p.setId(id);
      //Child c = new Child();
      //c.setPid(id);
      //c.setId(1);
      //c.setValue("child");
      //Child c1 = new Child();
      //c1.setPid(id);
      //c1.setId(2);
      //c1.setValue("child1");
      //List<Child> childs = new ArrayList<Child>();
      //childs.add(c);
      //childs.add(c1);
      //p.setChild(childs);
      //session.saveOrUpdate(p);

      tx.commit();
    }
    catch(Exception exc) {
      exc.printStackTrace();
    }
    finally {
      HibernateInitializer.closeSession(session, null);
    }

  }

}


import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;


//CREATE TABLE `Child` (
//  `ID` int(11) NOT NULL,
//  `PID` int(11) NOT NULL,     
//  `VALUE` varchar(128) default NULL,
//  PRIMARY KEY  (`ID`)
//)


@Entity
@Table(name = "Child")
public class Child {

  private Integer id;
  private Integer pid;
  private String value;

  public Child() {}

  @Id
  @Column(name = "ID", unique = true, nullable = false)
  public Integer getId() {
    return this.id;
  }


  public void setId(Integer id) {
    this.id = id;
  }


  @Column(name = "PID", nullable = false)
  public Integer getPid() {
    return this.pid;
  }


  public void setPid(Integer pid) {
    this.pid = pid;
  }


  @Column(name = "VALUE", length = 128)
  public String getValue() {
    return this.value;
  }


  public void setValue(String value) {
    this.value = value;
  }


}

When I run the program, I get following error:

Hibernate: 
    select
        parent0_.ID as ID81_1_,
        parent0_.DATA as DATA81_1_,
        child1_.PID as PID81_3_,
        child1_.ID as ID3_,
        child1_.ID as ID82_0_,
        child1_.PID as PID82_0_,
        child1_.VALUE as VALUE82_0_ 
    from
        Parent parent0_ 
    left outer join
        Child child1_ 
            on parent0_.ID=child1_.PID 
    where
        parent0_.ID=? 

org.hibernate.PropertyAccessException: Exception occurred inside setter 
  of com.fhp.ems.common.data.dao.test.Parent.child
  at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:89)
  at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:583)
  at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:229)
  at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3822)
  at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:152)
  at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982)
  at org.hibernate.loader.Loader.doQuery(Loader.java:857)
  at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
  at org.hibernate.loader.Loader.loadEntity(Loader.java:2037)
  at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86)
  at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76)
  at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3268)
  at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496)
  at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477)
  at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
  at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
  at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
  at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
  at org.hibernate.impl.SessionImpl.get(SessionImpl.java:1005)
  at org.hibernate.impl.SessionImpl.get(SessionImpl.java:998)
  at com.fhp.ems.common.data.dao.test.Parent.main(Parent.java:98)
Caused by: java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:66)
  ... 20 more
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
  at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
  at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
  at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:122)
  at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
  at com.fhp.ems.common.data.dao.test.Parent.setChild(Parent.java:71)

If I change the earger fetch to lazy fetch, it works fine. If I use @Fetch(value=FetchMode.SELECT), it also works fine. My question is: why eager fetch does not work?

1

There are 1 answers

2
Luca Basso Ricci On

How can you have a @OneToMany when you don't have ParentId in child table?
You are missing a key to have a multiple children related to single parent; add ParentId to table Child and use that column in @JoinColumn(name = "ParentId")

EDIT:

  • Init Parent.child with private List<Child> child = new ArrayList<Child>();
  • Change private Integer pid; with private Parent pid; (and add to @OneToMane mappedBy="parent")
  • Try Annotate field and not setter
  • Make your Parent.setChild() just an assignment

That are - usually - the best pratice for a parent-child relationship