我目前正在开发一个使用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之后运行(而不是在其间)。
答案 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。