【问题标题】:spring junit test with two hibernate transactions带有两个休眠事务的spring junit测试
【发布时间】:2012-04-05 10:11:19
【问题描述】:

我有 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 测试?

【问题讨论】:

    标签: spring unit-testing transactions junit


    【解决方案1】:

    听起来测试是在数据库中的 Contract 表上创建死锁。其根本原因很可能是使用了REQUIRES_NEW 传播级别,如this question 中所述。重要的部分是:

    PROPAGATION_REQUIRES_NEW 为给定范围启动一个新的、独立的“内部”事务。该事务将完全独立于外部事务提交或回滚,具有自己的隔离范围,自己的一组锁等。外部事务将在内部事务开始时暂停,并在内部事务完成后恢复完成

    createContractTransactional 方法正在尝试插入到 Contract 表中,但测试中的某些东西必须对其进行锁定,我猜这是对 persistenceManagerHibernate.isCreated(Contract.class, contract.getId()) 的调用。无论是什么原因,您都有两个独立的事务锁定在同一个表上,即死锁。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 2023-03-20
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多