简体   繁体   中英

Does JPA JPQL select query reads from database or from persistence context?

I have Document entity and some managed document object for doc with id=1.

 Document managedDoc = entityManager.find(Document .class, 1);
 managedDoc.setName("changedName");

As I know, managed doc state changed in persistent context (futher PC) after calling setter but nothing changed in database. Somewhere in my code I do the following:

 Query query = entityManager.createQuery("from Document");
 List<Document> list = query.getResultList();
 return list;

When I perform select-all query as shown above, is document with id=1 taken from DB or from PC? From DB means select will not see new name because new name still in PC.

Actually, my problem is in updating via merge() and flush() and futher retrieving all objects - currently my select-all query doesn't see new values of some fields. Looks like merge+flush is OK, but JPA Query reads not from DB but from PC. But even if I'm right, both PC and DB contains new value of the name, why my select-all doesn't see it?

Moreover, select all sometimes returns correct/updated values, sometimes not

UPDATE

Clarification:

  1. I put some object to PC via entityManager.find(Document .class, 1);
  2. I create new detached instance with some name property setted. Id and other props gotten from managed instance. For example, managedDoc = getFromSomeDataStructure(); Document nonManaged = new Document(managedDoc.getId()); nonManaged.setName("newName"); managedDoc = getFromSomeDataStructure(); Document nonManaged = new Document(managedDoc.getId()); nonManaged.setName("newName");
  3. I update DB via em.merge(nonManaged);flush();
  4. I saw my changes in DB when check it in Workbench.
  5. I'm pressing F5 (and even CTRL+F5) button which performs select-all JPQL query and on each odd button press==select-all query I see non-actual old value, on each even button press==select-all query I see correct value.

It will be taken from the Persistent Context, as long as it has them their. To be more correct: as long as you have an entity in a managed state (ie in the Persistence Context), it will not be overrriden. Of course, in the context when the same EntityManager instance is used.

If you want to refetch the value from DB, you have different possibilities:

  1. Use another EntityManager, in a different transaction (important!).
  2. Use EntityManager.detach() or if you want to clear the entire persistence context, use EntityManager.clear()
  3. Use EntityManager.refresh() to throw out all changes made to an entity instance.

Let me try to clarify with a couple of examples an maybe this answer your question or with luck, helps to make the question clearer.

Scenario #1: Two Different Reads

Department department = em.find(Department.class, 1);
department.setName("Jedi Masters");

TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
   if(found.getId().equals(1)){
      assert found == department;
      assert found.getName().equals(department.getName());
   }
}

In this first scenario you can expect the department and found to be exact same instance and therefore have the exact same values. Both assertions above pass.

Scenario #2: Merging Detached Entity

//detached entity
Department department = new Department();
department.setId(1);
department.setName("Jedi Masters");

em.merge(department);

TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
    if(found.getId().equals(1)){
        assert found != department);
        assert found.getName().equals(department.getName());
    }
}

At least with Hibernate, the behavior in this case is slightly different. The two objects are not the same instance. They are different instances, but they still should have the same contents.

So, depending on your implementation on how you are comparing them you might get unexpected results, above all if you do not implemented a right equals/hashCode protocol for detached cases like this.

As answered here , I should call refresh() for each item in result list. But only refreshing didn't work for me. After setting READ COMMITED in persistence.xml by writing

<property name="hibernate.connection.isolation" value="2" /> everything worked perfectly.

PS Don't forget to mark select method as @Transactional because refresh() doesn't work without this annotation.

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