Search code examples
hibernatecachingjpaeclipselinkcriteria

DML operations through JPA criteria/JPQL and JPA cache


JPA 2.1 provides support for performing delete/update DML operations through the JPA criteria API using CriteriaDelete and CriteriaUpdate respectively.

Given below a very simple example that performs an update operation to the associated database using CriteriaUpdate (provided by EclipseLink 2.5.2 and/or Hibernate 4.3.6 final).

public boolean update(byte[] file, Brand brand)
{
    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaUpdate<Brand>criteriaUpdate=criteriaBuilder.createCriteriaUpdate(Brand.class);
    Root<Brand> root = criteriaUpdate.from(entityManager.getMetamodel().entity(Brand.class));

    criteriaUpdate.set(root.get(Brand_.brandName), brand.getBrandName());
    criteriaUpdate.set(root.get(Brand_.category), brand.getCategory());
    criteriaUpdate.set(root.get(Brand_.brandImage), file);

    criteriaUpdate.where(criteriaBuilder.equal(root, brand));
    return entityManager.createQuery(criteriaUpdate).executeUpdate()>0;
}

The same thing can be rewritten using a corresponding JPQL as follows. I'm usually accustomed to using JPA criteria.

public boolean update(byte[] file, Brand brand)
{
    return entityManager.createQuery("UPDATE Brand b SET b.brandName=:brandName, b.category=:category, b.brandImage=:brandImage WHERE b.brandId=:brandId")
           .setParameter("brandName", brand.getBrandName())
           .setParameter("category", brand.getCategory())
           .setParameter("brandImage", file)
           .setParameter("brandId", brand.getBrandId())
           .executeUpdate()>0;
}

I assume the code snippets themselves are self-explanatory. Therefore, I remain silent about them.

In this case, unlike entityManager.merge(brand), the DML operation is performed directly on the database and consequently, the state of the associated managed entity brand does not get affected. The associated JPA callbacks like @PreUpdate, @PostUpdate, if any, will not triggered.

The database state will not be synchronized with the entities managed by the persistence provider i.e. depending upon the persistence provider, it is likely that the associated entity (brand in this case) may not reflect the updated values in the database.

The update operation will be propagated to the database every time this method is called even though the entity brand is not changed/updated.

It is something same as executing a native query using the entityManager.createNativeQuery("query") method directly to the underlying database.

Is it possible to give this DML operation the same effect as entityManager.merge(brand) so that the entity state is synchronized with current state of the database after such operations have been performed?

Is it required to execute entityManager.refresh(entity) (or something else) after this update operation is completed to reflect the current saved state in the database to the entity?


Solution

  • The DML operations are directly executed against the database, therefore bypassing the first level cache dirty checking emchanism.

    Neither Interceptors nor Event Listeners nor JPA PostUpdate hooks are going to be triggered, since updates are not generated by the Hibernate action queue mechanism.

    The AUTO flush is only triggered for HQL/Criteria queries and not for native ones, so you'd better flush the Session manually before such query to be sure the pending changes are synchronized with the database.

    After the update you need to refresh all current loaded entities, or evict all entities and reload them back.