3

I've got an integration test of a DAO in which I use a shared EntityManager (via Spring, using SharedEntityManagerCreator). The test class is marked as @Transactional, as is the DAO method under test.

In both the test class and the DAO I'm retreiving a User entity as follows:

User user = em.find(User.class, "test");

In the setup of my test I've modified the user object, but I wasn't seeing the modification in the DAO when the test came to run. It turned out that the two references did not refer to the same object; I proved this in my test class using:

System.out.println("User objects equal = " + (user == dao.getUser()));

This printed out false. I would expect that every call to an EntityManager using the same key would return the same object reference, and was surprised (and a bit alarmed!) to find out this was not the case. Can anyone shed any light on this? I've refactored my code so it's not actually an issue (the DAO shouldn't have had the User object in it anyway) but I'd still like to understand this better.

Thanks!

Java 1.6u22, Toplink Essentials 2.0.1, Spring 2.5.6

Conan
  • 2,288
  • 1
  • 28
  • 42

2 Answers2

6

find() returns the same instance inside a scope of persistence context.

In the case of shared EntityManager (container-managed transaction-scoped persistence context, in JPA Spec terms) lifecycle of persistence context is bound to the transaction, therefore find() returns the same instance when called from the same transaction. I guess in your case setup of your test doesn't happen in the same transaction as a test method, so find() produces different instances.

axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 1
    axtavt is correct. When the EntityManager is closed, the persistence context associated with it is garbage collected. If your instance of the EntityManager is supplied automatically by the container along with the transaction, then your calls to em.find() will always return the same physical object for the same primary key as long as the same transaction (and EntityManager) are still open. If you need an EntityManager with a longer lifespan then you have the option of using an application-managed EntityManager instead of a container-managed one, but with some extra work involved. – Jim Tough Nov 20 '10 at 02:17
  • I see, thanks for your responses. I'm sure there's only one transaction shared by both my test and my dao, as methods annotated @Before and @After are included by the @Transactional annotation; The Toplink logs also show a single transaction. So I guess I must not be getting the same EntityManager in both the test class and the DAO. Is there a way of finding out whether two EntityManagers are the same? I know the shared EntityManager that Spring gives me is actually a proxy which delegates the calls, so I can't see an obvious way of checking. – Conan Nov 22 '10 at 10:09
  • @Conan: I checked it and found out that `@Before`/`@After` methods are actually executed in the same transaction as `@Test`, so `em.find()` returns the same instances. Perhaps your problem has another cause. You can log `TransactionSynchronizationManager.getCurrentTransactionName()` to check transaction identity. – axtavt Nov 22 '10 at 12:28
  • @Conan: Also I assume that your tests are Spring-enabled, as described here http://static.springsource.org/spring/docs/2.5.x/reference/testing.html#integration-testing. Otherwise `@Transactional` in test classes doesn't have any effect. – axtavt Nov 22 '10 at 15:37
  • @avtaxt: Yes, my tests are Spring-enabled, I've been poring over the debug output. Thanks for your suggestion, I wasn't aware it was possible to get hold of the transaction in that way; I'll try it and see if I can glean some more information. I have a suspicion that the SharedEntityManagerCreator may be failing to pick up the existing thread-bound EntityManager (perhaps it's a different thread for some reason) and so is returning a fresh one, in which case I'd understand my User object references not matching, as they'd be coming from different peristence contexts. – Conan Nov 24 '10 at 16:24
0

No it does not. You should rely on object EQUALITY instead of IDENTITY anyway. Override the equals method.

willcodejavaforfood
  • 43,223
  • 17
  • 81
  • 111