Prevent rollback transactions in JBoss + Hibernate

We have a Java application running on JBoss 5.1, and in some cases we need to prevent the transaction from being closed if a JDBCException is raised by some basic method.

We have an EJB method that looks like this:

 @PersistenceContext(unitName = "bar") public EntityManager em; public Object foo() { try { insert(stuff); return stuff; } (catch PersistenceException p) { Object t = load(id); if (t != null) { find(t); return t; } } } 

If insert fails due to a PersistenceException (which wraps a JDBCException caused by a constraint violation), we want to continue execution with load inside the same transaction.

We cannot do this right now because the transaction is closed by the container. Here is what we see in the magazines:

 org.hibernate.exception.GenericJDBCException: Cannot open connection javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Cannot open connection at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614) ... Caused by: javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: 7f000101:85fe:4f04679d:182 status: ActionStatus.ABORT_ONLY > 

The EJB class is marked with the following annotations

 @Stateless @TransactionManagement(TransactionManagementType.CONTAINER) @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 

Is there any proper way to prevent transaction switching only in this particular case?

+7
source share
2 answers

You really should not try to do this. As mentioned in another answer, and quoting Hibernate Docs, no exception thrown by Hibernate should be considered as recoverable. This can lead to some difficult search / debugging tasks, especially with an automatic crash in sleep mode.

A clean way to solve this problem is to check these limitations before inserting an object. Use the query to check if the database restriction is violated.

 public Object foo() { if (!objectExists()) { insertStuff(); return stuff(); } // Code for loading object... } 

I know this seems a little painful, but the only way you probably know which restriction has been violated (you cannot get this information from Hibernate exceptions). I believe this is the cleanest solution (the safest, at least).


If you still want to recover from the exception, you will have to make some changes to your code.

As already mentioned, you can manage transactions manually, but I do not recommend doing this. The JTA API is really cumbersome. Also, if you use Bean Managed Transaction (BMT), you will have to manually create transactions for each method in your EJB, all or nothing.

On the other hand, you can reorganize your methods so that the container uses a different transaction for your request. Something like that:

 @Stateless public class Foo { ... @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public Object foo() { try { entityManager.insert(stuff); return stuff; } catch (PersistenceException e) { if (e.getCause() instanceof ConstraintViolationException) { // At this point the transaction has been rolled-backed. // Return null or use some other way to indicate a constrain // violation return null; } throw e; } } // Method extracted from foo() for loading the object. public Object load() { ... } } // On another EJB @EJB private Foo fooBean; public Object doSomething() { Object foo = fooBean.insert(); if (foo == null) { return fooBean.load(); } return foo; } 

When foo () is called, the current transaction (T1) will be suspended and the container will create a new one (T2). When an error occurs, T2 will be deployed and T1 will be restored. When load () is called, it will use T1 (which is still active).

Hope this helps!

+5
source

I do not think that's possible.

In may depend on your JPA provider, but, for example, Hibernate explicitly states that any exception leaves the session in an inconsistent state and, therefore, cannot be considered as recoverable ( 13.2.3 Exception handling ).

I think the best thing you can do is turn off automatic transaction management for this method and create a new transaction after the exception manually (using UserTransaction , as far as I remember).

+2
source

All Articles