rcomblen
rcomblen

Reputation: 4649

JPA update query: cache not properly invalidated

Why doesn't it work:

@Test
public void test() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("test-pu");
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    String id = "id";
    long value = 1234L;

    entityManager.getTransaction().begin();

    FancyEntity fancyEntity = new FancyEntity(id);
    entityManager.persist(fancyEntity);
    int updateCount = entityManager.createQuery("update FancyEntity item set item.value = ?2 where item.id = ?1").setParameter(1, id).setParameter(2, value).executeUpdate();
    assertEquals(1, updateCount);

    FancyEntity checkResult = entityManager.find(FancyEntity.class, id);
    assertEquals(1234L, checkResult.getValue()); // <- this assert fails

    entityManager.getTransaction().commit();
}

with

@Entity
public class FancyEntity {
    @Id
    private String id;
    @Column
    private long value;
    public FancyEntity(String id) {
        this.id = id;
        this.value = 0;
    }
    public FancyEntity() {
    }
    public long getValue() {
        return value;
    }
}

and

<persistence-unit name="test-pu"
                  transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>com.fancypackage.FancyEntity</class>

    <properties>
        <property name="eclipselink.logging.level" value="SEVERE"/>
        <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
        <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem;sql.enforce_strict_size=true;hsqldb.tx=mvcc" />
        <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
        <property name="eclipselink.ddl-generation.output-mode" value="database" />
        <property name="eclipselink.logging.level.sql" value="FINE"/>
        <property name="eclipselink.logging.parameters" value="true"/>
    </properties>
</persistence-unit>

Result is

java.lang.AssertionError: 
Expected :1234
Actual   :0

It seems there is some cache that is not invalidated by the update query. checkResult and fancyEntity are the same object. Forcing the refresh using entityManager.refresh(checkResult). The weirdest thing is that a select is issued for retrieving checkResult (seen in the eclipselink log), still its result is not taken into account. Same behavior using MySQL rather than HSQL.

Any hint on what could be wrong ?

Upvotes: 4

Views: 1678

Answers (2)

sleske
sleske

Reputation: 83577

As an addition to Alan Hay's answer:

Some practical hints to avoid the problem:

  • Ideally, avoid mixing bulk updates/deletes and and reading in the same transaction, then the problem described cannot occur. It is enough if you avoid bulk updates and reads on the same table/entity (though this may become tricky if you have entities accessing multiple tables).
  • Use EntityManager.refresh() As a workaround, if you need to load an entity that may not be synchronized with the database, you can call entityManager.refresh(myEntity). This will re-load the entity from the database, thus avoiding the problem. Note that refresh() discards any changes you made to the entity in memory, so only use it on freshly loaded entity instances.

Upvotes: 0

Alan Hay
Alan Hay

Reputation: 23226

That is a bulk update statement, As the JPQL language reference notes:

The persistence context is not synchronized with the result of the bulk update or delete. Caution should be used when executing bulk update or delete operations because they may result in inconsistencies between the database and the entities in the active persistence context. In general, bulk update and delete operations should only be performed within a separate transaction or at the beginning of a transaction (before entities have been accessed whose state might be affected by such operations).

https://docs.oracle.com/html/E24396_01/ejb3_langref.html#ejb3_langref_bulk_ops)

So the behaviour you are seeing makes perfect sense.

Upvotes: 8

Related Questions