/*
* 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.jpa.test.transaction;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.SynchronizationType;
import javax.persistence.TransactionRequiredException;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaUpdate;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.jta.TestingJtaPlatformImpl;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests of the JPA 2.1 added {@link SynchronizationType} handling. {@link SynchronizationType#SYNCHRONIZED} is
* the same as 2.0 behavior, so we do not explicitly test for that ({@link TransactionJoiningTest} handles it).
* Tests here specifically test the {@link SynchronizationType#UNSYNCHRONIZED} behavior
*
* @author Steve Ebersole
*/
@TestForIssue( jiraKey = "HHH-7451" )
public class SynchronizationTypeTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );
TestingJtaBootstrap.prepare( options );
options.put( AvailableSettings.TRANSACTION_TYPE, "JTA" );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Book.class };
}
@Test
public void testUnSynchronizedExplicitJoinHandling() throws Exception {
// JPA 2.1 adds this notion allowing to open an EM using a specified "SynchronizationType".
assertFalse( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
EntityManager entityManager = entityManagerFactory().createEntityManager( SynchronizationType.UNSYNCHRONIZED, null );
TransactionJoinHandlingChecker.validateExplicitJoiningHandling( entityManager );
}
@Test
public void testImplicitJoining() throws Exception {
// here the transaction is started before the EM is opened. Because the SynchronizationType is UNSYNCHRONIZED
// though, it should not auto join the transaction
assertFalse( "setup problem", JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
assertTrue( "setup problem", JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
EntityManager entityManager = entityManagerFactory().createEntityManager( SynchronizationType.UNSYNCHRONIZED, null );
SharedSessionContractImplementor session = entityManager.unwrap( SharedSessionContractImplementor.class );
ExtraAssertions.assertTyping( JtaTransactionCoordinatorImpl.class, session.getTransactionCoordinator() );
JtaTransactionCoordinatorImpl transactionCoordinator = (JtaTransactionCoordinatorImpl) session.getTransactionCoordinator();
assertFalse( "EM was auto joined on creation", transactionCoordinator.isSynchronizationRegistered() );
assertTrue( "EM was auto joined on creation", transactionCoordinator.isActive() );
assertFalse( "EM was auto joined on creation", transactionCoordinator.isJoined() );
session.getFlushMode();
assertFalse( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertFalse( transactionCoordinator.isJoined() );
entityManager.joinTransaction();
assertTrue( JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isSynchronizationRegistered() );
assertTrue( transactionCoordinator.isActive() );
assertTrue( transactionCoordinator.isJoined() );
assertTrue( entityManager.isOpen() );
assertTrue( session.isOpen() );
entityManager.close();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
assertFalse( entityManager.isOpen() );
assertFalse( session.isOpen() );
}
@Test
public void testDisallowedOperations() throws Exception {
// test calling operations that are disallowed while a UNSYNCHRONIZED persistence context is not
// yet joined/enlisted
assertFalse( "setup problem", JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() ) );
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
assertTrue(
"setup problem",
JtaStatusHelper.isActive( TestingJtaPlatformImpl.INSTANCE.getTransactionManager() )
);
EntityManager entityManager = entityManagerFactory().createEntityManager( SynchronizationType.UNSYNCHRONIZED, null );
// explicit flushing
try {
entityManager.flush();
fail( "Expecting flush() call to fail" );
}
catch (TransactionRequiredException expected) {
}
// bulk operations
try {
entityManager.createQuery( "delete Book" ).executeUpdate();
fail( "Expecting executeUpdate() call to fail" );
}
catch (TransactionRequiredException expected) {
}
try {
entityManager.createQuery( "update Book set name = null" ).executeUpdate();
fail( "Expecting executeUpdate() call to fail" );
}
catch (TransactionRequiredException expected) {
}
try {
CriteriaDelete<Book> deleteCriteria = entityManager.getCriteriaBuilder().createCriteriaDelete( Book.class );
deleteCriteria.from( Book.class );
entityManager.createQuery( deleteCriteria ).executeUpdate();
fail( "Expecting executeUpdate() call to fail" );
}
catch (TransactionRequiredException expected) {
}
try {
CriteriaUpdate<Book> updateCriteria = entityManager.getCriteriaBuilder().createCriteriaUpdate( Book.class );
updateCriteria.from( Book.class );
updateCriteria.set( Book_.name, (String) null );
entityManager.createQuery( updateCriteria ).executeUpdate();
fail( "Expecting executeUpdate() call to fail" );
}
catch (TransactionRequiredException expected) {
}
try {
entityManager.createQuery( "select b from Book b" )
.setLockMode( LockModeType.PESSIMISTIC_WRITE )
.getResultList();
fail( "Expecting attempted pessimistic lock query to fail" );
}
catch (TransactionRequiredException expected) {
}
entityManager.close();
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().rollback();
}
}