Reputation: 13915
How to programmatically control transaction boundaries within single @Test
method? Spring 4.x documentation has some clues but I think I missing something since the test throws error:
java.lang.IllegalStateException:
Cannot start a new transaction without ending the existing transaction first.
Test
import com.hibernate.query.performance.config.ApplicationConfig;
import com.hibernate.query.performance.config.CachingConfig;
import com.hibernate.query.performance.persistence.model.LanguageEntity;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.transaction.TestTransaction;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ApplicationConfig.class, CachingConfig.class }, loader = AnnotationConfigContextLoader.class)
@PersistenceContext
@Transactional(transactionManager = "hibernateTransactionManager")
@TestExecutionListeners({})
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests {
private static Logger logger = LoggerFactory.getLogger(EHCacheTest.class);
@BeforeClass
public static void setUpBeforeClass() throws Exception {
logger.info("setUpBeforeClass()");
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
logger.info("tearDownAfterClass()");
}
@Autowired
private SessionFactory sessionFactory;
@Test
public void testTransactionCaching(){
TestTransaction.start();
Session session = sessionFactory.getCurrentSession();
System.out.println(session.get(LanguageEntity.class, 1));
Query query = session.createQuery("from LanguageEntity le where le.languageId < 10").setCacheable(true).setCacheRegion("language");
@SuppressWarnings("unchecked")
List<LanguageEntity> customerEntities = query.list();
System.out.println(customerEntities);
session.getTransaction().commit();
TestTransaction.flagForCommit();
TestTransaction.end();
// Second Transaction
TestTransaction.start();
Session sessionNew = sessionFactory.getCurrentSession();
System.out.println(sessionNew.get(LanguageEntity.class, 1));
Query anotherQuery = sessionNew.createQuery("from LanguageEntity le where le.languageId < 10");
anotherQuery.setCacheable(true).setCacheRegion("language");
@SuppressWarnings("unchecked")
List<LanguageEntity> languagesFromCache = anotherQuery.list();
System.out.println(languagesFromCache);
sessionNew.getTransaction().commit();
TestTransaction.flagForCommit();
TestTransaction.end();
}
}
UPDATE
One more detail:
All occurences session.getTransaction().commit();
must be removed since they interrupt transaction workflow.
Upvotes: 1
Views: 4334
Reputation: 48153
In order to avoid this problem, just remove the first line of the test method and use the already available transaction:
@Test
public void testTransactionCaching() {
// Remove this => TestTransaction.start();
// Same as before
}
When you annotate your test class with @Transactional
or extending the AbstractTransactionalJUnit4SpringContextTests
:
// Other annotations
@Transactional(transactionManager = "hibernateTransactionManager")
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests { ... }
Each test method within that class will be run within a transaction. To be more precise, Spring Test Context (By using TransactionalTestExecutionListener
) would open a transaction in the beginning of each test method and roll it back after completion of the test.
So, your testTransactionCaching
test method:
@Test
public void testTransactionCaching() { ... }
Would have an open transaction in the beginning and you're trying to open another one manually by:
TestTransaction.start();
Hence the error:
Cannot start a new transaction without ending the existing transaction first.
In order to avoid this problem, just remove the first line of the test method and use that already available transaction. The other TestTransaction.*
method calls are OK but just remove the first one.
Upvotes: 4