3

REQUIRES_NEW として伝播される 2 つの連続したトランザクションで構成される春の junit テストがあります。

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;
        }
    });
}

なぜそれが起こるのですか(デバッガーはソースコードが提供されていないオラクルコードで停止します)、2つの連続したトランザクションでSpring Junitテストを正しく書く方法は?

4

1 に答える 1

0

テストにより、データベースのコントラクトテーブルにデッドロックが発生しているようです。この質問REQUIRES_NEWで詳しく説明されているように、これの根本的な原因は、伝播レベルの使用である可能性が最も高いです。重要な部分はこれです:

PROPAGATION_REQUIRES_NEWは、指定されたスコープに対して、新しい独立した「内部」トランザクションを開始します。このトランザクションは、外部トランザクションから完全に独立してコミットまたはロールバックされ、独自の分離スコープ、独自のロックセットなどがあります。外部トランザクションは、内部トランザクションの開始時に一時停止され、内部トランザクションが終了すると再開されます。完了

このcreateContractTransactionalメソッドはContractテーブルに挿入しようとしていますが、テストの早い段階でロックを保持している必要があります。これはへの呼び出しだと思いpersistenceManagerHibernate.isCreated(Contract.class, contract.getId())ます。原因が何であれ、同じテーブルでロックされている2つの独立したトランザクション、つまりデッドロックがあります。

テストのトランザクションの伝播レベルをREQUIREDデフォルト設定であるに設定してみてください。まだトランザクションがない場合は新しいトランザクションが作成され、そうでない場合は現在のトランザクションが使用されます。これにより、テストが1つのトランザクションで実行されるため、デッドロックが発生することはありません。それが機能したら、その伝播レベルに関するSpringのドキュメントを読んで、それREQUIREDがニーズに適したレベルであることを確認することをお勧めします。

于 2013-03-18T23:11:14.267 に答える