【问题标题】:JPQL and Native Query are executed in different orderJPQL 和 Native Query 以不同的顺序执行
【发布时间】:2018-08-26 02:21:25
【问题描述】:

我目前正在从事一个使用 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 之后运行(而不是介于两者之间)。

【问题讨论】:

  • 向我们展示您的代码。你在用executeUpdate()方法吗?
  • 这对我来说听起来很奇怪。如果它是具有两个不同查询的隔离方法,即使一个是本机的,另一个是 JPQL,那么它应该没问题。很可能您的查询有一些问题。也许在您的原生查询中您不小心碰到了 A。请发布您的两个查询以供进一步调查。

标签: java oracle hibernate jpql nativequery


【解决方案1】:

可能是调用休眠flush()的时间有问题。

本机查询不考虑在事务关闭后进行的事务提交,这只影响methodA。因此,您处于这样的场景中:

  • methodA 调用更新:更新正在等待刷新提交
  • methodB 调用更新:更新立即开始
  • methodA 事务已提交,休眠调用刷新
  • methodA在methodB更新期间启动数据库更新

所以,你可以在执行原生查询之前调用flush

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-02
    相关资源
    最近更新 更多