Allow Hibernate's Transaction act like JPA's EntityTransaction

This commit is contained in:
Steve Ebersole 2017-11-30 20:16:01 -06:00
parent 3a1eb3382b
commit ad0aa213bc
21 changed files with 1044 additions and 451 deletions

View File

@ -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 );
}

View File

@ -287,6 +287,7 @@ public class SequenceStyleGenerator
fallbackSequenceName = generatorName;
}
}
// JPA_ENTITY_NAME value honors <class ... entity-name="..."> (HBM) and @Entity#name (JPA) overrides.
final String defaultSequenceName = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEQUENCE_PER_ENTITY, params, false )
? params.getProperty( JPA_ENTITY_NAME ) + sequencePerEntitySuffix

View File

@ -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 );
}
}

View File

@ -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<Return> generateCustomReturns(boolean queryHadAliases) {
List<Return> customReturns = new ArrayList<Return>();
Map<String,Return> customReturnsByAlias = new HashMap<String,Return>();
@ -354,6 +389,58 @@ public class SQLQueryReturnProcessor {
return customReturns;
}
public List<Return> generateCallableReturns() {
final List<Return> 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) ) {

View File

@ -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(

View File

@ -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)",

View File

@ -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<TransactionObserver>();
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<TransactionObserver>();
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 ) {

View File

@ -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?
* <p/>
@ -85,12 +94,6 @@ public interface TransactionCoordinator {
*/
void removeObserver(TransactionObserver observer);
/**
*
* @return
*/
TransactionCoordinatorBuilder getTransactionCoordinatorBuilder();
void setTimeOut(int seconds);
int getTimeOut();

View File

@ -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<org.hibernate.loader.custom.Return> customReturns = processor.generateCustomReturns( false );
final List<org.hibernate.loader.custom.Return> 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<Return> customQueryReturns) {
final List<EntityAliases> 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 {

View File

@ -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" );
}
}

View File

@ -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();

View File

@ -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

View File

@ -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();
}
);
}
}
}

View File

@ -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()

View File

@ -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() );
}
}
);
}
}

View File

@ -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 )
)
);
}
}
);
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
);
}
}

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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;