I am quite new to JPA/Hibernate (Java in general) so my question is as follows (note, I have searched far and wide and have not come across an answer to this):
I have two entities:
Parent and Child (naming changed).
Parent contains a list of Children and Children refers back to parent.
e.g.
@Entity
public class Parent {
@Id
@Basic
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "PARENT_ID", insertable = false, updatable = false)
private int id;
/* ..... */
@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID", nullable = true)
private Set<child> children;
/* ..... */
}
@Entity
public class Child {
@Id
@Basic
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "CHILD_ID", insertable = false, updatable = false)
private int id;
/* ..... */
@ManyToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID")
private Parent parent;
/* ..... */
}
I want to be able to do the following:
Retrieve a Parent entity which would contain a list of all its children (List), however, when listing Parent (getting List, it of course should omit the children from the results, therefore setting FetchType.LAZY.
Retrieve a Child entity which would contain an instance of the Parent entity.
Using the code above (or similar) results in two exceptions:
Retrieving Parent: A cycle is detected in the object graph. This will cause infinitely deep XML...
Retrieving Child: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: xxxxxxxxxxx, no session or session was closed
When retrieving the Parent entity, I am using a named query (i.e. calling it specifically) @NamedQuery(name = "Parent.findByParentId", query = "SELECT p FROM Parent AS p LEFT JOIN FETCH p.children where p.id = :id")
Code to get Parent (i.e. service layer):
public Parent findByParentId(int parentId) {
Query query = em.createNamedQuery("Parent.findByParentId");
query.setParameter("id", parentId);
return (Parent) query.getSingleResult();
}
Why am I getting a LazyInitializationException event though the List property on the Parent enti开发者_C百科ty is set as Lazy (when retrieving the Child entity)?
Exception 1. Your XML serialization does not have anything with JPA/Hibernate, you will be able to serialize your two-way-setup successfully. If automatic serialization fails, preprocess your to-be serialized objects in such a way that you remove the link from child to parent - outside the transaction (assuming you have automatic merges in your container).
Exception 2. You should read up on Transactions. In short, there are rules to when traversal of objects requiring database interaction can be done and not. Logically this divide must exist. If you want lazy relationships to behave as 'normal' (read: fetched) outside the transaction session, you simple 'prefetch' the relationship by traversing or accessing it in such a way that the relationship is resolved. For example call .size, or iterating over the members, on a set or a list will do this.
ADDED
Your mapping is a bit strange. What your mapping describe are two different one directional Releation ships:
- Parent -- 1:n -> Child
- Child -- n:1 -> Parent
which both are independend, but are stored in the same Database column.
I guess that this is not want you want, I guess that you want to have one bidirectional relation ship. The easyest way is to change the Parent's child Set mapping, to use mappedBy
instead of @JoinColumn
:
@Entity
public class Parent {
...
/* fetch = FetchType.LAZY is default in one:many */
@OneToMany(cascade = { CascadeType.ALL }, mappedBy="parent")
private Set<child> children;
...
}
From what you described in the comments of my old answer, this should fix the problem. Because if you now naviagate Parant -> Child -> Parent, both parent will be the same, and the persistence provider know it.
Beware of the fact, that the relationship now is only maintained on the child side.
OLD
When you get a LazyInitializationException
then the problem is that the not loaded entity is not longer attached to the EntityManager. In most cases the problem is that the transaction is closed, before the entity is loaded.
You can do fore things:
- keep the transaction open
- load the entity before you close the transaction
- switch from lazy to eager load
- fix your mapping (see: the added part of my answer)
I had the same problem. In fact I have a unidirectional relation (exp: Department has many Employee). So Employee will have a foreign key on Department. Once generated, Hibernate do a bidirectional relation with ManyToOne and OneToMany. So check weather you relation is uni or Bi-directional. In the first case, you need to remove the mapping oneToMany (from Department)
Hope that it will help.
精彩评论