HHH-9888 - Oracle database constraint violation reported as RollbackException with JTA transaction manager

This commit is contained in:
Steve Ebersole 2015-09-11 06:36:15 -05:00
parent 9d6886198b
commit 918c9d4ffd
1 changed files with 143 additions and 0 deletions

View File

@ -0,0 +1,143 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.tm;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.resource.transaction.jta.JtaPlatformStandardTestingImpl;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author Steve Ebersole
*/
public class BeforeCompletionFailureTest extends BaseNonConfigCoreFunctionalTestCase {
private static final Logger log = Logger.getLogger( BeforeCompletionFailureTest.class );
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] { SimpleEntity.class };
}
@Override
protected void addSettings(Map settings) {
super.addSettings( settings );
TestingJtaBootstrap.prepare( settings );
settings.put( AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jta" );
}
@Test
@TestForIssue( jiraKey = "HHH-9888" )
public void testUniqueConstraintViolationDuringManagedFlush() throws Exception {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// set up test data
Session session = openSession();
session.getTransaction().begin();
session.save( newEntity( 1 ) );
session.getTransaction().commit();
session.close();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// do the test
final TransactionManager tm = JtaPlatformStandardTestingImpl.INSTANCE.transactionManager();
assertEquals( Status.STATUS_NO_TRANSACTION, tm.getStatus() );
// begin the transaction ("CMT" style)
tm.begin();
session = openSession();
session.save( newEntity( 2 ) );
// complete the transaction ("CMT" style) - this leads to the managed flush
// which should lead to the UK violation
try {
tm.commit();
fail( "Expecting a failure from JTA commit" );
}
catch (RollbackException expected) {
log.info( "Test encountered expected JTA RollbackException; looking for nested JDBCException", expected );
boolean violationExceptionFound = false;
Throwable cause = expected;
while ( cause != null ) {
if ( cause instanceof JDBCException ) {
log.info( "Found JDBCException, assuming related to UK violation", cause );
violationExceptionFound = true;
break;
}
cause = cause.getCause();
}
if ( !violationExceptionFound ) {
fail( "Did not find JDBCException in JTA RollbackException chain" );
}
}
finally {
if ( !( (SessionImplementor) session ).isClosed() ) {
session.close();
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// clean up test data
session = openSession();
session.getTransaction().begin();
session.createQuery( "delete SimpleEntity" ).executeUpdate();
session.getTransaction().commit();
session.close();
}
private SimpleEntity newEntity(int id) {
// since "key" is reused, should violate the UK
return new SimpleEntity( id, "key", "name" );
}
private void runTest() {
}
@Entity(name = "SimpleEntity")
public static class SimpleEntity {
@Id
public Integer id;
@Column(unique = true)
public String key;
public String name;
public SimpleEntity() {
}
public SimpleEntity(Integer id, String key, String name) {
this.id = id;
this.key = key;
this.name = name;
}
}
}