如何手动处理Spring 4笔交易?
How to manually handle Spring 4 transactions?
如何在单个 @Test
方法中以编程方式控制事务边界? Spring 4.x 文档有一些 clues 但我想我遗漏了一些东西,因为测试抛出错误:
java.lang.IllegalStateException:
Cannot start a new transaction without ending the existing transaction first.
测试
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();
}
}
更新
再详细一点:
所有 session.getTransaction().commit();
必须 删除,因为它们会中断事务工作流程。
TL;DR
为了避免这个问题,只需删除测试方法的第一行并使用已经可用的事务:
@Test
public void testTransactionCaching() {
// Remove this => TestTransaction.start();
// Same as before
}
详细解答
当您使用 @Transactional
注释您的测试 class 或扩展 AbstractTransactionalJUnit4SpringContextTests
:
// Other annotations
@Transactional(transactionManager = "hibernateTransactionManager")
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests { ... }
class 中的每个测试方法都将在事务中 运行。更准确地说,Spring Test Context (By using TransactionalTestExecutionListener
) 会在每个测试方法开始时打开一个事务,并在完成后回滚测试。
那么,您的 testTransactionCaching
测试方法:
@Test
public void testTransactionCaching() { ... }
一开始会有一个打开的交易,而您正尝试通过以下方式手动打开另一个交易:
TestTransaction.start();
因此出现错误:
Cannot start a new transaction without ending the existing transaction
first.
为了避免这个问题,只需删除测试方法的第一行并使用那个已经可用的事务。其他 TestTransaction.*
方法调用没问题,但只需删除第一个。
如何在单个 @Test
方法中以编程方式控制事务边界? Spring 4.x 文档有一些 clues 但我想我遗漏了一些东西,因为测试抛出错误:
java.lang.IllegalStateException:
Cannot start a new transaction without ending the existing transaction first.
测试
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();
}
}
更新
再详细一点:
所有 session.getTransaction().commit();
必须 删除,因为它们会中断事务工作流程。
TL;DR
为了避免这个问题,只需删除测试方法的第一行并使用已经可用的事务:
@Test
public void testTransactionCaching() {
// Remove this => TestTransaction.start();
// Same as before
}
详细解答
当您使用 @Transactional
注释您的测试 class 或扩展 AbstractTransactionalJUnit4SpringContextTests
:
// Other annotations
@Transactional(transactionManager = "hibernateTransactionManager")
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests { ... }
class 中的每个测试方法都将在事务中 运行。更准确地说,Spring Test Context (By using TransactionalTestExecutionListener
) 会在每个测试方法开始时打开一个事务,并在完成后回滚测试。
那么,您的 testTransactionCaching
测试方法:
@Test
public void testTransactionCaching() { ... }
一开始会有一个打开的交易,而您正尝试通过以下方式手动打开另一个交易:
TestTransaction.start();
因此出现错误:
Cannot start a new transaction without ending the existing transaction first.
为了避免这个问题,只需删除测试方法的第一行并使用那个已经可用的事务。其他 TestTransaction.*
方法调用没问题,但只需删除第一个。