I am using JPA for the first time, on a WebSphere Commerce 7 environment. I was able to get it all working fine using container managed persistence, and so it saves the objects on its own volition. The problem is, I need to make sure the data gets saved to the DB before the end of the logic in which the transaction exists, because there is more logic at the end that could introduce a race condition if the data is not committed to the database before hand. If I add debug points at the point I call entityManager.persist
, and also after the point I call entityManager.close
, I can query the DB and see no data yet there. But then the data shows as soon as I let the logic run its course and get to the end of the class.
So I figured maybe I need to try application managed persistence. As I read about how to do it, I am getting pretty confused, because it looks like I was already structuring my logic in that way unintentionally, and yet it still is not allowing me to manage the transaction. And also everything I try results in some exception, especially when I try calling getTransaction.commit()
or anything of the sort to manually commit. So here is what I had that worked using CMT (or at least I think it was CMT)
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="NIJAVA" transaction-type="JTA">
<jta-data-source>jdbc/com/ni/jndi</jta-data-source>
<class>com.ni.commerce.nios.order.objects.NiPnAttributes</class>
<class>com.ni.commerce.nios.order.objects.NiosAddresses</class>
<class>com.ni.commerce.nios.order.objects.NiosContacts</class>
<class>com.ni.commerce.nios.order.objects.NiosHeaders</class>
<class>com.ni.commerce.nios.order.objects.NiosLines</class>
<class>com.ni.commerce.nios.order.objects.NiosPayment</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
Then I have created a JpaConnectionHelper that I hoped to make into something I could pass a value to and have it create a specific persistence unit for me based on the String I pass in. It is because of this that I seem to be doing logic that appears to already be the type of logic you need for application managed transactions. In order to make that work, I have to call Persistence.createEntityManagerFactory(persistenceUnitName)
in order to create An EntityManagerFactory
since i am passing the persistenceUnitName into the class. And the stuff I read tells me this means I am going to be doing application managed - but maybe I am wrong on this. Anyway, the core of that class is this:
public class JpaConnectionHelper {
private EntityManagerFactory emFactory;
private EntityManager entityManager;
private void createEntityManager(String persistenceUnitName) throws ECException {
setEmFactory(Persistence.createEntityManagerFactory(persistenceUnitName));
setEntityManager(getEmFactory().createEntityManager());
}
public EntityManager getEntityManager() {
return entityManager;
}
}
The getter gives me access to the Entitymanager whenever I pass this object to other classes - maybe this is my mistake, in trying to create this helper class rather than just creating the persistence logic closer to where I am persisting data?
I then proceed to create a bunch of DAOs that return an object that has all the persistence annotations to map to my database. I pass in the instance of the JpaConnectionHelper to each DAO, so now they all have access to the EntityManager and the objects to be saved. I also give all the DAOs a .persistDao()
method that will use the EntityManager to call .persist()
for the objects that were created.
So now I basically have a DAO with the populated object I want to save, and an EntityManager. So I call the persist method on the DAO, and it saves just fine.
Here is how that all looks in action from the calling class:
// creates the EntityManager instance
JPAConnectionHelper jpaConnectionHelper = new JPAConnectionHelper();
// creates the object I want to save to the DB
NiosHeadersDao niosHeadersDao = zwebDaoFactory.getNiosHeadersDao(jpaConnectionHelper);
// this method is simply calling EntityManager.persist(niosHeaders);
niosHeadersDao.persistNiosHeaders();
// close EntityManager (if I try to close the factory it throws exception btw)
zwebDaoFactory.endConnectionScope(jpaConnectionHelper);
So I want to try and make that work with application managed transactions. I start by simply trying to call entityManager.flush()
after the persist method calls, but that does nothing (no exception either). I then try to add entityManager.getTransaction.commit()
in the same place, and that throws an exception as you might expect:
You cannot access the EntityTransaction when using managed transactions.
Now correct me if I am wrong, but doesn't this indicate I am using CMT? And if so, why am I able to when I am not using injection for my EntityManagerFactory
or Entitymanager
? BTW, I tried versions of that with and without first calling joinTransaction()
So I read up more, and find I might need to inject a UserTransaction, so I try to add this to the JPAConnectionHelper (where the EntityManager instance lives)
@Resource
private UserTransaction userTransaction;
But that simply results in a null userTransaction at all times. I even tried creating it from the InitialContext calling .lookup()
but there was no UserTransaction on there as far as I saw. I tried these attempts:
InitialContext ctx = new InitialContext();
UserTransaction tx = (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
UserTransaction tx = (UserTransaction) ctx.lookup("java:comp/UserTransaction");
That resulted in NamingExceptions for each (don't laugh, this stuff was new to you once too...)
So I cannot even get a UserTransaction with which to beginTransaction()
or commit()
.
As a test, I also tried using injection to get my EntityManagerFactory and/or EntityManager objects, but they all ended up null as well. So I am doing something so wrong that these attempts at injection are not succeeding in the least.
Can anyone help me? Thanks if you made it this far...
I found a solution that gave me access to the transaction specifically in a WebSphere environment using:
TransactionManager.commit();
It seems that this method is able to get access to the UserTransaction context in a way that I did not try. So I am sure I could do that too now if needed, but hey if this class does it for me, why not just use that