JPQL和Native Query以不同的顺序执行

时间:2018-03-16 22:51:49

标签: java oracle hibernate jpql nativequery

我目前正在开发一个使用Java,Oracle和Hibernate的项目。我们的许多遗留代码使用本机查询来获取和更新我们的数据库记录,但我们一直在慢慢过渡到使用JPQL。我怀疑两者的混合导致了一些问题,并希望听到你的想法。

在其中一个场景中,我们添加了新的逻辑,使用JPQL从我们的数据库中获取两个实体并对它们进行修改(让我们称之为方法A)。之后,我们通过本机查询执行更新语句,以翻转数据库中相同实体的标志(让我们称之为方法B)。这两种方法是相互独立的,这就是为什么它们是分开的而不是在同一个体内完成的。

我在阅读log4j日志时注意到,方法B中的本机查询在方法A运行时执行,特别是在更新两个实体之间。产生的错误是实体1得到更新(方法A),本机查询更新实体1和2(方法B),但是当实体2更新时(仍然是方法A),它会覆盖方法B的更新。

但是,当我将方法B的本机查询替换为完全相同的JPQL时,它会按预期运行 - 在方法A完全完成之后。

有谁知道为什么会这样?

提前致谢!

编辑:感谢您的回复,伙计们!以下是使用本机查询的原始代码的剥离版本:

private void handleDateChange(List<Long> dpIdList) {
    for(Long dpid : dpIdList) {
        updateFeeTransactionsForDateChange(dpid);
        deactivateFeeTransactionsAfterUpdate(dpid);
    }
}

public void updateFeeTransactionsForDateChange(final Long dealProductId) {
    List<FeeTransaction> feeTransactionList = feeTransactionDAO.getActiveUnmappedFeeTransactionEntitiesForDealProduct(dealProductId);
    for (FeeTransaction feeTransaction : feeTransactionList) {
        //Some Logic
    }
}

public void deactivateFeeTransactionsAfterUpdate(final Long dealProductId) {
    String UPDATE_ACTIVE_FOR_ALLOCATIONS = "update fee_transaction ft set ft.active = 'N' where  ft.deal_product_id = ?1 and ft.active = 'Y'";
    entityManager.createNativeQuery(UPDATE_ACTIVE_FOR_ALLOCATIONS).setParameter(1, dealProduct.getDealProductId()).executeUpdate();
}

以下是我所做的更改,从将JPQL添加到我的实体类开始:

@Entity
@Table(name = "FEE_TRANSACTION")
@SequenceGenerator(name = "FEETRANSPK", sequenceName = "FEETRANSID_SEQ")
@Named("feeTransaction")
@NamedQueries({
    @NamedQuery(
            name = FeeTransaction.UPDATE_ACTIVE_FOR_FEE_TRANSACTIONS_BY_DEAL_PRODUCT_IDS,
            query = "update FeeTransaction set active = false where dealProductId in (:dealProductIds) and active = 'Y' ")
})
public class FeeTransaction implements Serializable {
    //Attributes, getters, and setters
}

我的DAO课程:

public void updateActiveForFeeTransactionsByDealProductIds(Set<Long> dealProductIds) {
    entityManager.createNamedQuery(FeeTransaction.UPDATE_ACTIVE_FOR_FEE_TRANSACTIONS_BY_DEAL_PRODUCT_IDS)
            .setParameter("dealProductIds", dealProductIds)
            .executeUpdate();
}

再把它们捆绑在一起:

private void handleDateChange(List<Long> dpIdList) {
    for(Long dpid : dpIdList) {
        updateFeeTransactionsForDateChange(dpid);
    }
    feeTransactionDAO.updateActiveForFeeTransactionsByDealProductIds(dpIdList);
}

我还应该补充一点,在我的测试用例中,dpIdList中只有一个元素,根据其ID,返回2个费用交易。这意味着我的本机查询仍然应该在updateFeeTransactionsForDateChange之后运行(而不是在其间)。

1 个答案:

答案 0 :(得分:0)

调用hibernate flush()的时间可能有问题。

本机查询不遵循事务关闭后产生的事务提交,并且仅影响methodA。所以,你的情况如下:

  • methodA调用update:update正在等待flush提交
  • methodB调用update:update startslly立即启动
  • 提交
  • methodA事务并且hibernate调用flush
  • methodA在methodB update
  • 期间启动数据库更新

因此,您可以在执行本机查询之前调用flush

private void handleDateChange(List<Long> dpIdList) {
    for(Long dpid : dpIdList) {
        updateFeeTransactionsForDateChange(dpid);
        em.flush();
        deactivateFeeTransactionsAfterUpdate(dpid);
    }
}

您可以了解有关Flush行为的更多详细信息here