From ad0aa213bc860520842f7c61e0de9223e04a2bef Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 30 Nov 2017 20:16:01 -0600 Subject: [PATCH] Allow Hibernate's Transaction act like JPA's EntityTransaction --- .../transaction/internal/TransactionImpl.java | 8 +- .../id/enhanced/SequenceStyleGenerator.java | 1 + .../AbstractSharedSessionContract.java | 7 +- .../custom/sql/SQLQueryReturnProcessor.java | 87 +++++++ ...ocalTransactionCoordinatorBuilderImpl.java | 6 +- ...sourceLocalTransactionCoordinatorImpl.java | 38 ++- .../JtaTransactionCoordinatorImpl.java | 16 +- .../spi/TransactionCoordinator.java | 43 ++-- .../result/internal/OutputsImpl.java | 38 ++- .../transaction/FlushAndTransactionTest.java | 23 +- .../test/annotations/ConfigurationTest.java | 51 ++-- .../test/hql/ASTParserLoadingTest.java | 239 ++++++++++-------- .../hibernate/test/hql/CaseStatementTest.java | 103 ++++---- .../tck2_2/EntityTransactionTests.java | 79 ++++++ .../jpa/compliance/tck2_2/QueryApiTest.java | 29 ++- .../jdbc/BasicJdbcTransactionTests.java | 160 +++++++----- .../test/sql/storedproc/H2ProcTesting.java | 223 ++++++++++++++++ .../sql/storedproc/ResultMappingTest.java | 111 ++++++++ .../sql/storedproc/StoredProcedureTest.java | 176 +------------ .../util/BatchModeTransactionCoordinator.java | 8 + .../testing/transaction/TransactionUtil2.java | 49 ++-- 21 files changed, 1044 insertions(+), 451 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/H2ProcTesting.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/ResultMappingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionImpl.java index e3d019c6b1..70a9e92424 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionImpl.java @@ -71,8 +71,13 @@ public class TransactionImpl implements TransactionImplementor { @Override public void commit() { - if ( !isActive() ) { + if ( !isActive( true ) ) { // allow MARKED_ROLLBACK to propagate through to transactionDriverControl + // the boolean passed to isActive indicates whether MARKED_ROLLBACK should be + // considered active + // + // essentially here we have a transaction that is not active and + // has not been marked for rollback only throw new IllegalStateException( "Transaction not successfully started" ); } @@ -124,6 +129,7 @@ public class TransactionImpl implements TransactionImplementor { @Override public boolean isActive() { // old behavior considered TransactionStatus#MARKED_ROLLBACK as active +// return isActive( jpaCompliance.isJpaTransactionComplianceEnabled() ? false : true ); return isActive( true ); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java index 5a0424e3d6..4b20030bd3 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java @@ -287,6 +287,7 @@ public class SequenceStyleGenerator fallbackSequenceName = generatorName; } } + // JPA_ENTITY_NAME value honors (HBM) and @Entity#name (JPA) overrides. final String defaultSequenceName = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEQUENCE_PER_ENTITY, params, false ) ? params.getProperty( JPA_ENTITY_NAME ) + sequencePerEntitySuffix diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 454a4e8cae..ed4d5c953c 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -361,7 +361,11 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public void markForRollbackOnly() { - accessTransaction().markRollbackOnly(); + try { + accessTransaction().markRollbackOnly(); + } + catch (Exception ignore) { + } } @Override @@ -670,6 +674,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont return query; } catch (RuntimeException e) { + markForRollbackOnly(); throw exceptionConverter.convert( e ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java index 34828dad6b..fdafd4203b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/sql/SQLQueryReturnProcessor.java @@ -183,6 +183,41 @@ public class SQLQueryReturnProcessor { return new ResultAliasContext(); } + private interface QueryReturnVisitor { + void visitScalarReturn(NativeSQLQueryScalarReturn rtn); + void visitRootReturn(NativeSQLQueryRootReturn rtn); + void visitCollectionReturn(NativeSQLQueryCollectionReturn rtn); + + void visitFetch(NativeSQLQueryJoinReturn rtn); + + void visitDynamicInstantiation(NativeSQLQueryConstructorReturn rtn); + } + + public void visitReturns(QueryReturnVisitor visitor) { + for ( NativeSQLQueryReturn queryReturn : queryReturns ) { + if ( NativeSQLQueryScalarReturn.class.isInstance( queryReturn ) ) { + visitor.visitScalarReturn( (NativeSQLQueryScalarReturn) queryReturn ); + } + else if ( NativeSQLQueryRootReturn.class.isInstance( queryReturn ) ) { + visitor.visitRootReturn( (NativeSQLQueryRootReturn) queryReturn ); + } + else if ( NativeSQLQueryCollectionReturn.class.isInstance( queryReturn ) ) { + visitor.visitCollectionReturn( (NativeSQLQueryCollectionReturn) queryReturn ); + } + else if ( NativeSQLQueryJoinReturn.class.isInstance( queryReturn ) ) { + visitor.visitFetch( (NativeSQLQueryJoinReturn) queryReturn ); + } + else if ( NativeSQLQueryConstructorReturn.class.isInstance( queryReturn ) ) { + visitor.visitDynamicInstantiation( (NativeSQLQueryConstructorReturn) queryReturn ); + } + else { + throw new IllegalStateException( + "Unrecognized NativeSQLQueryReturn concrete type : " + queryReturn + ); + } + } + } + public List generateCustomReturns(boolean queryHadAliases) { List customReturns = new ArrayList(); Map customReturnsByAlias = new HashMap(); @@ -354,6 +389,58 @@ public class SQLQueryReturnProcessor { return customReturns; } + public List generateCallableReturns() { + final List customReturns = new ArrayList<>(); + + visitReturns( + new QueryReturnVisitor() { + @Override + public void visitScalarReturn(NativeSQLQueryScalarReturn rtn) { + customReturns.add( new ScalarReturn( rtn.getType(), rtn.getColumnAlias() ) ); + } + + @Override + public void visitRootReturn(NativeSQLQueryRootReturn rtn) { + customReturns.add( + new RootReturn( + rtn.getAlias(), + rtn.getReturnEntityName(), + new ColumnEntityAliases( + (Map) entityPropertyResultMaps.get( rtn.getAlias() ), + (SQLLoadable) alias2Persister.get( rtn.getAlias() ), + (String) alias2Suffix.get( rtn.getAlias() ) + ), + rtn.getLockMode() + ) + ); + } + + @Override + public void visitCollectionReturn(NativeSQLQueryCollectionReturn rtn) { + throw new UnsupportedOperationException( "Collection returns not supported for stored procedure mapping" ); + } + + @Override + public void visitFetch(NativeSQLQueryJoinReturn rtn) { + throw new UnsupportedOperationException( "Collection returns not supported for stored procedure mapping" ); + } + + @Override + public void visitDynamicInstantiation(NativeSQLQueryConstructorReturn rtn) { + final ScalarReturn[] scalars = new ScalarReturn[ rtn.getColumnReturns().length ]; + int i = 0; + for ( NativeSQLQueryScalarReturn scalarReturn : rtn.getColumnReturns() ) { + scalars[i++] = new ScalarReturn( scalarReturn.getType(), scalarReturn.getColumnAlias() ); + } + + customReturns.add( new ConstructorReturn( rtn.getTargetClass(), scalars ) ); + } + } + ); + + return customReturns; + } + private SQLLoadable getSQLLoadable(String entityName) throws MappingException { EntityPersister persister = factory.getEntityPersister( entityName ); if ( !(persister instanceof SQLLoadable) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorBuilderImpl.java index 8048ae3acb..98e5696d49 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorBuilderImpl.java @@ -31,7 +31,11 @@ public class JdbcResourceLocalTransactionCoordinatorBuilderImpl implements Trans @Override public TransactionCoordinator buildTransactionCoordinator(TransactionCoordinatorOwner owner, Options options) { if ( owner instanceof JdbcResourceTransactionAccess ) { - return new JdbcResourceLocalTransactionCoordinatorImpl( this, owner, (JdbcResourceTransactionAccess) owner ); + return new JdbcResourceLocalTransactionCoordinatorImpl( + this, + owner, + (JdbcResourceTransactionAccess) owner + ); } throw new HibernateException( diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java index 6e308e2c2f..898cee5609 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/JdbcResourceLocalTransactionCoordinatorImpl.java @@ -8,6 +8,7 @@ package org.hibernate.resource.transaction.backend.jdbc.internal; import java.util.ArrayList; import java.util.List; +import javax.persistence.RollbackException; import javax.transaction.Status; import org.hibernate.TransactionException; @@ -15,6 +16,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.transaction.spi.IsolationDelegate; import org.hibernate.engine.transaction.spi.TransactionObserver; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.jpa.JpaCompliance; import org.hibernate.resource.jdbc.spi.JdbcSessionOwner; import org.hibernate.resource.transaction.backend.jdbc.spi.JdbcResourceTransaction; import org.hibernate.resource.transaction.backend.jdbc.spi.JdbcResourceTransactionAccess; @@ -43,6 +45,8 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC private final TransactionCoordinatorOwner transactionCoordinatorOwner; private final SynchronizationRegistryStandardImpl synchronizationRegistry = new SynchronizationRegistryStandardImpl(); + private final JpaCompliance jpaCompliance; + private TransactionDriverControlImpl physicalTransactionDelegate; private int timeOut = -1; @@ -63,6 +67,12 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC this.transactionCoordinatorBuilder = transactionCoordinatorBuilder; this.jdbcResourceTransactionAccess = jdbcResourceTransactionAccess; this.transactionCoordinatorOwner = owner; + + this.jpaCompliance = owner.getJdbcSessionOwner() + .getJdbcSessionContext() + .getSessionFactory() + .getSessionFactoryOptions() + .getJpaCompliance(); } /** @@ -107,6 +117,11 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC return synchronizationRegistry; } + @Override + public JpaCompliance getJpaCompliance() { + return jpaCompliance; + } + @Override public boolean isActive() { return transactionCoordinatorOwner.isActive(); @@ -225,13 +240,32 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC public void commit() { try { if ( rollbackOnly ) { - throw new TransactionException( "Transaction was marked for rollback only; cannot commit" ); + log.debugf( "On commit, transaction was marked for roll-back only, rolling back" ); + + try { + rollback(); + } + catch (RuntimeException e) { + log.debug( "Encountered failure rolling back failed commit", e ); + throw e; + } + finally { + if ( jpaCompliance.isJpaTransactionComplianceEnabled() ) { + throw new RollbackException( "Transaction was marked for rollback-only" ); + } + else { + return; + } + } } JdbcResourceLocalTransactionCoordinatorImpl.this.beforeCompletionCallback(); jdbcResourceTransaction.commit(); JdbcResourceLocalTransactionCoordinatorImpl.this.afterCompletionCallback( true ); } + catch (RollbackException e) { + throw e; + } catch (RuntimeException e) { try { rollback(); @@ -261,7 +295,7 @@ public class JdbcResourceLocalTransactionCoordinatorImpl implements TransactionC @Override public void markRollbackOnly() { - if ( getStatus() != TransactionStatus.ROLLED_BACK && getStatus() != TransactionStatus.NOT_ACTIVE ) { + if ( getStatus() != TransactionStatus.ROLLED_BACK ) { if ( log.isDebugEnabled() ) { log.debug( "JDBC transaction marked for rollback-only (exception provided for stack trace)", diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java index 4f973a97e5..d6d034f379 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionCoordinatorImpl.java @@ -19,6 +19,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.engine.transaction.spi.IsolationDelegate; import org.hibernate.engine.transaction.spi.TransactionObserver; +import org.hibernate.jpa.JpaCompliance; import org.hibernate.resource.jdbc.spi.JdbcSessionContext; import org.hibernate.resource.jdbc.spi.JdbcSessionOwner; import org.hibernate.resource.transaction.TransactionRequiredForJoinException; @@ -74,11 +75,12 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy TransactionCoordinatorBuilder transactionCoordinatorBuilder, TransactionCoordinatorOwner owner, boolean autoJoinTransactions) { - this.observers = new ArrayList(); this.transactionCoordinatorBuilder = transactionCoordinatorBuilder; this.transactionCoordinatorOwner = owner; this.autoJoinTransactions = autoJoinTransactions; + this.observers = new ArrayList<>(); + final JdbcSessionContext jdbcSessionContext = owner.getJdbcSessionOwner().getJdbcSessionContext(); this.jtaPlatform = jdbcSessionContext.getServiceRegistry().getService( JtaPlatform.class ); @@ -100,7 +102,6 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy boolean preferUserTransactions, boolean performJtaThreadTracking, TransactionObserver... observers) { - this.observers = new ArrayList(); this.transactionCoordinatorBuilder = transactionCoordinatorBuilder; this.transactionCoordinatorOwner = owner; this.autoJoinTransactions = autoJoinTransactions; @@ -108,6 +109,8 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy this.preferUserTransactions = preferUserTransactions; this.performJtaThreadTracking = performJtaThreadTracking; + this.observers = new ArrayList<>(); + if ( observers != null ) { Collections.addAll( this.observers, observers ); } @@ -207,6 +210,15 @@ public class JtaTransactionCoordinatorImpl implements TransactionCoordinator, Sy return this.transactionCoordinatorOwner; } + @Override + public JpaCompliance getJpaCompliance() { + return transactionCoordinatorOwner.getJdbcSessionOwner() + .getJdbcSessionContext() + .getSessionFactory() + .getSessionFactoryOptions() + .getJpaCompliance(); + } + @Override public TransactionDriver getTransactionDriverControl() { if ( physicalTransactionDelegate == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/TransactionCoordinator.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/TransactionCoordinator.java index da4ee56c8b..ae05a7b212 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/TransactionCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/spi/TransactionCoordinator.java @@ -8,6 +8,7 @@ package org.hibernate.resource.transaction.spi; import org.hibernate.engine.transaction.spi.IsolationDelegate; import org.hibernate.engine.transaction.spi.TransactionObserver; +import org.hibernate.jpa.JpaCompliance; /** * Models the coordination of all transaction related flows. @@ -15,6 +16,28 @@ import org.hibernate.engine.transaction.spi.TransactionObserver; * @author Steve Ebersole */ public interface TransactionCoordinator { + /** + * Access to the builder that generated this coordinator + */ + TransactionCoordinatorBuilder getTransactionCoordinatorBuilder(); + + /** + * Get the delegate used by the local transaction driver to control the underlying transaction + * + * @return The control delegate. + */ + TransactionDriver getTransactionDriverControl(); + + /** + * Get access to the local registry of Synchronization instances + * + * @return The local Synchronization registry + */ + SynchronizationRegistry getLocalSynchronizations(); + + + JpaCompliance getJpaCompliance(); + /** * Indicates an explicit request to join a transaction. This is mainly intended to handle the JPA requirement * around {@link javax.persistence.EntityManager#joinTransaction()}, and generally speaking only has an impact in @@ -35,20 +58,6 @@ public interface TransactionCoordinator { */ void pulse(); - /** - * Get the delegate used by the local transaction driver to control the underlying transaction - * - * @return The control delegate. - */ - TransactionDriver getTransactionDriverControl(); - - /** - * Get access to the local registry of Synchronization instances - * - * @return The local Synchronization registry - */ - SynchronizationRegistry getLocalSynchronizations(); - /** * Is this transaction still active? *

@@ -85,12 +94,6 @@ public interface TransactionCoordinator { */ void removeObserver(TransactionObserver observer); - /** - * - * @return - */ - TransactionCoordinatorBuilder getTransactionCoordinatorBuilder(); - void setTimeOut(int seconds); int getTimeOut(); diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java index 1cdc2e2930..f611b264b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java @@ -9,6 +9,7 @@ package org.hibernate.result.internal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -18,8 +19,11 @@ import org.hibernate.JDBCException; import org.hibernate.engine.spi.QueryParameters; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreLogging; +import org.hibernate.loader.EntityAliases; import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomQuery; +import org.hibernate.loader.custom.Return; +import org.hibernate.loader.custom.RootReturn; import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor; import org.hibernate.param.ParameterBinder; import org.hibernate.result.NoMoreReturnsException; @@ -231,7 +235,7 @@ public class OutputsImpl implements Outputs { context.getSession().getFactory() ); processor.process(); - final List customReturns = processor.generateCustomReturns( false ); + final List customReturns = processor.generateCallableReturns(); CustomQuery customQuery = new CustomQuery() { @Override @@ -264,8 +268,11 @@ public class OutputsImpl implements Outputs { } private static class CustomLoaderExtension extends CustomLoader { - private QueryParameters queryParameters; - private SharedSessionContractImplementor session; + private static final EntityAliases[] NO_ALIASES = new EntityAliases[0]; + + private final QueryParameters queryParameters; + private final SharedSessionContractImplementor session; + private final EntityAliases[] entityAliases; private boolean needsDiscovery = true; @@ -276,8 +283,33 @@ public class OutputsImpl implements Outputs { super( customQuery, session.getFactory() ); this.queryParameters = queryParameters; this.session = session; + + entityAliases = interpretEntityAliases( customQuery.getCustomQueryReturns() ); } + private EntityAliases[] interpretEntityAliases(List customQueryReturns) { + final List entityAliases = new ArrayList<>(); + for ( Return queryReturn : customQueryReturns ) { + if ( !RootReturn.class.isInstance( queryReturn ) ) { + continue; + } + + entityAliases.add( ( (RootReturn) queryReturn ).getEntityAliases() ); + } + + if ( entityAliases.isEmpty() ) { + return NO_ALIASES; + } + + return entityAliases.toArray( new EntityAliases[ entityAliases.size() ] ); + } + + @Override + protected EntityAliases[] getEntityAliases() { + return entityAliases; + } + + // todo : this would be a great way to add locking to stored procedure support (at least where returning entities). public List processResultSet(ResultSet resultSet) throws SQLException { diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java index bb96e02e46..1037e9940b 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java @@ -7,6 +7,7 @@ package org.hibernate.jpa.test.transaction; import java.util.List; +import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.OptimisticLockException; @@ -16,6 +17,7 @@ import javax.persistence.RollbackException; import javax.persistence.TransactionRequiredException; import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.jpa.HibernateEntityManagerFactory; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.stat.Statistics; @@ -226,6 +228,7 @@ public class FlushAndTransactionTest extends BaseEntityManagerFunctionalTestCase catch ( PersistenceException e ) { //success } + try { em.getTransaction().commit(); fail( "Commit should be rollbacked" ); @@ -288,13 +291,6 @@ public class FlushAndTransactionTest extends BaseEntityManagerFunctionalTestCase em.close(); } - @Override - public Class[] getAnnotatedClasses() { - return new Class[] { - Book.class - }; - } - @Test public void testSetRollbackOnlyAndFlush() throws Exception { Book book = new Book(); @@ -313,4 +309,17 @@ public class FlushAndTransactionTest extends BaseEntityManagerFunctionalTestCase em.close(); } + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Book.class + }; + } + + @Override + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + options.put( AvailableSettings.JPA_TRANSACTION_COMPLIANCE, "true" ); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/ConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/ConfigurationTest.java index 9a8d67a60d..8b0c4376f7 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/ConfigurationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/ConfigurationTest.java @@ -8,20 +8,19 @@ //$Id$ package org.hibernate.test.annotations; -import javax.persistence.PersistenceException; - -import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.hql.internal.ast.QuerySyntaxException; import org.junit.Test; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -54,25 +53,37 @@ public class ConfigurationTest { cfg.configure( "org/hibernate/test/annotations/hibernate.cfg.xml" ); cfg.setProperty( Environment.HBM2DDL_AUTO, "create-drop" ); cfg.setProperty( Configuration.ARTEFACT_PROCESSING_ORDER, "class" ); - SessionFactory sf = cfg.buildSessionFactory(); - assertNotNull( sf ); - Session s = sf.openSession(); - Transaction tx = s.beginTransaction(); - Query q; - try { - s.createQuery( "from Boat" ).list(); - fail( "Boat should not be mapped" ); + + try ( SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory() ) { + assertNotNull( sf ); + + inTransaction( + sf, + session -> { + try { + session.createQuery( "from Boat" ).list(); + fail( "Boat should not be mapped" ); + } + catch (IllegalArgumentException expected) { + assertTyping( QuerySyntaxException.class, expected.getCause() ); + // expected outcome + + // see org.hibernate.test.jpa.compliance.tck2_2.QueryApiTest#testInvalidQueryMarksTxnForRollback + // for testing of how this invalid query String case is handled in terms of transactions + } + } + ); + + + inTransaction( + sf, + session -> { + assertEquals( 0, session.createQuery( "from Plane" ).list().size() ); + } + ); } - catch (IllegalArgumentException e) { - assertTyping( QuerySyntaxException.class, e.getCause()); - //all good - } - q = s.createQuery( "from Plane" ); - assertEquals( 0, q.list().size() ); - tx.commit(); - s.close(); - sf.close(); } + @Test public void testPrecedenceHbm() throws Exception { Configuration cfg = new Configuration(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java index 96e5f9abcc..0eef7ebd21 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java @@ -46,6 +46,7 @@ import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.dialect.SybaseAnywhereDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.dialect.TeradataDialect; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory; import org.hibernate.hql.internal.ast.QuerySyntaxException; import org.hibernate.internal.util.StringHelper; @@ -82,6 +83,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hibernate.testing.junit4.ExtraAssertions.assertClassAssignability; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -851,61 +853,84 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { @Test public void testInvalidCollectionDereferencesFail() { - Session s = openSession(); - s.beginTransaction(); - // control group... - s.createQuery( "from Animal a join a.offspring o where o.description = 'xyz'" ).list(); - s.createQuery( "from Animal a join a.offspring o where o.father.description = 'xyz'" ).list(); - s.createQuery( "from Animal a join a.offspring o order by o.description" ).list(); - s.createQuery( "from Animal a join a.offspring o order by o.father.description" ).list(); - try { - s.createQuery( "from Animal a where a.offspring.description = 'xyz'" ).list(); - fail( "illegal collection dereference semantic did not cause failure" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch( QueryException qe ) { - log.trace("expected failure...", qe); - } + try ( final SessionImplementor s = (SessionImplementor) openSession() ) { + // control group... + inTransaction( + s, + session -> { + s.createQuery( "from Animal a join a.offspring o where o.description = 'xyz'" ).list(); + s.createQuery( "from Animal a join a.offspring o where o.father.description = 'xyz'" ).list(); + s.createQuery( "from Animal a join a.offspring o order by o.description" ).list(); + s.createQuery( "from Animal a join a.offspring o order by o.father.description" ).list(); + } + ); - try { - s.createQuery( "from Animal a where a.offspring.father.description = 'xyz'" ).list(); - fail( "illegal collection dereference semantic did not cause failure" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch( QueryException qe ) { - log.trace("expected failure...", qe); - } + inTransaction( + s, + session -> { + try { + s.createQuery( "from Animal a where a.offspring.description = 'xyz'" ).list(); + fail( "illegal collection dereference semantic did not cause failure" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch (QueryException qe) { + log.trace( "expected failure...", qe ); + } + } + ); - try { - s.createQuery( "from Animal a order by a.offspring.description" ).list(); - fail( "illegal collection dereference semantic did not cause failure" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch( QueryException qe ) { - log.trace("expected failure...", qe); - } + inTransaction( + s, + session -> { + try { + s.createQuery( "from Animal a where a.offspring.father.description = 'xyz'" ).list(); + fail( "illegal collection dereference semantic did not cause failure" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch (QueryException qe) { + log.trace( "expected failure...", qe ); + } + } + ); - try { - s.createQuery( "from Animal a order by a.offspring.father.description" ).list(); - fail( "illegal collection dereference semantic did not cause failure" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch( QueryException qe ) { - log.trace("expected failure...", qe); - } + inTransaction( + s, + session -> { + try { + s.createQuery( "from Animal a order by a.offspring.description" ).list(); + fail( "illegal collection dereference semantic did not cause failure" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch (QueryException qe) { + log.trace( "expected failure...", qe ); + } + } + ); - s.getTransaction().commit(); - s.close(); + inTransaction( + s, + session -> { + try { + s.createQuery( "from Animal a order by a.offspring.father.description" ).list(); + fail( "illegal collection dereference semantic did not cause failure" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch (QueryException qe) { + log.trace( "expected failure...", qe ); + } + } + ); + } } @Test @@ -1571,42 +1596,46 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { @Test @FailureExpected( jiraKey = "unknown" ) public void testParameterTypeMismatch() { - Session s = openSession(); - s.beginTransaction(); - - Query query = s.createQuery( "from Animal a where a.description = :nonstring" ) - .setParameter( "nonstring", Integer.valueOf( 1 ) ); - try { - query.list(); - fail( "query execution should have failed" ); + try ( final SessionImplementor s = (SessionImplementor) openSession() ) { + inTransaction( + s, + session -> { + try { + s.createQuery( "from Animal a where a.description = :nonstring" ) + .setParameter( "nonstring", Integer.valueOf( 1 ) ) + .list(); + fail( "query execution should have failed" ); + } + catch (IllegalArgumentException e) { + assertTyping( TypeMismatchException.class, e.getCause() ); + } + catch (TypeMismatchException tme) { + // expected behavior + } + } + ); } - catch (IllegalArgumentException e) { - assertTyping( TypeMismatchException.class, e.getCause() ); - } - catch( TypeMismatchException tme ) { - // expected behavior - } - - s.getTransaction().commit(); - s.close(); } @Test public void testMultipleBagFetchesFail() { - Session s = openSession(); - s.beginTransaction(); - try { - s.createQuery( "from Human h join fetch h.friends f join fetch f.friends fof" ).list(); - fail( "failure expected" ); + try ( final SessionImplementor s = (SessionImplementor) openSession() ) { + inTransaction( + s, + session-> { + try { + s.createQuery( "from Human h join fetch h.friends f join fetch f.friends fof" ).list(); + fail( "failure expected" ); + } + catch (IllegalArgumentException e) { + assertTyping( MultipleBagFetchException.class, e.getCause() ); + } + catch( HibernateException e ) { + assertTrue( "unexpected failure reason : " + e, e.getMessage().indexOf( "multiple bags" ) > 0 ); + } + } + ); } - catch (IllegalArgumentException e) { - assertTyping( MultipleBagFetchException.class, e.getCause() ); - } - catch( HibernateException e ) { - assertTrue( "unexpected failure reason : " + e, e.getMessage().indexOf( "multiple bags" ) > 0 ); - } - s.getTransaction().commit(); - s.close(); } @Test @@ -1880,31 +1909,39 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { @Test public void testInvalidFetchSemantics() { - Session s = openSession(); - s.beginTransaction(); + try ( final SessionImplementor s = (SessionImplementor) openSession()) { - try { - s.createQuery( "select mother from Human a left join fetch a.mother mother" ).list(); - fail( "invalid fetch semantic allowed!" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch( QueryException e ) { - } + inTransaction( + s, + session -> { + try { + s.createQuery( "select mother from Human a left join fetch a.mother mother" ).list(); + fail( "invalid fetch semantic allowed!" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch( QueryException e ) { + } + } + ); - try { - s.createQuery( "select mother from Human a left join fetch a.mother mother" ).list(); - fail( "invalid fetch semantic allowed!" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch( QueryException e ) { - } + inTransaction( + s, + session-> { + try { + s.createQuery( "select mother from Human a left join fetch a.mother mother" ).list(); + fail( "invalid fetch semantic allowed!" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch( QueryException e ) { + } + } + ); - s.getTransaction().commit(); - s.close(); + } } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java index 4a861dda26..21b2453009 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java @@ -12,11 +12,13 @@ import javax.persistence.Id; import org.hibernate.QueryException; import org.hibernate.Session; import org.hibernate.Transaction; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction; import static org.junit.Assert.fail; /** @@ -63,30 +65,36 @@ public class CaseStatementTest extends BaseCoreFunctionalTestCase { @Test public void testSimpleCaseStatementWithParamAllResults() { - Session s = openSession(); - Transaction t = s.beginTransaction(); + try ( final SessionImplementor s = (SessionImplementor) openSession() ) { + inTransaction( + s, + session-> { + try { + s.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + fail( "was expecting an exception" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch (QueryException expected) { + // expected + } + } + ); - try { - s.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" ) - .setString( "opt1", "x" ) - .setString( "opt2", "y" ) - .list(); - fail( "was expecting an exception" ); + inTransaction( + s, + session-> { + s.createQuery( "select case p.name when 'Steve' then cast( :opt1 as string ) else cast( :opt2 as string) end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + } + ); } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch (QueryException expected) { - // expected - } - - s.createQuery( "select case p.name when 'Steve' then cast( :opt1 as string ) else cast( :opt2 as string) end from Person p" ) - .setString( "opt1", "x" ) - .setString( "opt2", "y" ) - .list(); - - t.commit(); - s.close(); } @Test @@ -116,29 +124,36 @@ public class CaseStatementTest extends BaseCoreFunctionalTestCase { @Test public void testSearchedCaseStatementWithAllParamResults() { - Session s = openSession(); - Transaction t = s.beginTransaction(); + try ( final SessionImplementor s = (SessionImplementor) openSession() ) { + inTransaction( + s, + session-> { + try { + s.createQuery( "select case when p.name = 'Steve' then :opt1 else :opt2 end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + fail( "was expecting an exception" ); + } + catch (IllegalArgumentException e) { + assertTyping( QueryException.class, e.getCause() ); + } + catch (QueryException expected) { + // expected + } + } + ); - try { - s.createQuery( "select case when p.name = 'Steve' then :opt1 else :opt2 end from Person p" ) - .setString( "opt1", "x" ) - .setString( "opt2", "y" ) - .list(); - fail( "was expecting an exception" ); - } - catch (IllegalArgumentException e) { - assertTyping( QueryException.class, e.getCause() ); - } - catch (QueryException expected) { - // expected - } + inTransaction( + s, + session-> { + s.createQuery( "select case when p.name = 'Steve' then cast( :opt1 as string) else :opt2 end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); - s.createQuery( "select case when p.name = 'Steve' then cast( :opt1 as string) else :opt2 end from Person p" ) - .setString( "opt1", "x" ) - .setString( "opt2", "y" ) - .list(); - - t.commit(); - s.close(); + } + ); + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/EntityTransactionTests.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/EntityTransactionTests.java index a5a4be0924..bbe1b6d478 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/EntityTransactionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/EntityTransactionTests.java @@ -6,18 +6,25 @@ */ package org.hibernate.test.jpa.compliance.tck2_2; +import javax.persistence.RollbackException; + import org.hibernate.Transaction; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.resource.transaction.spi.TransactionStatus; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; +import org.hamcrest.CoreMatchers; + +import static org.hamcrest.MatcherAssert.assertThat; import static org.hibernate.testing.transaction.TransactionUtil2.inSession; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -60,6 +67,78 @@ public class EntityTransactionTests extends BaseUnitTestCase { } } + @Test + public void testSetRollbackOnlyOutcomeExpectations() { + final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() + .applySetting( AvailableSettings.JPA_TRANSACTION_COMPLIANCE, "true" ) + .build(); + + try { + final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) new MetadataSources( ssr ) + .buildMetadata() + .buildSessionFactory(); + + try { + inSession( + sessionFactory, + session -> { + final Transaction transaction = session.getTransaction(); + transaction.begin(); + + try { + assertTrue( transaction.isActive() ); + + transaction.setRollbackOnly(); + assertTrue( transaction.isActive() ); + assertTrue( transaction.getRollbackOnly() ); + } + finally { + if ( transaction.isActive() ) { + transaction.rollback(); + } + } + } + ); + + inSession( + sessionFactory, + session -> { + final Transaction transaction = session.getTransaction(); + transaction.begin(); + + try { + assertTrue( transaction.isActive() ); + + transaction.setRollbackOnly(); + assertTrue( transaction.isActive() ); + assertTrue( transaction.getRollbackOnly() ); + + // now try to commit, this should force a rollback + try { + transaction.commit(); + } + catch (RollbackException e) { + assertFalse( transaction.isActive() ); + assertThat( transaction.getStatus(), CoreMatchers.is( TransactionStatus.ROLLED_BACK ) ); + } + } + finally { + if ( transaction.isActive() ) { + transaction.rollback(); + } + } + } + ); + } + finally { + sessionFactory.close(); + } + } + finally { + StandardServiceRegistryBuilder.destroy( ssr ); + } + } + @Test public void testSetRollbackOnlyExpectations() { final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/QueryApiTest.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/QueryApiTest.java index 0f9d67d8fe..619b5db9f4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/QueryApiTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/compliance/tck2_2/QueryApiTest.java @@ -7,6 +7,7 @@ package org.hibernate.test.jpa.compliance.tck2_2; import java.util.Date; +import java.util.Map; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Parameter; @@ -16,12 +17,14 @@ import javax.persistence.TemporalType; import javax.persistence.TransactionRequiredException; import org.hibernate.boot.MetadataSources; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.Test; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -38,6 +41,12 @@ public class QueryApiTest extends BaseNonConfigCoreFunctionalTestCase { Date dob; } + @Override + protected void addSettings(Map settings) { + super.addSettings( settings ); + settings.put( AvailableSettings.JPA_TRANSACTION_COMPLIANCE, "true" ); + } + @Override protected void applyMetadataSources(MetadataSources sources) { super.applyMetadataSources( sources ); @@ -181,7 +190,7 @@ public class QueryApiTest extends BaseNonConfigCoreFunctionalTestCase { session -> { try { // Query - session.createQuery( "select p from Person p where name = ?" ).setMaxResults( -3 ); + session.createQuery( "select p from Person p where name = ?1" ).setMaxResults( -3 ); fail( "expecting failure" ); } catch (IllegalArgumentException expected) { @@ -207,4 +216,22 @@ public class QueryApiTest extends BaseNonConfigCoreFunctionalTestCase { } ); } + + @Test + public void testInvalidQueryMarksTxnForRollback() { + inSession( + session -> { + try { + assertFalse( session.getTransaction().isActive() ); + // Query + session.createQuery( "invalid" ).list(); + fail( "expecting failure" ); + } + catch (IllegalArgumentException expected) { + assertTrue( session.getTransaction().isActive() ); + assertTrue( session.getTransaction().getRollbackOnly() ); + } + } + ); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/BasicJdbcTransactionTests.java b/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/BasicJdbcTransactionTests.java index 86f182d638..cc894366ed 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/BasicJdbcTransactionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/test/resource/transaction/jdbc/BasicJdbcTransactionTests.java @@ -7,104 +7,144 @@ package org.hibernate.test.resource.transaction.jdbc; import org.hibernate.TransactionException; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl; import org.hibernate.resource.transaction.spi.TransactionCoordinator; +import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.resource.transaction.spi.TransactionStatus; +import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.test.resource.common.SynchronizationCollectorImpl; import org.hibernate.test.resource.common.SynchronizationErrorImpl; import org.junit.Test; +import org.hamcrest.CoreMatchers; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hibernate.testing.transaction.TransactionUtil2.inSession; import static org.junit.Assert.assertEquals; /** * @author Steve Ebersole */ -public class BasicJdbcTransactionTests { +public class BasicJdbcTransactionTests extends BaseUnitTestCase { + + private SessionFactoryImplementor generateSessionFactory() { + final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder() + // should be the default, but lets be specific about which we want to test + .applySetting( AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, "jdbc" ) + .build(); + try { + return (SessionFactoryImplementor) new MetadataSources( ssr ).buildMetadata().buildSessionFactory(); + } + catch (Exception e) { + StandardServiceRegistryBuilder.destroy( ssr ); + throw e; + } + } @Test - public void basicUsageTest() throws Exception { - final TransactionCoordinatorOwnerTestingImpl owner = new TransactionCoordinatorOwnerTestingImpl(); - final JdbcResourceLocalTransactionCoordinatorBuilderImpl transactionCoordinatorBuilder = - new JdbcResourceLocalTransactionCoordinatorBuilderImpl(); + public void basicUsageTest() { + try ( final SessionFactoryImplementor sf = generateSessionFactory() ) { + inSession( + sf, + session-> { + final TransactionCoordinator coordinator = session.getTransactionCoordinator(); - final TransactionCoordinator transactionCoordinator = transactionCoordinatorBuilder.buildTransactionCoordinator( - owner, - () -> false - ); + final SynchronizationCollectorImpl sync = new SynchronizationCollectorImpl(); + coordinator.getLocalSynchronizations() + .registerSynchronization( sync ); - SynchronizationCollectorImpl sync = new SynchronizationCollectorImpl(); - transactionCoordinator.getLocalSynchronizations().registerSynchronization( sync ); + coordinator.getTransactionDriverControl().begin(); + assertEquals( 0, sync.getBeforeCompletionCount() ); + assertEquals( 0, sync.getSuccessfulCompletionCount() ); + assertEquals( 0, sync.getFailedCompletionCount() ); - transactionCoordinator.getTransactionDriverControl().begin(); - assertEquals( 0, sync.getBeforeCompletionCount() ); - assertEquals( 0, sync.getSuccessfulCompletionCount() ); - assertEquals( 0, sync.getFailedCompletionCount() ); - - transactionCoordinator.getTransactionDriverControl().commit(); - assertEquals( 1, sync.getBeforeCompletionCount() ); - assertEquals( 1, sync.getSuccessfulCompletionCount() ); - assertEquals( 0, sync.getFailedCompletionCount() ); + coordinator.getTransactionDriverControl().commit(); + assertEquals( 1, sync.getBeforeCompletionCount() ); + assertEquals( 1, sync.getSuccessfulCompletionCount() ); + assertEquals( 0, sync.getFailedCompletionCount() ); + } + ); + } } @Test @SuppressWarnings("EmptyCatchBlock") public void testMarkRollbackOnly() { - final TransactionCoordinatorOwnerTestingImpl owner = new TransactionCoordinatorOwnerTestingImpl(); - final JdbcResourceLocalTransactionCoordinatorBuilderImpl transactionCoordinatorBuilder = - new JdbcResourceLocalTransactionCoordinatorBuilderImpl(); + try ( final SessionFactoryImplementor sf = generateSessionFactory() ) { + inSession( + sf, + session-> { + final TransactionCoordinator coordinator = session.getTransactionCoordinator(); - final TransactionCoordinator transactionCoordinator = transactionCoordinatorBuilder.buildTransactionCoordinator( - owner, - () -> false - ); - assertEquals( TransactionStatus.NOT_ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() ); + assertEquals( TransactionStatus.NOT_ACTIVE, coordinator.getTransactionDriverControl().getStatus() ); - transactionCoordinator.getTransactionDriverControl().begin(); - assertEquals( TransactionStatus.ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() ); + session.getTransaction().begin(); + assertEquals( TransactionStatus.ACTIVE, coordinator.getTransactionDriverControl().getStatus() ); - transactionCoordinator.getTransactionDriverControl().markRollbackOnly(); - assertEquals( TransactionStatus.MARKED_ROLLBACK, transactionCoordinator.getTransactionDriverControl().getStatus() ); + session.getTransaction().markRollbackOnly(); + assertEquals( TransactionStatus.MARKED_ROLLBACK, coordinator.getTransactionDriverControl().getStatus() ); + + try { + session.getTransaction().commit(); + } + catch (TransactionException expected) { + } + finally { + assertThat( + coordinator.getTransactionDriverControl().getStatus(), + anyOf( + is( TransactionStatus.NOT_ACTIVE ), + is( TransactionStatus.ROLLED_BACK ) + ) + ); + } + } + ); - try { - transactionCoordinator.getTransactionDriverControl().commit(); - } - catch (TransactionException expected) { - } - finally { - assertEquals( TransactionStatus.NOT_ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() ); } } @Test @SuppressWarnings("EmptyCatchBlock") public void testSynchronizationFailure() { - final TransactionCoordinatorOwnerTestingImpl owner = new TransactionCoordinatorOwnerTestingImpl(); - final JdbcResourceLocalTransactionCoordinatorBuilderImpl transactionCoordinatorBuilder = - new JdbcResourceLocalTransactionCoordinatorBuilderImpl(); + try ( final SessionFactoryImplementor sf = generateSessionFactory() ) { + inSession( + sf, + session -> { + final TransactionCoordinator coordinator = session.getTransactionCoordinator(); - final TransactionCoordinator transactionCoordinator = transactionCoordinatorBuilder.buildTransactionCoordinator( - owner, - () -> false - ); + assertEquals( TransactionStatus.NOT_ACTIVE, coordinator.getTransactionDriverControl().getStatus() ); + coordinator.getLocalSynchronizations().registerSynchronization( SynchronizationErrorImpl.forBefore() ); - assertEquals( TransactionStatus.NOT_ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() ); - transactionCoordinator.getLocalSynchronizations().registerSynchronization( SynchronizationErrorImpl.forBefore() ); + coordinator.getTransactionDriverControl().begin(); + assertEquals( TransactionStatus.ACTIVE, coordinator.getTransactionDriverControl().getStatus() ); - transactionCoordinator.getTransactionDriverControl().begin(); - assertEquals( TransactionStatus.ACTIVE, transactionCoordinator.getTransactionDriverControl().getStatus() ); - - try { - transactionCoordinator.getTransactionDriverControl().commit(); - } - catch (Exception expected) { - } - finally { - assertEquals( - TransactionStatus.NOT_ACTIVE, - transactionCoordinator.getTransactionDriverControl().getStatus() + try { + coordinator.getTransactionDriverControl().commit(); + } + catch (Exception expected) { + } + finally { + assertThat( + coordinator.getTransactionDriverControl().getStatus(), + anyOf( + is( TransactionStatus.NOT_ACTIVE ), + is( TransactionStatus.ROLLED_BACK ) + ) + ); + } + } ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/H2ProcTesting.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/H2ProcTesting.java new file mode 100644 index 0000000000..a4851d2433 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/H2ProcTesting.java @@ -0,0 +1,223 @@ +/* + * 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.sql.storedproc; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityResult; +import javax.persistence.FieldResult; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQueries; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.QueryHint; +import javax.persistence.SqlResultSetMapping; +import javax.persistence.StoredProcedureParameter; + +import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; + +/** + * @author Steve Ebersole + */ +public class H2ProcTesting { + public static void applyProcDefinitions(Configuration configuration) { + configuration.addAuxiliaryDatabaseObject( + new AuxiliaryDatabaseObject() { + @Override + public String getExportIdentifier() { + return "function:findOneUser"; + } + + @Override + public boolean appliesToDialect(Dialect dialect) { + return H2Dialect.class.isInstance( dialect ); + } + + @Override + public boolean beforeTablesOnCreation() { + return false; + } + + @Override + public String[] sqlCreateStrings(Dialect dialect) { + return new String[] { + "CREATE ALIAS findOneUser AS $$\n" + + "import org.h2.tools.SimpleResultSet;\n" + + "import java.sql.*;\n" + + "@CODE\n" + + "ResultSet findOneUser() {\n" + + " SimpleResultSet rs = new SimpleResultSet();\n" + + " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + + " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + + " rs.addRow(1, \"Steve\");\n" + + " return rs;\n" + + "}\n" + + "$$" + }; + } + + @Override + public String[] sqlDropStrings(Dialect dialect) { + return new String[] { + "DROP ALIAS findUser IF EXISTS" + }; + } + } + ); + + configuration.addAuxiliaryDatabaseObject( + new AuxiliaryDatabaseObject() { + @Override + public String getExportIdentifier() { + return "function:findUsers"; + } + + @Override + public boolean appliesToDialect(Dialect dialect) { + return H2Dialect.class.isInstance( dialect ); + } + + @Override + public boolean beforeTablesOnCreation() { + return false; + } + + @Override + public String[] sqlCreateStrings(Dialect dialect) { + return new String[] { + "CREATE ALIAS findUsers AS $$\n" + + "import org.h2.tools.SimpleResultSet;\n" + + "import java.sql.*;\n" + + "@CODE\n" + + "ResultSet findUsers() {\n" + + " SimpleResultSet rs = new SimpleResultSet();\n" + + " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + + " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + + " rs.addRow(1, \"Steve\");\n" + + " rs.addRow(2, \"John\");\n" + + " rs.addRow(3, \"Jane\");\n" + + " return rs;\n" + + "}\n" + + "$$" + }; + } + + @Override + public String[] sqlDropStrings(Dialect dialect) { + return new String[] {"DROP ALIAS findUser IF EXISTS"}; + } + } + ); + + configuration.addAuxiliaryDatabaseObject( + new AuxiliaryDatabaseObject() { + @Override + public String getExportIdentifier() { + return "function:findUserRange"; + } + + @Override + public boolean appliesToDialect(Dialect dialect) { + return H2Dialect.class.isInstance( dialect ); + } + + @Override + public boolean beforeTablesOnCreation() { + return false; + } + + @Override + public String[] sqlCreateStrings(Dialect dialect) { + return new String[] { + "CREATE ALIAS findUserRange AS $$\n" + + "import org.h2.tools.SimpleResultSet;\n" + + "import java.sql.*;\n" + + "@CODE\n" + + "ResultSet findUserRange(int start, int end) {\n" + + " SimpleResultSet rs = new SimpleResultSet();\n" + + " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + + " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + + " for ( int i = start; i < end; i++ ) {\n" + + " rs.addRow(1, \"User \" + i );\n" + + " }\n" + + " return rs;\n" + + "}\n" + + "$$" + }; + } + + @Override + public String[] sqlDropStrings(Dialect dialect) { + return new String[] {"DROP ALIAS findUserRange IF EXISTS"}; + } + } + ); + + } + + @Entity + @NamedStoredProcedureQueries( { + @NamedStoredProcedureQuery( + name = "findUserRangeNoNullPassing", + procedureName = "findUserRange", + parameters = { + @StoredProcedureParameter( type = Integer.class ), + @StoredProcedureParameter( type = Integer.class ), + } + ), + @NamedStoredProcedureQuery( + name = "findUserRangeNamedNullPassing", + procedureName = "findUserRange", + hints = @QueryHint( name = "hibernate.proc.param_null_passing.firstArg", value = "true" ), + parameters = { + @StoredProcedureParameter( name = "firstArg", type = Integer.class ), + @StoredProcedureParameter( name = "secondArg", type = Integer.class ), + } + ), + @NamedStoredProcedureQuery( + name = "findUserRangeOrdinalNullPassing", + procedureName = "findUserRange", + hints = @QueryHint( name = "hibernate.proc.param_null_passing.1", value = "true" ), + parameters = { + @StoredProcedureParameter( type = Integer.class ), + @StoredProcedureParameter( type = Integer.class ), + } + ) + } ) + @SqlResultSetMapping( + name = "all-fields", + entities = @EntityResult( + entityClass = MyEntity.class, + fields = { + @FieldResult( name = "id", column = "id" ), + @FieldResult( name = "name", column = "name" ) + } + ) + ) + @SqlResultSetMapping( + name = "some-fields", + entities = @EntityResult( + entityClass = MyEntity.class, + fields = { + @FieldResult( name = "id", column = "id" ) + } + ) + ) + @SqlResultSetMapping( + name = "no-fields", + entities = @EntityResult( + entityClass = MyEntity.class + ) + ) + public static class MyEntity { + @Id + public Integer id; + String name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/ResultMappingTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/ResultMappingTest.java new file mode 100644 index 0000000000..4db8e3b682 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/ResultMappingTest.java @@ -0,0 +1,111 @@ +/* + * 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.sql.storedproc; + +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.procedure.ProcedureCall; +import org.hibernate.procedure.ProcedureOutputs; +import org.hibernate.result.Output; +import org.hibernate.result.ResultSetOutput; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Steve Ebersole + */ +@RequiresDialect( H2Dialect.class ) +public class ResultMappingTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { H2ProcTesting.MyEntity.class }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + H2ProcTesting.applyProcDefinitions( configuration ); + } + + @Test + public void testResultClass() { + inTransaction( + sessionFactory(), + session -> { + final ProcedureCall call = session.createStoredProcedureCall( + "findOneUser", + H2ProcTesting.MyEntity.class + ); + final ProcedureOutputs procedureResult = call.getOutputs(); + final Output currentOutput = procedureResult.getCurrent(); + assertNotNull( currentOutput ); + final ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); + final Object result = resultSetReturn.getSingleResult(); + assertTyping( H2ProcTesting.MyEntity.class, result ); + assertEquals( "Steve", ( (H2ProcTesting.MyEntity) result ).name ); + } + ); + } + + @Test + public void testMappingAllFields() { + inTransaction( + sessionFactory(), + session -> { + final ProcedureCall call = session.createStoredProcedureCall( "findOneUser", "all-fields" ); + final ProcedureOutputs procedureResult = call.getOutputs(); + final Output currentOutput = procedureResult.getCurrent(); + assertNotNull( currentOutput ); + final ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); + final Object result = resultSetReturn.getSingleResult(); + assertTyping( H2ProcTesting.MyEntity.class, result ); + assertEquals( "Steve", ( (H2ProcTesting.MyEntity) result ).name ); + } + ); + } + + @Test + public void testMappingSomeFields() { + inTransaction( + sessionFactory(), + session -> { + final ProcedureCall call = session.createStoredProcedureCall( "findOneUser", "some-fields" ); + final ProcedureOutputs procedureResult = call.getOutputs(); + final Output currentOutput = procedureResult.getCurrent(); + assertNotNull( currentOutput ); + final ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); + final Object result = resultSetReturn.getSingleResult(); + assertTyping( H2ProcTesting.MyEntity.class, result ); + assertEquals( "Steve", ( (H2ProcTesting.MyEntity) result ).name ); + } + ); + } + + @Test + public void testMappingNoFields() { + inTransaction( + sessionFactory(), + session -> { + final ProcedureCall call = session.createStoredProcedureCall( "findOneUser", "no-fields" ); + final ProcedureOutputs procedureResult = call.getOutputs(); + final Output currentOutput = procedureResult.getCurrent(); + assertNotNull( currentOutput ); + final ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); + final Object result = resultSetReturn.getSingleResult(); + assertTyping( H2ProcTesting.MyEntity.class, result ); + assertEquals( "Steve", ( (H2ProcTesting.MyEntity) result ).name ); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java index 0be6cd595d..7a35e2d5ed 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java @@ -7,19 +7,11 @@ package org.hibernate.test.sql.storedproc; import java.util.List; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.NamedStoredProcedureQueries; -import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.ParameterMode; -import javax.persistence.QueryHint; -import javax.persistence.StoredProcedureParameter; import org.hibernate.JDBCException; import org.hibernate.Session; -import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.cfg.Configuration; -import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureOutputs; @@ -31,6 +23,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -42,143 +35,13 @@ import static org.junit.Assert.fail; public class StoredProcedureTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { - return new Class[] { MyEntity.class }; + return new Class[] { H2ProcTesting.MyEntity.class }; } @Override protected void configure(Configuration configuration) { super.configure( configuration ); - configuration.addAuxiliaryDatabaseObject( - new AuxiliaryDatabaseObject() { - @Override - public String getExportIdentifier() { - return "function:findOneUser"; - } - - @Override - public boolean appliesToDialect(Dialect dialect) { - return H2Dialect.class.isInstance( dialect ); - } - - @Override - public boolean beforeTablesOnCreation() { - return false; - } - - @Override - public String[] sqlCreateStrings(Dialect dialect) { - return new String[] { - "CREATE ALIAS findOneUser AS $$\n" + - "import org.h2.tools.SimpleResultSet;\n" + - "import java.sql.*;\n" + - "@CODE\n" + - "ResultSet findOneUser() {\n" + - " SimpleResultSet rs = new SimpleResultSet();\n" + - " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + - " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + - " rs.addRow(1, \"Steve\");\n" + - " return rs;\n" + - "}\n" + - "$$" - }; - } - - @Override - public String[] sqlDropStrings(Dialect dialect) { - return new String[] { - "DROP ALIAS findUser IF EXISTS" - }; - } - } - ); - - configuration.addAuxiliaryDatabaseObject( - new AuxiliaryDatabaseObject() { - @Override - public String getExportIdentifier() { - return "function:findUsers"; - } - - @Override - public boolean appliesToDialect(Dialect dialect) { - return H2Dialect.class.isInstance( dialect ); - } - - @Override - public boolean beforeTablesOnCreation() { - return false; - } - - @Override - public String[] sqlCreateStrings(Dialect dialect) { - return new String[] { - "CREATE ALIAS findUsers AS $$\n" + - "import org.h2.tools.SimpleResultSet;\n" + - "import java.sql.*;\n" + - "@CODE\n" + - "ResultSet findUsers() {\n" + - " SimpleResultSet rs = new SimpleResultSet();\n" + - " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + - " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + - " rs.addRow(1, \"Steve\");\n" + - " rs.addRow(2, \"John\");\n" + - " rs.addRow(3, \"Jane\");\n" + - " return rs;\n" + - "}\n" + - "$$" - }; - } - - @Override - public String[] sqlDropStrings(Dialect dialect) { - return new String[] {"DROP ALIAS findUser IF EXISTS"}; - } - } - ); - - configuration.addAuxiliaryDatabaseObject( - new AuxiliaryDatabaseObject() { - @Override - public String getExportIdentifier() { - return "function:findUserRange"; - } - - @Override - public boolean appliesToDialect(Dialect dialect) { - return H2Dialect.class.isInstance( dialect ); - } - - @Override - public boolean beforeTablesOnCreation() { - return false; - } - - @Override - public String[] sqlCreateStrings(Dialect dialect) { - return new String[] { - "CREATE ALIAS findUserRange AS $$\n" + - "import org.h2.tools.SimpleResultSet;\n" + - "import java.sql.*;\n" + - "@CODE\n" + - "ResultSet findUserRange(int start, int end) {\n" + - " SimpleResultSet rs = new SimpleResultSet();\n" + - " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + - " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + - " for ( int i = start; i < end; i++ ) {\n" + - " rs.addRow(1, \"User \" + i );\n" + - " }\n" + - " return rs;\n" + - "}\n" + - "$$" - }; - } - - @Override - public String[] sqlDropStrings(Dialect dialect) { - return new String[] {"DROP ALIAS findUserRange IF EXISTS"}; - } - } - ); + H2ProcTesting.applyProcDefinitions( configuration ); } @Test @@ -413,37 +276,4 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { session.close(); } - @Entity - @NamedStoredProcedureQueries( { - @NamedStoredProcedureQuery( - name = "findUserRangeNoNullPassing", - procedureName = "findUserRange", - parameters = { - @StoredProcedureParameter( type = Integer.class ), - @StoredProcedureParameter( type = Integer.class ), - } - ), - @NamedStoredProcedureQuery( - name = "findUserRangeNamedNullPassing", - procedureName = "findUserRange", - hints = @QueryHint( name = "hibernate.proc.param_null_passing.firstArg", value = "true" ), - parameters = { - @StoredProcedureParameter( name = "firstArg", type = Integer.class ), - @StoredProcedureParameter( name = "secondArg", type = Integer.class ), - } - ), - @NamedStoredProcedureQuery( - name = "findUserRangeOrdinalNullPassing", - procedureName = "findUserRange", - hints = @QueryHint( name = "hibernate.proc.param_null_passing.1", value = "true" ), - parameters = { - @StoredProcedureParameter( type = Integer.class ), - @StoredProcedureParameter( type = Integer.class ), - } - ) - } ) - public static class MyEntity { - @Id - public Integer id; - } } diff --git a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionCoordinator.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionCoordinator.java index 39ce9ddd51..ac7aa78d6d 100644 --- a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionCoordinator.java +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionCoordinator.java @@ -2,6 +2,7 @@ package org.hibernate.test.cache.infinispan.util; import java.sql.Connection; import java.sql.SQLException; +import java.util.Collections; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; @@ -13,6 +14,8 @@ import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.transaction.spi.IsolationDelegate; import org.hibernate.engine.transaction.spi.TransactionObserver; +import org.hibernate.jpa.JpaCompliance; +import org.hibernate.jpa.spi.JpaComplianceImpl; import org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate; import org.hibernate.resource.transaction.backend.jta.internal.StatusTranslator; import org.hibernate.resource.transaction.spi.SynchronizationRegistry; @@ -112,6 +115,11 @@ public class BatchModeTransactionCoordinator implements TransactionCoordinator { }; } + @Override + public JpaCompliance getJpaCompliance() { + return new JpaComplianceImpl( Collections.emptyMap(), false ); + } + @Override public boolean isActive() { try { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil2.java b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil2.java index 965f2a9279..3a00bc9b6a 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil2.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil2.java @@ -58,24 +58,43 @@ public class TransactionUtil2 { action.accept( session ); log.trace( "Called action - in txn" ); - log.trace( "Committing transaction" ); - txn.commit(); - log.trace( "Committed transaction" ); + if ( txn.isActive() ) { + if ( txn.getRollbackOnly() ) { + log.trace( "Rolling back transaction due to being marked for rollback only" ); + txn.rollback(); + log.trace( "Rolled back transaction due to being marked for rollback only" ); + } + else { + log.trace( "Committing transaction" ); + txn.commit(); + log.trace( "Committed transaction" ); + } + } } catch (Exception e) { - log.tracef( - "Error calling action: %s (%s) - rolling back", - e.getClass().getName(), - e.getMessage() - ); - try { - txn.rollback(); + if ( txn.isActive() ) { + log.tracef( + "Error calling action: %s (%s) - rolling back", + e.getClass().getName(), + e.getMessage() + ); + + try { + txn.rollback(); + } + catch (Exception ignore) { + log.trace( "Was unable to roll back transaction" ); + // really nothing else we can do here - the attempt to + // rollback already failed and there is nothing else + // to clean up. + } } - catch (Exception ignore) { - log.trace( "Was unable to roll back transaction" ); - // really nothing else we can do here - the attempt to - // rollback already failed and there is nothing else - // to clean up. + else { + log.tracef( + "Error calling action: %s (%s) - transaction was already rolled back", + e.getClass().getName(), + e.getMessage() + ); } throw e;