Hi there,
I've got a (most probably) unusual case of a bidirectional association and run into trouble getting it out of the 2nd level cache. I tested the code with hibernate versions 3.5.0 and 3.6.2. Tested cache implementations are JBossCache and EHCache.
Here is the - shortened - code of the classes:
Code:
@Embeddable
public class ParentId implements Serializable{
private int id;
}
@Embeddable
public class ChildId implements Serializable{
private int id;
private Parent parent;
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Parent.class)
@JoinColumn(name = "parent_id", nullable = false, insertable = false, updatable = false)
public Parent getParent() {
return parent;
}
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "myCache")
public class ChildElement {
private ChildId id = new ChildId();
private String name = "unknown child";
}
public class Parent {
private ParentId id = new ParentId();
private List<ChildElement> childElements = new ArrayList<ChildElement>();
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "id.parent", targetEntity = ChildElement.class) @IndexColumn(name="child_id") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "myCache")
public List<ChildElement> getChildElements() {
return childElements;
}
@Override
public boolean equals(Object obj) {
Parent other = (Parent) obj;
if (childElements == null) {
if (other.childElements != null)
return false;
} else if (!childElements.equals(other.childElements)) return false;
return true;
}
}
public static void main(String[] args) {
boolean run = true;
while (run) {
Session session = InitSessionFactory.getInstance().openSession();
Transaction tx = session.beginTransaction();
tx = session.beginTransaction();
Parent p = (Parent) session.get(Parent.class, new ParentId(0));
ChildElement c = p.getChildElements().get(0); }
}
The above code shows the target implementation, comments indicate tested variations. Running this code the second pass @[6] causes an infinite recursion when hibernate tries to fetch the list from the second level cache. The callstack shows that Parent.equals() is called in this recursion.
Variants that work:
- Remove the list from equals() [5]. This would be possible, but there might be a need for any "equality" function in the future.
- Replace the bidirectional association with a unidirectional ([0] with [1]; remove all "Parent" code from ChildId). This is possible as well, but it makes the access a more inconvienient.
- Remove the list from the cache. Is possible for sure, but that is counterproductive.
So each variant has its own disadvantages (in my opinion). My current favourite is b).
Finally my questions:
- Is this a "bug or a feature"?
- Am I violating any "best practices"?
- Is there any other proposal that solves the problem (bidir. assoc., index and cache)?
Cheers,
mello