David Portabella
David Portabella

Reputation: 12720

JPA createQuery and flush/clear

The following code works, however if I remove the flush() and clear() calls, then the second call to showTable() does not show the updated name "joan", but "john".

What is the proper way to achieve this, without calling flush() and clear()?

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.example.Customer;

@ContextConfiguration(locations = {"classpath:spring/test/spring-test.xml"})
@TransactionConfiguration(transactionManager = "txManager")
public class Test extends AbstractTransactionalJUnit4SpringContextTests {
    private final Logger log = LoggerFactory.getLogger(getClass());

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void test() throws Exception {
        Customer customer = new Customer();
        customer.setName("john");

        entityManager.persist(customer);
        entityManager.flush();
        entityManager.clear();

        showTable();

        final Query query = entityManager.createQuery("update Customer c set c.name='joan'");
        int updateCount = query.executeUpdate();
        log.debug("Update count: " + updateCount);
        entityManager.flush();
        entityManager.clear();

        showTable();
    }

    public void showTable() {
        final Query query = entityManager.createQuery("select c FROM Customer c");
        List<Customer> list = query.getResultList();
        for (Customer customer: list) {
            log.info("customer name: " + customer.getName());
        }
    }
}

Upvotes: 3

Views: 13481

Answers (1)

JB Nizet
JB Nizet

Reputation: 691735

The first test shouldn't need flush or clear, because the entity manager must detect that the query results could be affected by the pending changes not already saved. So you shouldn't have to flush(), and even less to clear(), before executing the query.

The second test, however, is different. You're executing an update query, and thoses queries completely bypass the first level cache. They make changes in the database behind its back. This is the reason why you need to clear: if you don't, the select query will find the customer that is already in the cache, and the cached (but obsolete) entity will be returned.

Upvotes: 5

Related Questions