3
votes

I'm new to JTA and currently dive into its specification. I've also created some example projects to dive into this subject faster. I use IBM WebSphere 9 as a runtime.

I've created a simple project consists of EJB and MDB. The idea is that I sending some JMS to the queue, MDB then get this message, processing it and invoke EJB using local interface (both MDB and EJB are located at the same EAR). EJB in its turn will handle incoming object and write it to the Oracle database using JDBC via XA datasource.

MDB onMessage() method has a TransactionAttributeType.NOT_SUPPORTED defined, and as JTA says it should run outside the transaction context.

The process() method of EJB, which is invoked from MDB hasn't any TransactionAttributes defined, and as it comes from JTA it should have a default value which is TransactionAttributeType.REQUIRES_NEW. Thus, if I'm not wrong, it starts a new global TX when being invoked, or am I wrong?

I've also created a simple DAO class, which gets a JDBC connection and runs statement to store the data received from EJB. It is located in a plain Java class at the package next to EJB.

The problem occurs when I try to run the project, to be more specific it occurs when I'm trying to get a connection from the datasource. Since I use the XA datasource, XAER_PROTO exception occurs:

[7/17/18 16:32:52:771 GMT+01:00] 000001b4 WSRdbXaResour E DSRA0304E: XAException occurred. XAException contents and details are:
The XA Error is: -6 The XA Error message is: Routine was invoked in an improper context. The Oracle > Error code is: 24776 The Oracle Error message is: Internal XA Error

After some time spent on investigating this issue I've figured out, that this problem is possibly related to this statements from JTA specification:

3.4.7 Local and Global Transactions

...

When using the same connection to perform both local and global transactions, the following rules apply:

• The local transaction must be committed (or rolled back) before starting a global transaction in the connection.

• The global transaction must be disassociated from the connection before any local transaction is started.

So my questions are:

  1. Is an EJB method, annotated with a TransactionAttributeType.REQUIRES_NEW starts a global TX in terms of JTA?

  2. Am I right in my assumptions that retrieving of new JDBC connection from datasource initiates new local transaction in terms of JTA?

  3. If all above is right than is it actually possible to use plain JDBC in EJB under a global TX? Or I should invoke JDBC-related methods only from non-transactional EJBs?

  4. Should I consider the described above approach as a faulty one?

  5. Should I use more abstract JTA interfaces to work with database instead of use of "plain" JDBC methods? If so then which way would be preferable?

1

1 Answers

4
votes

To answer your questions:

1. Is an EJB method, annotated with a TransactionAttributeType.REQUIRES_NEW starts a global TX in terms of JTA?

Yes, REQUIRES_NEW causes the container to start a new global transaction.

2. Am I right in my assumptions that retrieving of new JDBC connection from datasource initiates new local transaction in terms of JTA?

You are almost right on this one. Retrieving a JDBC connection doesn't actually initiate a transaction. However, doing meaningful work on a JDBC connection (such as createStatement, execute, and so forth) initiates a local transaction, in the absence of a global transaction.

3. If all above is right than is it actually possible to use plain JDBC in EJB under a global TX? Or I should invoke JDBC-related methods only from non-transactional EJBs?

Plain JDBC is perfectly valid from an EJB, whether under a global transaction or not.

4. Should I consider the described above approach as a faulty one?

The scenario as you described ought to work fine. Most likely there is some additional detail that causing the trouble, possibly related to ordering or not committing.

5. Should I use more abstract JTA interfaces to work with database instead of use of "plain" JDBC methods? If so then which way would be preferable?

I would recommend debugging the cause of the problem you are seeing.

If I understand the scenario you described, you invoke getConnection twice, first within a global transaction due to EJB marked REQUIRES_NEW, and subsequently, after that method returns and the transaction commits, the second time you invoke getConnection and use it is for a new local transaction. You should clarify which getConnection attempt is failing, whether there are others, if possible posting snippets from your source code. You should also post the full stack of the error. Lacking these, it means the answers you get are more likely to be guesses than answers. For example, given the Oracle error, I could guess that you might have additional prior usage of the connection in a local transaction that is never committed before attempting to use the connection in a global transaction. It should also be noted that in an application server, connections are pooled, and so prior usage could be from a different thread that did not commit a connection in a local transaction. The application server does its best to detect this sort of thing and clean up for you, but it can't always. So you will also want to review your other usage of connections for any that might not be committing/rolling back under some condition.