I'm working Glassfish 2.1 with EcipseLink 2.0.0, so really using JPA 1.0 specification, and I have a stateless EJB that finds entities among other things. As far as i know JPA 1.0 defines a L1 cache that works at Persistence Context level (transaction level for stateless EJBs) but I can't figure out why the next code prints "Not same instance" if it's within the same transaction.
THIS is extremely weird, object identity should definitely be maintained inside a transaction in a Java EE context. This is very well documented in the JPA wiki book:
Object identity in Java means if two
variables (x, y) refer to the same
logical object, then x == y returns
true. Meaning that both reference the
same thing (both a pointer to the same
memory location).
In JPA object identity is maintained
within a transaction, and (normally)
within the same EntityManager. The
exception is in a JEE managed
EntityManager, object identity is only
maintained inside of a transaction.
So the following is true in JPA:
Employee employee1 = entityManager.find(Employee.class, 123);
Employee employee2 = entityManager.find(Employee.class, 123);
assert (employee1 == employee2);
This holds true no matter how the
object is accessed:
Employee employee1 = entityManager.find(Employee.class, 123);
Employee employee2 = employee1.getManagedEmployees().get(0).getManager();
assert (employee1 == employee2);
In JPA object identity is not
maintained across EntityManagers. Each
EntityManager maintains its own
persistence context, and its own
transactional state of its objects.
So the following is true in JPA:
EntityManager entityManager1 = factory.createEntityManager();
EntityManager entityManager2 = factory.createEntityManager();
Employee employee1 = entityManager1.find(Employee.class, 123);
Employee employee2 = entityManager2.find(Employee.class, 123);
assert (employee1 != employee2);
Object identity is normally a good
thing, as it avoids having your
application manage multiple copies of
objects, and avoids the application
changing one copy, but not the other.
The reason different EntityManagers or
transactions (in JEE) don't maintain
object identity is that each
transaction must isolate its changes
from other users of the system. This
is also normally a good thing, however
it does require the application to be
aware of copies, detached objects and
merging.
Some JPA products may have a concept
of read-only objects, in which object
identity may be maintained across
EntityManagers through a shared object
cache.
And I couldn't reproduce the problem with EclipseLink 2.0 in a Java SE environment (within a transaction and the same EntityManager
) - sorry I won't test under GF 2.1.
I even tried with the property: <property name="eclipselink.cache.type.default" value="Full"/>
in the persistence.xml
file, but does the same
There is nothing to "activate" for the L1 cache.
What I would really like to achieve, if possible, is that multiple calls to my stateless EJB return the same instance, in other words span the JPA cache life across transactions and Persistence Contexts using stateless EJBs (...):
A L2 cache is indeed a cache that spans multiple transactions and EntityManagers and L2 caching is supported by most JPA providers. But while L2 caching will reduce database hits, object identity is not guaranteed with all providers.
For example, with Hibernate, the L2 cache isn't enabled by default and you won't get object identity as Hibernate doesn't put the entities themselves in cache.
With EclipseLink, L2 cache is enabled by default and you'll get object identity depending on the cache type. The default is the SOFT-WEAK cache of size 100 and it does preserve object identity. While you can configure things very finely (down to the entity level), for distributed environment or not, things should work by default.
See also