简体   繁体   中英

JPQL and Native Query are executed in different order

I am currently working on a project that uses Java, Oracle, and Hibernate. A lot of our legacy code uses native queries to fetch and update our database records, but we've been slowly transitioning to using JPQL. I suspect that the mixing of the two is causing some issues and would like to hear your thoughts on this.

In one of these scenarios, we added new logic to fetch two entities from our database using JPQL and make modifications to them (let's call this method A). Afterwards, we execute an update statement via native query to flip a flag on those same entities in the database (let's call this method B). These two methods are independent of each other, which is why they are separate and not done in the same body.

What I've noticed from reading our log4j logs is that the native query in Method B gets executed WHILE method A is running, specifically in between updating the two entities. The resulting bug is that entity 1 gets updated (method A), the native query updates both entity 1 and 2 (method B), but when entity 2 is updated (still method A), it overwrites method B's update.

However, when I replace method B's native query with JPQL that does the exact same thing, it runs as expected - after method A has finished entirely.

Does anyone know why this may be happening?

Thanks in advance!

EDIT: Thanks for your responses, folks! Here is a stripped version of the original code using native queries:

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

Below are the changes I made, starting with adding the JPQL to my entity class:

@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
}

My DAO class:

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

And tying it all together again:

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

I should also add that in my test case, there was only one element in dpIdList which, based on its ID, returns 2 fee transactions. This means that my native query still should have been run after updateFeeTransactionsForDateChange (as opposed to in between).

Probably is a problem with the time when the hibernate flush() is called.

The native query does not respect the transaction commit that are made after the transaction is closed and that affects only the methodA . So, you are in a scenario like:

  • methodA calls update: update is waiting the flush to commit
  • methodB calls update: update starts immediatelly
  • methodA transaction is commited and hibernate calls flush
  • methodA starts the update on database during the methodB update

So, you can call flush before execute the native query:

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

You can have more details about the Flush behavior here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM