From fd6aadcd2a9f9c1892545edccc2a5c3731508283 Mon Sep 17 00:00:00 2001 From: Andrei Ivanov Date: Mon, 7 Mar 2016 15:54:18 +0200 Subject: [PATCH] HHH-10510 - Root cause not properly extracted when transaction commit fails --- .../jpa/internal/TransactionImpl.java | 7 +- .../TransactionCommitFailureTest.java | 110 ++++++++++++++++++ 2 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionCommitFailureTest.java diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/TransactionImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/TransactionImpl.java index eb8a086384..9360e1fb27 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/TransactionImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/TransactionImpl.java @@ -63,11 +63,12 @@ public class TransactionImpl implements EntityTransaction { catch (Exception e) { Throwable wrappedException; if ( e instanceof PersistenceException ) { - if ( e.getCause() instanceof HibernateException ) { - wrappedException = entityManager.convert( (HibernateException) e.getCause() ); + Throwable cause = e.getCause() == null ? e : e.getCause(); + if ( cause instanceof HibernateException ) { + wrappedException = entityManager.convert( (HibernateException) cause ); } else { - wrappedException = e.getCause(); + wrappedException = cause; } } else if ( e instanceof HibernateException ) { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionCommitFailureTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionCommitFailureTest.java new file mode 100644 index 0000000000..854efa2c00 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/transaction/TransactionCommitFailureTest.java @@ -0,0 +1,110 @@ +/* + * 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 . + */ +package org.hibernate.jpa.test.transaction; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceException; + +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.jpa.boot.spi.Bootstrap; +import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter; +import org.hibernate.jpa.test.SettingsGenerator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +public class TransactionCommitFailureTest { + + public static final String COMMIT_FAILURE = "Commit failed!"; + + private static final AtomicBoolean transactionFailureTrigger = new AtomicBoolean( false ); + + @Test + public void testConfiguredInterceptor() { + Map settings = basicSettings(); + EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder( new PersistenceUnitDescriptorAdapter(), settings ).build(); + EntityManager em = emf.createEntityManager(); + + try { + em.getTransaction().begin(); + transactionFailureTrigger.set( true ); + em.getTransaction().commit(); + } + catch ( Exception e ) { + assertEquals(PersistenceException.class, e.getCause().getClass()); + assertEquals(COMMIT_FAILURE, e.getCause().getMessage()); + } + finally { + if ( em.getTransaction() != null && em.getTransaction().isActive() ) { + em.getTransaction().rollback(); + } + em.close(); + emf.close(); + } + } + + protected Map basicSettings() { + return SettingsGenerator.generateSettings( + Environment.HBM2DDL_AUTO, "create-drop", + Environment.USE_NEW_ID_GENERATOR_MAPPINGS, "true", + Environment.DIALECT, Dialect.getDialect().getClass().getName(), + Environment.CONNECTION_PROVIDER, ProxyConnectionProvider.class.getName() + ); + } + + public static class ProxyConnectionProvider extends DriverManagerConnectionProviderImpl { + + @Override + public Connection getConnection() throws SQLException { + Connection delegate = super.getConnection(); + return (Connection) Proxy.newProxyInstance( + this.getClass().getClassLoader(), + new Class[]{Connection.class}, + new ConnectionInvocationHandler(delegate)); + } + } + + private static class ConnectionInvocationHandler implements InvocationHandler { + + private final Connection delegate; + + public ConnectionInvocationHandler(Connection delegate) { + this.delegate = delegate; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if("commit".equals( method.getName() )) { + if ( transactionFailureTrigger.get() ) { + throw new PersistenceException( COMMIT_FAILURE ); + } + } + else if("rollback".equals( method.getName() )) { + if ( transactionFailureTrigger.get() ) { + transactionFailureTrigger.set( false ); + throw new PersistenceException( "Rollback failed!" ); + } + } + return method.invoke(delegate, args); + } + } + +}