Hey Java folks,
today I will tell you about a problem I ran into and which kept me busy for two days now until I found a solution I can live with.
Intro:
I have a JPA entity which I want to save to two different databases at the same time. The use case is that I have a business database and an admin database. All fields of the entity are filled in the admin database but they only get filled into the business database after some time, because the data is critical for a specific period of time.
During startup of my application I create a @Singleton EJB which starts to generate some data inside the @PostConstruct method. This data is then saved to both databases, except that some fields are nulled for the business database.
Problem:
Since EJB 3.1 the container automagically handles transactions for all EJB business methods. This is not bad at all but it seems to produce a problem as soon as we try to write to different databases within the same transaction.
It throws errors like:
* ARJUNA012140: Adding multiple last resources is disallowed. Trying to add LastResourceRecord(XAOnePhaseResource
* Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Could not open connection
* Caused by: java.sql.SQLException: javax.resource.ResourceException: IJ000457: Unchecked throwable in managedConnectionReconnected()
* Caused by: javax.resource.ResourceException: IJ000461: Could not enlist in transaction on entering meta-aware object
* Caused by: javax.transaction.SystemException: IJ000356: Failed to enlist: java.lang.Throwable: Unabled to enlist resource, see the previous warnings.These errors looked very weired to me and I didn´t find very much about this topic on the internet.
Solution:
After fiddling around with it for some time I took the decision to try and handle the transactions myself. The first thing I had to do was to tell the container that the bean manages the transactions itself by adding @TransactionManagement(TransactionManagementType.BEAN) to the @Singleton bean. After that I added a save method, which was passed an EntityManager and the object I wanted to save.
This method looks like this:
1: private void save(EntityManager em, Bla bla) {
2: UserTransaction utx = sessionContext.getUserTransaction();
3: try {
4: // begin the transaction
5: utx.begin();
6: em.persist(bla);
7: utx.commit();
8: } catch (Exception e) {
9: // something went wrong, lets try to rollback the transaction
10: try {
11: utx.setRollbackOnly();
12: } catch (IllegalStateException ise) {
13: log.error(ise.getMessage());
14: } catch (SystemException se) {
15: log.error(se.getMessage());
16: }
17: log.error(„problem with the database transaction: “ + e.getMessage());
18: }
19: }
Finally I called this method twice with the corresponding parameters for the different databases / EntityManagers. This solution worked out for me really nice so far.
I hope that this has helped anybody who stumbled upon the same error. If you have got a nicer solution, just drop me a line here and I will be happy to try it out!
Maybe somebody even knows where this error directly comes from and knows how to fix it.
See ya,
w0mbat