Hello everyone,
I am using EclipseLink 1.0.1 and have the default implicit
copy on read behavior enabled in my JDK 1.5 environment. What I have recently
discovered was that copies of objects made previously are not being reused in
certain situations, and I would like to create as few copies of my mapped
objects as possible to reduce heap needed to hydrate and interrogate a given
domain object graph.
Suppose I have the following simplified domain model with a
one to many from C to A and a back pointing many to one from A to C :
public class C
{
@Id
private int id;
@OneToMany(fetch=LAZY,
mappedBy="c", cascade={ALL})
private List<A> as;
…
}
public class A
{
@ManyToOne(fetch=LAZY)
@JoinColumn(name="C_FK")
private C c;
public C getC() { return c; }
…
}
Further, suppose I find a given instance of C with a simple
primary key based JQPL query as depicted in the following code snippet :
EntityManager em = getEntityManager();
Query q = em.createQuery("SELECT c FROM C c
WHERE c.id = :id");
q.setParameter("id", idOfInterest);
C result = (C) q.getSingleResult();
em.close();
1. After the call to em.close(), if I interrogate the A
instances of the resulting C instance returned, I see that the C instance returned
by each A instance’s getC() method is not the same as the original C
instance returned. i.e. someA.getC() == originalC is false. This I expect
since the C instance becomes detached at the moment the EntityManager is
closed.
2. However, if I instead interrogate the C’s A
instances and perform the same interrogation prior to the em.close() call, I
see the same results, which I am not expecting. I would expect that calling
getC() on an instance of A would return the C result instance since the
EntityManager is still open at that point in time and thus the C result
instance is not yet detached.
3. Further, I find that if I mark both relationships as
being EAGER, then all of the A instance’s getC() calls do in fact return
the same C result reference. This happens when the interrogation happens prior
to the em.close() call, and I would expect the same result after the C instance
is detached since the relationships are fully hydrated.
Are all three of these test results to be expected?
I now need to further refine my question. In my domain
model, there is also a OneToMany from A to B, and a back pointing ManyToOne
from B to A. In terms of a typical object graph, a given C instance may have 30
A instances and of all of those A instances, could have hundreds or thousands
of B instances in total. In my test object graph, the C instance has some 80 or
so A instances and some 3000 or so B instances.
I could instead use Fetch Joins to express the desire to eagerly
load the A and B instances, and leave those annotations specifying lazy
loading, which of course would give me better control for eagerly loading those
relationships on a use case by use case basis. However, that approach concerns
me due to the “performance implications” noted at the top of page
206 of Mike Keith’s and Merrick Schincariol’s Pro EJB 3 book.
Additionally, I have found that this technique also results in the back
pointing C reference to be a different C reference than that originally read.
If I change the JPQL to instead look like :
SELECT c FROM C c JOIN c.as a JOIN FETCH c.as JOIN
FETCH a.c JOIN FETCH a.bs WHERE c.id = :id
I do not see the same results as I saw in point 3 above, but
the query does run fine otherwise. The same is true if I eliminate the JOIN
FETCH a.c part of the above query as well. I should note that I have
attempted this with batch reading being used for the A’s B instances as
well.
Have I exhausted the techniques currently available to avoid
the creation of “extra” domain object copies?
Thank you,
Doug Gschwind