Hello, I'm trying to keep transactions alive inside of a TRANSACTION typed transaction context long enough to test collection valued references of the query results. Right now I have a test case that makes 26 different transactions to the database, when I'm sure it should be more like 3 or 4.
The crux of my problem is that I have entities with numerous collection-valued references to other entities in my library. In my tests I want to load an entity, and each of its collection valued reference. An example would look something like this: ... > Party party = myBeanManager.findParty(id); > Assert.assertNotNull(party); > Set<AssociatedAddress> addresses = party.getAddresses(); > Set<Capabilities> caps = party.getCapabilities(); > Set<PartyRole> roles = party.getPartyRoles(); > Set<RegisteredIdentifiers> regIDs = party.getRegisteredIdentifiers(); > Set<Binding> prefs = party.getPreferences(); > Assert.assertNotNull(addresses); > Assert.assertNotNull(caps); > Assert.assertNotNull(roles); > Assert.assertNotNull(regIDs); > Assert.assertNotNull(prefs); > Assert.assertTrue(caps.size() > 0); > Assert.assertTrue(roles.size() > 0); > Assert.assertTrue(regIDs.size() > 0); > Assert.assertTrue(prefs.size() > 0); > Assert.assertTrue(addresses.size() > 0); > ... > However, since my stateless session bean is using a TRANSACTION type persistence context to get my Party object detaches it, so I get NPEs for the lower block of assertions. Just about all of my classes have cascades set up for persist and merge between the entity references, and I find if I use the EXTENDED type persistence context I get ConcurrentModificationExceptions. I initially tried to use OpenJPA-recommended FetchGroups but got ConcurrentModificationExceptions when I tried to merge objects. I found that I could avoid these if I set the FetchType on my field references to EAGER, but I also understand the danger in doing that and want to avoid it--it should the be the exception for almost all of my entities--not the rule. In the end the only thing I could think of to do was to call a transaction with a LEFT JOIN FETCH for each collection-valued reference in my object. Thus, the above code morphed into this ugly and slow code: ... > Party party; > > party = myBeanManager.findParty(id, "addresses"); > Set<AssociatedAddress> addresses = party.getAddresses(); > Assert.assertNotNull(party); > Assert.assertNotNull(addresses); > Assert.assertTrue(addresses.size() > 0); > > party = myBeanManager.findParty(id, "roles"); > Set<PartyRole> roles = party.getPartyRoles(); > Assert.assertNotNull(party); > Assert.assertNotNull(roles); > Assert.assertTrue(roles.size() > 0); > > party = myBeanManager.findParty(id, "capabilities"); > Set<Capability> caps = party.getCapabilities(); > Assert.assertNotNull(party); > Assert.assertNotNull(caps); > Assert.assertTrue(caps.size() > 0); > > party = myBeanManager.findParty(id, "preferences"); > Set<Binding> prefs = party.getPreferences(); > Assert.assertNotNull(party); > Assert.assertNotNull(prefs); > Assert.assertTrue(prefs.size() > 0); > > party = myBeanManager.findParty(id, "registeredIdentifiers"); > Set<RegisteredIdentifier> regIDs = party.getRegisteredIdentifiers(); > Assert.assertNotNull(party); > Assert.assertNotNull(regiIDs); > Assert.assertTrue(regIDs.size() > 0); > ... > The code above uses five connections to the database for what should be done in one. Although my tests work, they're needlessly slow and I know this isn't how things are supposed to be done. So, before I can continue testing the other modules in my library I want to get these tests streamlined. I showed the code to Dain and he had some suggestions. I blogged that conversation here: http://blog.lib.umn.edu/saintx/eremite/2008/01/fault_loading_and_usertransact.html Dain mentioned an example in the itests module for the OpenEJB project that does something like this, but his example was written for version 2.1 entity beans, and the beans were gotten via the LocalHome interfaces for the bean object--I don't know this example is the best one for what I'm trying to do. At Dain's suggestion I looked into the UserTransaction technique--it is covered in the EJB 3 book by O'Reilly. However, Container Managed Transaction beans cannot use UserTransaction. I also have been attempting different ways of moving my test code into the session bean. I tried implementing a visitor pattern that I could push into the Session bean's finder methods. Here's one example of that: public class PartyCascadeTest extends PersistenceTest { > public void testPartyCascade() { > > Party initial = new Party(); > mgr.add(initial); > > Visitor<Party> visitor = new PartyVisitor<Party>(); > mgr.check("Party", visitor); > > } > > private class PartyVisitor<A extends Party> implements Visitor<A> { > public void test(List<A> list) { > Assert.assertNotNull("party list should not be null", list); > Assert.assertTrue(list.size() == 1); > Party p = list.get(0); > Set<PartyRole> roles = p.getPartyRoles(); > Assert.assertNotNull("roles should not be null", roles); > } > } > } > The operant part of the session bean looks like this: public void check(String table, Visitor visitor) { > Query query = entityManager.createQuery("Select a from " + table + > " as a"); > List<Party> l = query.getResultList(); > Assert.assertNotNull(l); > > visitor.test(l); > } > However, I get NullPointerExceptions in that call to visitor.test(). I tried sending "visitor.test(query.getResultList())", but it appears by the time I get that result list the transaction is already over and the object is detached. So, I can't do fault loading on any of the objects. Now I'm trying a different method--really just grasping at straws. I'm trying to get the EntityManager from directly inside of the unit test and explicitly control the transaction manager. That isn't working either--I get NPE on the transaction manager after trying to look it up. Here's my experimental test class: public class EfficientPartyCascadeTest extends PersistenceTest { > > private TransactionManager txMan; > private EntityManagerFactory emf; > private EntityManager em; > > public void beginTransaction() throws Exception { > txMan.begin(); > } > > protected void completeTransaction() throws Exception { > int status = txMan.getStatus(); > if (status == Status.STATUS_ACTIVE) { > txMan.commit(); > } else if (status != Status.STATUS_NO_TRANSACTION) { > txMan.rollback(); > } > } > > public void setUp() throws Exception { > super.setUp(); > try { > Assert.assertNotNull("context", context); > txMan = (TransactionManager) context.lookup > ("java:openejb/TransactionManager"); > } catch (NamingException e) { > e.printStackTrace(); > } > Assert.assertNotNull("transactionManager", txMan); // This fails > at the moment--can't find the transaction manager > > emf = Persistence.createEntityManagerFactory("party-test-unit"); > em = emf.createEntityManager(); > } > > public void test02_ASetBDropExisting() throws Exception { > Party initial = new Party(); > initial.setName("Initial"); > mgr.add(initial); // TODO: +1 unit of work > > beginTransaction(); > try { > Party party = findParty(initial.getID()); > Assert.assertNotNull(party); > } finally { > completeTransaction(); > } > } > > private Party findParty(long ID) { > return em.find(Party.class, ID); > } > } I know that EM injection doesn't work for unit tests at present. Like I said before, I can just make another round trip to the db every time I need a collection-valued reference, but it's bad for business. Ideas? Suggestions? Regards, -- Alex
