HHH-9760 - TransactionImpl itself should not be throwing new TransactionExceptions

This commit is contained in:
Steve Ebersole 2015-05-08 13:03:42 -05:00
parent f8186e10c2
commit 88a6e4ba27
26 changed files with 142 additions and 126 deletions

View File

@ -506,9 +506,9 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
}
@Override
public void afterTransactionCompletion(boolean successful) {
public void afterTransactionCompletion(boolean successful, boolean delayed) {
afterTransaction();
owner.afterTransactionCompletion( successful );
owner.afterTransactionCompletion( successful, delayed );
}
@Override
@ -541,7 +541,7 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
* JDK deserialization hook
*
* @param ois The stream into which to write our state
* @param JdbcSessionOwner The Jdbc Session owner which owns the JdbcCoordinatorImpl to be deserialized.
* @param owner The Jdbc Session owner which owns the JdbcCoordinatorImpl to be deserialized.
*
* @return The deserialized JdbcCoordinatorImpl
*

View File

@ -80,9 +80,6 @@ public class TransactionImpl implements Transaction {
try {
this.transactionDriverControl.commit();
}
catch (Exception e) {
throw new TransactionException( "commit failed", e );
}
finally {
invalidate();
}
@ -100,9 +97,6 @@ public class TransactionImpl implements Transaction {
try {
this.transactionDriverControl.rollback();
}
catch (Exception e) {
throw new TransactionException( "rollback failed", e );
}
finally {
invalidate();
}

View File

@ -47,6 +47,6 @@ public interface TransactionObserver {
*
* @param successful Was the transaction successful?
*/
public void afterCompletion(boolean successful);
public void afterCompletion(boolean successful, boolean delayed);
}

View File

@ -335,9 +335,9 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
@Override
public void afterCompletion(boolean successful) {
afterTransactionCompletion( successful );
if ( isOpen() && autoCloseSessionEnabled ) {
public void afterCompletion(boolean successful, boolean delayed) {
afterTransactionCompletion( successful, delayed );
if ( !isClosed() && autoCloseSessionEnabled ) {
managedClose();
}
}
@ -2248,7 +2248,7 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
@Override
public void beforeTransactionCompletion() {
LOG.trace( "before transaction completion" );
LOG.tracef( "SessionImpl#beforeTransactionCompletion()" );
flushBeforeTransactionCompletion();
actionQueue.beforeTransactionCompletion();
try {
@ -2260,9 +2260,8 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
}
@Override
public void afterTransactionCompletion(boolean successful) {
LOG.trace( "after transaction completion" );
public void afterTransactionCompletion(boolean successful, boolean delayed) {
LOG.tracef( "SessionImpl#afterTransactionCompletion(successful=%s, delayed=%s)", successful, delayed );
persistenceContext.afterTransactionCompletion();
actionQueue.afterTransactionCompletion( successful );
@ -2280,8 +2279,10 @@ public final class SessionImpl extends AbstractSessionImpl implements EventSourc
LOG.exceptionInAfterTransactionCompletionInterceptor( t );
}
if ( shouldAutoClose() && !isClosed() ) {
managedClose();
if ( !delayed ) {
if ( shouldAutoClose() && !isClosed() ) {
managedClose();
}
}
if ( autoClear ) {

View File

@ -774,9 +774,8 @@ public class StatelessSessionImpl extends AbstractSessionImpl implements Statele
}
@Override
public void afterTransactionCompletion(boolean successful) {
if ( shouldAutoClose()
&& !isClosed() ) {
public void afterTransactionCompletion(boolean successful, boolean delayed) {
if ( shouldAutoClose() && !isClosed() ) {
managedClose();
}
}

View File

@ -55,8 +55,9 @@ public interface JdbcSessionOwner {
* An after-completion callback to the owner.
*
* @param successful Was the transaction successful?
* @param delayed Is this a delayed after transaction completion call (aka after a timeout)?
*/
public void afterTransactionCompletion(boolean successful);
public void afterTransactionCompletion(boolean successful, boolean delayed);
public void flushBeforeTransactionCompletion();
}

View File

@ -171,9 +171,9 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC
final int statusToSend = successful ? Status.STATUS_COMMITTED : Status.STATUS_UNKNOWN;
synchronizationRegistry.notifySynchronizationsAfterTransactionCompletion( statusToSend );
transactionCoordinatorOwner.afterTransactionCompletion( successful );
transactionCoordinatorOwner.afterTransactionCompletion( successful, false );
for ( TransactionObserver observer : observers ) {
observer.afterCompletion( successful );
observer.afterCompletion( successful, false );
}
invalidateDelegate();
}

View File

@ -304,7 +304,8 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy
public void beforeCompletion() {
try {
transactionCoordinatorOwner.beforeTransactionCompletion();
}catch (Exception e){
}
catch (Exception e) {
physicalTransactionDelegate.markRollbackOnly();
}
synchronizationRegistry.notifySynchronizationsBeforeTransactionCompletion();
@ -315,19 +316,26 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy
}
@Override
public void afterCompletion(boolean successful) {
public void afterCompletion(boolean successful, boolean delayed) {
if ( !transactionCoordinatorOwner.isActive() ) {
return;
}
final int statusToSend = successful ? Status.STATUS_COMMITTED : Status.STATUS_UNKNOWN;
synchronizationRegistry.notifySynchronizationsAfterTransactionCompletion( statusToSend );
transactionCoordinatorOwner.afterTransactionCompletion( successful );
// afterCompletionAction.doAction( this, statusToSend );
transactionCoordinatorOwner.afterTransactionCompletion( successful, delayed );
for ( TransactionObserver observer : observers ) {
observer.afterCompletion( successful );
observer.afterCompletion( successful, delayed );
}
if ( physicalTransactionDelegate != null ) {
physicalTransactionDelegate.invalidate();
}
physicalTransactionDelegate = null;
synchronizationRegistered = false;
}

View File

@ -68,13 +68,17 @@ public class SynchronizationCallbackCoordinatorNonTrackingImpl implements Synchr
@Override
public void afterCompletion(int status) {
doAfterCompletion( JtaStatusHelper.isCommitted( status ) );
log.tracef( "Synchronization coordinator: afterCompletion(status=%s)", status );
doAfterCompletion( JtaStatusHelper.isCommitted( status ), false );
}
protected void doAfterCompletion(boolean successful) {
protected void doAfterCompletion(boolean successful, boolean delayed) {
log.tracef( "Synchronization coordinator: doAfterCompletion(successful=%s, delayed=%s)", successful, delayed );
try {
target.afterCompletion( successful );
}finally {
target.afterCompletion( successful, delayed );
}
finally {
reset();
}
}

View File

@ -39,13 +39,11 @@ import static org.hibernate.internal.CoreLogging.messageLogger;
public class SynchronizationCallbackCoordinatorTrackingImpl extends SynchronizationCallbackCoordinatorNonTrackingImpl {
private static final CoreMessageLogger log = messageLogger( SynchronizationCallbackCoordinatorTrackingImpl.class );
// magic number :(
private static final long NO_THREAD_ID = Long.MIN_VALUE;
private volatile long registrationThreadId = NO_THREAD_ID;
private volatile long registrationThreadId;
private volatile boolean delayedCompletionHandling;
public SynchronizationCallbackCoordinatorTrackingImpl(SynchronizationCallbackTarget target) {
// super ctor calls reset() followed by pulse()
super( target );
}
@ -57,13 +55,16 @@ public class SynchronizationCallbackCoordinatorTrackingImpl extends Synchronizat
// 2) after "after completion" handling is finished.
//
// Here we use that to "clear out" all 'delayed after-completion" state. The registrationThreadId will
// "lazily" be re-populated on the next synchronizationRegistered call to allow for the potential of the next Session transaction
// occurring on a different thread (though that transaction would need to completely operate on that thread).
// "lazily" be re-populated on the next synchronizationRegistered call to allow for the potential of the
// next Session transaction occurring on a different thread (though that transaction would need to completely
// operate on that thread).
delayedCompletionHandling = false;
}
@Override
public void afterCompletion(int status) {
log.tracef( "Synchronization coordinator: afterCompletion(status=%s)", status );
// The whole concept of "tracking" comes down to this code block..
// Essentially we need to see if we can process the callback immediately. So here we check whether the
// current call is happening on the same thread as the thread under which we registered the Synchronization.
@ -79,30 +80,29 @@ public class SynchronizationCallbackCoordinatorTrackingImpl extends Synchronizat
// check for it in SessionImpl. See HHH-7910.
delayedCompletionHandling = true;
// todo : update code to use message logger
//log.rollbackFromBackgroundThread( status );
log.warn( "Rollback from background thread (update code to use message logger)" );
log.rollbackFromBackgroundThread( status );
return;
}
}
// otherwise, do the callback immediately
doAfterCompletion( JtaStatusHelper.isCommitted( status ) );
doAfterCompletion( JtaStatusHelper.isCommitted( status ), false );
}
@Override
public void synchronizationRegistered() {
if ( registrationThreadId == NO_THREAD_ID ) {
registrationThreadId = Thread.currentThread().getId();
}
registrationThreadId = Thread.currentThread().getId();
}
@Override
public void processAnyDelayedAfterCompletion() {
if ( delayedCompletionHandling ) {
// false here because, as discussed above, the delayed logic should only ever occur during rollback
delayedCompletionHandling = false;
doAfterCompletion( false );
// false here (rather than how we used to keep and check the status) because as discussed above
// the delayed logic should only ever occur during rollback
doAfterCompletion( false, true );
// NOTE : doAfterCompletion calls reset
throw new HibernateException( "Transaction was rolled back in a different thread!" );
}

View File

@ -52,7 +52,7 @@ package org.hibernate.resource.transaction.backend.jta.internal.synchronization;
public interface SynchronizationCallbackTarget {
/**
* Is the callback target still active? Generally this is checked by the caller prior to calling
* {@link #beforeCompletion()} or {@link #afterCompletion(boolean)}
* {@link #beforeCompletion} or {@link #afterCompletion}
*
* @return {@code true} indicates the target is active; {@code false} indicates it is not.
*/
@ -72,5 +72,5 @@ public interface SynchronizationCallbackTarget {
*
* @see javax.transaction.Synchronization#afterCompletion
*/
void afterCompletion(boolean successful);
void afterCompletion(boolean successful, boolean delayed);
}

View File

@ -59,8 +59,9 @@ public interface TransactionCoordinatorOwner {
* An after-completion callback from the coordinator to its owner.
*
* @param successful Was the transaction successful?
* @param delayed Is this a delayed after transaction completion call (aka after a timeout)?
*/
public void afterTransactionCompletion(boolean successful);
public void afterTransactionCompletion(boolean successful, boolean delayed);
public JdbcSessionOwner getJdbcSessionOwner();

View File

@ -291,8 +291,8 @@ public class EntityTest extends BaseNonConfigCoreFunctionalTestCase {
tx.commit();
fail( "Optimistic locking should work" );
}
catch (TransactionException e) {
assertTrue( e.getCause() instanceof StaleStateException );
catch (StaleStateException expected) {
// expected exception
}
finally {
if ( tx != null ) {

View File

@ -120,7 +120,7 @@ public class ImmutableTest extends BaseCoreFunctionalTestCase {
fail();
}
catch (HibernateException e) {
assertTrue(e.getCause().getMessage().contains("changed an immutable collection instance"));
assertTrue(e.getMessage().contains("changed an immutable collection instance"));
log.debug("success");
}
s.close();
@ -137,7 +137,7 @@ public class ImmutableTest extends BaseCoreFunctionalTestCase {
tx.commit();
fail();
} catch (HibernateException e) {
assertTrue(e.getCause().getMessage().contains("changed an immutable collection instance"));
assertTrue(e.getMessage().contains("changed an immutable collection instance"));
log.debug("success");
}
s.close();

View File

@ -195,8 +195,8 @@ public class MultiPathCascadeTest extends BaseCoreFunctionalTestCase {
s.getTransaction().commit();
fail( "should have thrown TransientObjectException" );
}
catch (TransactionException e) {
assertTrue( e.getCause() instanceof TransientObjectException );
catch (TransientObjectException expected) {
// expected
}
finally {
s.getTransaction().rollback();
@ -244,8 +244,8 @@ public class MultiPathCascadeTest extends BaseCoreFunctionalTestCase {
s.getTransaction().commit();
fail( "should have thrown TransientObjectException" );
}
catch (TransactionException e) {
assertTrue( e.getCause() instanceof TransientObjectException );
catch (TransientObjectException expected) {
// expected
}
finally {
s.getTransaction().rollback();
@ -293,8 +293,8 @@ public class MultiPathCascadeTest extends BaseCoreFunctionalTestCase {
s.getTransaction().commit();
fail( "should have thrown TransientObjectException" );
}
catch (TransactionException e) {
assertTrue( e.getCause() instanceof TransientObjectException );
catch (TransientObjectException expected) {
// expected
}
finally {
s.getTransaction().rollback();

View File

@ -33,15 +33,18 @@ public class JournalingTransactionObserver implements TransactionObserver {
private int beforeCompletions = 0;
private int afterCompletions = 0;
@Override
public void afterBegin() {
begins++;
}
@Override
public void beforeCompletion() {
beforeCompletions++;
}
public void afterCompletion(boolean successful) {
@Override
public void afterCompletion(boolean successful, boolean delayed) {
afterCompletions++;
}

View File

@ -935,11 +935,11 @@ public abstract class AbstractEntityWithManyToManyTest extends BaseCoreFunctiona
t.commit();
assertFalse( isContractVersioned );
}
catch (TransactionException e){
catch (StaleStateException ex) {
t.rollback();
assertTrue( isContractVersioned );
if ( ! sessionFactory().getSettings().isJdbcBatchVersionedData() ) {
assertTrue( StaleObjectStateException.class.isInstance( e.getCause() ) );
if ( ! sessionFactory().getSessionFactoryOptions().isJdbcBatchVersionedData() ) {
assertTrue( StaleObjectStateException.class.isInstance( ex ) );
}
}
s.close();

View File

@ -1162,11 +1162,11 @@ public abstract class AbstractEntityWithOneToManyTest extends BaseCoreFunctional
t.commit();
assertFalse( isContractVersioned );
}
catch (TransactionException ex) {
catch (StaleStateException ex) {
t.rollback();
assertTrue( isContractVersioned );
if ( ! sessionFactory().getSettings().isJdbcBatchVersionedData() ) {
assertTrue( StaleObjectStateException.class.isInstance( ex.getCause() ) );
if ( ! sessionFactory().getSessionFactoryOptions().isJdbcBatchVersionedData() ) {
assertTrue( StaleObjectStateException.class.isInstance( ex ) );
}
}
s.close();

View File

@ -157,17 +157,11 @@ public class InterceptorTest extends BaseCoreFunctionalTestCase {
fail( "Transaction should have timed out" );
}
catch (TransactionException e) {
// Insure that the Exception is "transaction timeout expired"
String exceptionActual = e.getCause().toString();
String exceptionExpected = "org.hibernate.TransactionException: transaction timeout expired";
if ( !exceptionActual.contains( exceptionExpected ) ) {
String msg = String.format(
"Transaction failed for the wrong reason. Expected [%s] but received [%s]",
exceptionExpected, exceptionActual
);
fail( msg );
}
assertTrue(
"Transaction failed for the wrong reason. Expecting transaction timeout, but found [" +
e.getMessage() + "]" ,
e.getMessage().contains( "transaction timeout expired" )
);
}
}

View File

@ -70,9 +70,8 @@ public class CascadeTest extends AbstractJPATest {
s.getTransaction().commit();
fail( "expecting TransientObjectException on flush" );
}
catch (TransactionException te) {
assertTrue( te.getCause() instanceof TransientObjectException );
log.trace( "handled expected exception", te );
catch (TransientObjectException toe) {
log.trace( "handled expected exception", toe );
s.getTransaction().rollback();
}
finally {

View File

@ -26,18 +26,16 @@ package org.hibernate.test.ops;
import java.util.ArrayList;
import java.util.Collection;
import org.junit.Test;
import org.hibernate.PersistentObjectException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.exception.ConstraintViolationException;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
@ -148,9 +146,8 @@ public class CreateTest extends AbstractOperationTestCase {
tx.commit();
fail( "Expecting constraint failure" );
}
catch (TransactionException te) {
catch (ConstraintViolationException te) {
//verify that an exception is thrown!
assertTrue( te.getCause() instanceof ConstraintViolationException);
}
tx.rollback();
s.close();
@ -165,9 +162,8 @@ public class CreateTest extends AbstractOperationTestCase {
tx.commit();
assertFalse(true);
}
catch (TransactionException te) {
catch (ConstraintViolationException te) {
//verify that an exception is thrown!
assertTrue( te.getCause() instanceof ConstraintViolationException);
}
tx.rollback();
s.close();

View File

@ -109,7 +109,7 @@ public class TransactionCoordinatorOwnerTestingImpl
}
@Override
public void afterTransactionCompletion(boolean successful) {
public void afterTransactionCompletion(boolean successful, boolean delayed) {
log.debug( "#afterTransactionCompletion called" );
if ( successful ) {
successfulCompletionCount++;

View File

@ -90,7 +90,7 @@ public class TransactionCoordinatorOwnerTestingImpl implements TransactionCoordi
}
@Override
public void afterTransactionCompletion(boolean successful) {
public void afterTransactionCompletion(boolean successful, boolean delayed) {
log.debug( "#afterTransactionCompletion called" );
if ( successful ) {
successfulCompletionCount++;

View File

@ -78,9 +78,8 @@ public class BeanValidationTest extends BaseEntityManagerFunctionalTestCase {
}
catch ( RollbackException e ) {
final Throwable cve = e.getCause();
assertTrue( cve instanceof PersistenceException );
assertTrue( cve.getCause() instanceof ConstraintViolationException );
assertEquals( 1, ( (ConstraintViolationException) cve.getCause() ).getConstraintViolations().size() );
assertTrue( cve instanceof ConstraintViolationException );
assertEquals( 1, ( (ConstraintViolationException) cve ).getConstraintViolations().size() );
}
em.close();
}

View File

@ -36,6 +36,7 @@ import org.hibernate.TransientPropertyValueException;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.FailureExpected;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@ -257,10 +258,8 @@ public class MultiCircleJpaCascadeTest extends BaseEntityManagerFunctionalTestCa
// because e is not in the process of being saved yet.
// when HHH-6999 is fixed, this test should be changed to
// check for g and f.g
assertTrue( ise.getCause() instanceof TransactionException );
Throwable cause = ise.getCause().getCause();
assertTrue( cause instanceof TransientPropertyValueException );
TransientPropertyValueException tpve = ( TransientPropertyValueException ) cause;
//noinspection ThrowableResultOfMethodCallIgnored
TransientPropertyValueException tpve = assertTyping( TransientPropertyValueException.class, ise.getCause() );
assertEquals( E.class.getName(), tpve.getTransientEntityName() );
assertEquals( D.class.getName(), tpve.getPropertyOwnerEntityName() );
assertEquals( "e", tpve.getPropertyName() );

View File

@ -50,9 +50,9 @@ import static org.junit.Assert.fail;
public class TransactionRolledBackInDifferentThreadTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions(options);
TestingJtaBootstrap.prepare(options);
options.put(AvailableSettings.TRANSACTION_TYPE, "JTA");
super.addConfigOptions( options );
TestingJtaBootstrap.prepare( options );
options.put( AvailableSettings.TRANSACTION_TYPE, "JTA" );
}
@Test
@ -85,25 +85,32 @@ public class TransactionRolledBackInDifferentThreadTest extends BaseEntityManage
em.joinTransaction();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().setRollbackOnly();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
} catch (javax.persistence.PersistenceException e) {
if (e.getCause() instanceof HibernateException &&
e.getCause().getMessage().equals("Transaction was rolled back in a different thread!")) {
}
catch (javax.persistence.PersistenceException e) {
if ( e.getCause() instanceof HibernateException &&
e.getCause().getMessage().equals( "Transaction was rolled back in a different thread!" ) ) {
/**
* Save the exception for the main test thread to fail
*/
e.printStackTrace(); // show the error first
transactionRolledBackInDifferentThreadException[0] = (HibernateException) e.getCause();
}
} catch (RollbackException ignored) {
}
catch (RollbackException ignored) {
// expected to see RollbackException: ARJUNA016053: Could not commit transaction.
} catch (Throwable throwable) {
}
catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
}
finally {
try {
if (TestingJtaPlatformImpl.INSTANCE.getTransactionManager().getStatus() != Status.STATUS_NO_TRANSACTION)
if ( TestingJtaPlatformImpl.INSTANCE.getTransactionManager()
.getStatus() != Status.STATUS_NO_TRANSACTION ) {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
} catch (SystemException ignore) {
}
}
catch (SystemException ignore) {
}
}
@ -123,45 +130,56 @@ public class TransactionRolledBackInDifferentThreadTest extends BaseEntityManage
*/
em.joinTransaction();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
} catch (javax.persistence.PersistenceException e) {
if (e.getCause() instanceof HibernateException &&
e.getCause().getMessage().equals("Transaction was rolled back in a different thread!")) {
}
catch (javax.persistence.PersistenceException e) {
if ( e.getCause() instanceof HibernateException &&
e.getCause().getMessage().equals( "Transaction was rolled back in a different thread!" ) ) {
/**
* Save the exception for the main test thread to fail
*/
e.printStackTrace(); // show the error first
transactionRolledBackInDifferentThreadException[1] = (HibernateException) e.getCause();
}
} catch (Throwable throwable) {
}
catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
}
finally {
try {
if (TestingJtaPlatformImpl.INSTANCE.getTransactionManager().getStatus() != Status.STATUS_NO_TRANSACTION)
if ( TestingJtaPlatformImpl.INSTANCE.getTransactionManager()
.getStatus() != Status.STATUS_NO_TRANSACTION ) {
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
} catch (SystemException ignore) {
}
}
catch (SystemException ignore) {
}
}
}
};
Thread thread = new Thread(run1, "test thread1");
Thread thread = new Thread( run1, "test thread1" );
thread.start();
thread.join();
Thread thread2 = new Thread(run2, "test thread2");
Thread thread2 = new Thread( run2, "test thread2" );
thread2.start();
thread2.join();
// show failure for exception caught in run2.run()
if (transactionRolledBackInDifferentThreadException[0] != null
|| transactionRolledBackInDifferentThreadException[1] != null)
if ( transactionRolledBackInDifferentThreadException[0] != null
|| transactionRolledBackInDifferentThreadException[1] != null )
{
fail("failure in test thread 1 = " +
(transactionRolledBackInDifferentThreadException[0] != null ? transactionRolledBackInDifferentThreadException[0].getMessage() : "(none)")
+ ", failure in test thread 2 = " +
(transactionRolledBackInDifferentThreadException[1] != null ? transactionRolledBackInDifferentThreadException[1].getMessage() : "(none)")
fail(
"failure in test thread 1 = " +
( transactionRolledBackInDifferentThreadException[0] != null ?
transactionRolledBackInDifferentThreadException[0].getMessage() :
"(none)" )
+ ", failure in test thread 2 = " +
( transactionRolledBackInDifferentThreadException[1] != null ?
transactionRolledBackInDifferentThreadException[1].getMessage() :
"(none)" )
);
}
@ -171,7 +189,7 @@ public class TransactionRolledBackInDifferentThreadTest extends BaseEntityManage
@Override
public Class[] getAnnotatedClasses() {
return new Class[]{
return new Class[] {
};
}