Use Case for UserTransactions in Java EE 6

There are some constructs in CDI which are available to injection per default but you do not see right out-of-the-box for what use case it can be good. Or if you have a use case you do not know how to use this bean.

I had the same problem with UserTransaction and now I share my knowledge about the topic.

I worked on a Java EE 6 project where I had the task to log the database transactions within the Oracle database as sessions.

This logging is useful for DBAs to see which queries can be a bottleneck in the database’s performance because they run too long or are too expensive.

As the original implementation of the solution is confidential (as always) I cannot go into deeper detail about the solution. In the database we used a procedure to save the data in the session but we needed to call this procedure every time we start a transaction to the database.

Note that a transaction can be more longer than a simple query because we have to do some business logic based on data from the database and have to save and load data in the mean-time too. So this is not a simple @Transactional transaction. It is not technical, it is functional.

Calling a procedure is fortunately not a big thing. You use your entity manager, create a parametrized query to fill in the data into your procedure and execute the query.

Query query = entityManager.createNativeQuery("CALL YOUR_PROCEDURE(?,?)");
query.setParameter(1, "APPLICATION_NAME");
query.setParameter(2, "METHOD_NAME");
query.executeUpdate();

As you can see, in this example procedure we need to set the name of the application and the name of the method which is executed. The name of the method in this particular case it the one where we start the transaction.

Currently this version is not a usable one — only if you have one method where the transactions start. That’s why I placed this code inside the body of an interceptor. Now we can annotate the required methods with the interceptor annotation and every time these methods are called the interceptor is executed and writes the required data automatically into the database using the procedure.

And to get the name of the method, you simply have to use the following code block:

@AroundInvoke
public Object trackSession(final InvocationContext ctx) throws Exception {
// ...
    query.executeUpdate();
// ...
}

So if I run this code in the application, I get the following exception:

Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:96)
    at org.jboss.as.jpa.container.QueryNonTxInvocationDetacher.executeUpdate(QueryNonTxInvocationDetacher.java:80)

As you can see, if you use an interceptor, you need a custom transaction.

For this case comes the UserTransaction handy. With it you can create a transaction when you need one.

To get the UserTransaction you simply need it to inject as a CDI resource, with the @Resource annotation:

@Resource
private UserTransaction userTransaction;

And to start and commit a transaction, you can do the following:

userTransaction.begin();

query.executeUpdate();

userTransaction.commit();

Sometimes it can happen, that you are already in a transaction when the interceptor gets executed, so you cannot create or commit the transaction because it can lead to errors.

For this, you can add a validation to check if you have a transaction and if you do not have only in this case create and commit one.

To see if you have an open transaction you have to look at the status of your UserTransaction object. If it is STATUS_NO_TRANSACTION then you can create a new transaction:

boolean started = false;

if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
    userTransaction.begin();
    started = true;
}

query.executeUpdate();

if (started && userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) {
    userTransaction.commit();
}

And this was it.

Conclusion

As you can see, a UserTransaction does not hide any magic behind itself. You can use it if you are in the need to create a new transaction — for example in an interceptor.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s