具有两个休眠事务的 spring junit 测试

标签 spring unit-testing transactions junit

我有一个 spring junit 测试,它由两个作为 REQUIRES_NEW 传播的顺序事务组成:

public class ContractServiceTest extends AbstractIntegrationTest {

@Autowired
private PersistenceManagerHibernate persistenceManagerHibernate;

@Autowired
private ContractService contractService;

@Autowired
private EntityChangeService entityChangeService;

@Resource
private AddServiceService addService;

@Autowired
private ReferenceBookService refService;

@Autowired
private PropertyService propertyService;

@Autowired
private HibernateTransactionManager transactionManager;

@Test
public void testContractDeletes() {
    Long contractId = 1L;
    final Contract contract = createTestDetachedContract(contractId, PropertyServiceTest.createManaged(propertyService, refService), refService);
    ensureContractCreated(contract);
    deleteTransactional(contract);
    Assert.assertEquals(1, entityChangeService.findByPaginationOrderByUpdateDate(Contract.class.getName(), contract.getId().toString(), null, 0, 30).size());
}

@Test
@Ignore
public void testContractCreates() {
    Long contractId = 1L;
    final Contract contract = createTestDetachedContract(contractId, PropertyServiceTest.createManaged(propertyService, refService), refService);
    ensureContractDeleted(contract);
    createContractTransactional(contract);
    Assert.assertEquals(1, entityChangeService.findByPaginationOrderByUpdateDate(Contract.class.getName(), contract.getId().toString(), null, 0, 30).size());
}

private void ensureContractCreated(Contract contract) {
    if (persistenceManagerHibernate.isCreated(Contract.class, contract.getId())) {          
        return;
    }
    createContractTransactional(contract);
}

private void deleteTransactional(final Contract contract) {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
    transactionTemplate.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
            try {
                contractService.delete(contract);
            } catch (Exception e) {
                toString();
            }
            return null;
        }
    });
}

private void createContractTransactional(final Contract contract) {
    TransactionTemplate transactionTemplate2 = new TransactionTemplate(transactionManager);
    transactionTemplate2.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
    transactionTemplate2.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
            contractService.create(contract);
            return null;
        }
    });
}

private void ensureContractDeleted(final Contract contract) {
    if (!persistenceManagerHibernate.isCreated(Contract.class, contract.getId())) {
        return;
    }
    deleteTransactional(contract);
}

public static Contract createTestDetachedContract(Long contractId, Property property, ReferenceBookService refService) {
    Contract contract1 = new Contract();
    contract1.setId(contractId);
    contract1.setName("test name");
    contract1.setProperty(property);
    contract1.setNumber("10");
    contract1.setType(refService.get(ContractType.class, 1L));
    contract1.setStatus(refService.get(ContractStatus.class, 1L));
    contract1.setCreated(new Date());
    contract1.setCurrencyRate(new BigDecimal(10));
    contract1.setInitialSum(new BigDecimal(10));
    contract1.setSum(new BigDecimal(10));
    return contract1;
}
}

测试在插入 sql 语句的事务提交时卡住,即:

 private void createContractTransactional(final Contract contract) {
    TransactionTemplate transactionTemplate2 = new TransactionTemplate(transactionManager);
    transactionTemplate2.setPropagationBehavior(Propagation.REQUIRES_NEW.value());
    transactionTemplate2.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
            contractService.create(contract);
            return null;
        }
    });
}

为什么会发生这种情况(调试器在没有提供源代码的情况下停在某些 oracle 代码处)以及如何正确编写具有两个顺序事务的 spring junit 测试?

最佳答案

听起来测试在您数据库中的 Contract 表上造成了死锁。其根本原因很可能是使用了 REQUIRES_NEW 传播级别,详见 this question。 .重要的部分是:

PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed

createContractTransactional 方法试图插入到 Contract 表中,但测试的早期部分必须锁定它,我猜这是对 persistenceManagerHibernate.isCreated( Contract.class, contract.getId()).无论是什么原因,您都有两个独立的事务锁定在同一个表上,即死锁。

尝试将测试中事务的传播级别设置为 REQUIRED,这是默认设置。如果还没有,这将创建一个新事务,否则将使用当前事务。那应该使您的测试在单个事务中执行,因此您不应该陷入僵局。一旦它起作用,那么您可能想阅读 spring documentation在其传播级别上,以确保 REQUIRED 是满足您需求的正确级别。

关于具有两个休眠事务的 spring junit 测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10026647/

相关文章:

带有 ChangeDetectionStrategy.OnPush 的 Angular 单元测试不起作用

sql-server - 事务隔离级别 REPEATABLE READ 导致死锁

spring - 使用 Spring Boot 的 SPA - 为非 API 请求提供 index.html

spring - Spring MVC 可以为 HTTP PUT 方法提供请求参数,还是必须使用 post?我应该使用哪个来成为 RESTful?

java - 从 3.0 版本开始,Quartz 调度程序不再对 Spring 有用吗?

sql-server - ZombieCheck 异常 - 此 SqlTransaction 已完成;它不再可用——在简单提交期间

mysql - 单个字段上可靠的递减/递增所需的隔离级别

Spring应用程序中JSPX解析后的jQuery错误

c# - FakeItEasy:根据表达式返回不同的对象

c - 使用指针对函数(下图)进行单元测试