3

我有 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;
}
}

测试在使用 insert 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 测试?

4

1 回答 1

0

It sounds like the test is creating a deadlock on the Contract table in your database. The root cause of this is most likely the use of the REQUIRES_NEW propagation level, as detailed in this question. The important part is this:

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

The createContractTransactional method is trying to insert into the Contract table but something earlier in the test must be holding a lock on it, I'm guessing it's the call to persistenceManagerHibernate.isCreated(Contract.class, contract.getId()). Whatever the cause, you have two independent transactions that are locking on the same table, i.e. a deadlock.

Try setting the propagation level the transactions in your test to REQUIRED, which is the default setting. This creates a new transaction if there isn't one already, otherwise the current transaction is used. That should make your test execute in a single transaction and so you shouldn't get a deadlock. Once that is working then you may want to read the spring documentation on its propagation levels to make sure that REQUIRED is the right level for your needs.

于 2013-03-18T23:11:14.267 回答