4

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");
  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.
Community
  • 1
  • 1
  • Related to the Workbench, I think that either it changes the execution order of the operations each time, or it simply executes them so quick (may be in parallel) that the transaction is not fully committed, when the select-all is executed. My recommendation is to debug in the MYSQL log file what queries are executed in order to see the problem. – V G Feb 18 '15 at 10:07

3 Answers3

4

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 (i.e 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.
V G
  • 18,822
  • 6
  • 51
  • 89
  • If it taken from PC, why select-all query doesn't see `merge(objectWithNewState);flush();` results? AFAIK, `merge` returns and puts objects with new properties to PC. –  Feb 18 '15 at 09:24
  • 1
    I can't beleive you that the select-all does not see the changes. It would be a very serious bug. Try doing everything in the same service method/transaction (important in the service, not in the controller): `document.setFlag(!document.getFlag())); em.merge(document); em.flush(); em.createQuery...` – V G Feb 18 '15 at 09:27
  • select-all in same method with merge is OK. Even much later after merge+flush is OK - but in each odd query new values is not seen. On even query select-all works perfectly. Really strange. I press F5 which only calls select-all and see different results each time) –  Feb 18 '15 at 09:39
  • What do you mean with even and odd? Try pressing CTRL-F5 to make sure the page is not taken from the Browsers cache. – V G Feb 18 '15 at 09:43
  • ctrl+f5 doesn't work. Still diffdrent results on each page refresh (select-all) while in DB everything is OK - I'm checking it with workbench –  Feb 18 '15 at 09:54
  • That is weird, it begins to smell as a bug. – V G Feb 18 '15 at 11:12
0

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.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
  • Related to Scenario2: `Department managedScenario = em.merge(department);` and then try comparing the `managedScenario` and you will get the same behaviour as in Scenario1. Besides, Scenario2 as you described it should be consistent between all JPA implementations. Do you know any implementations (with its version) that behave otherwise? – V G Feb 18 '15 at 09:41
  • @AndreiI The code I just shared above is code that I just wrote and tested in my local environment. So I am not theorizing here. I am saying what I am seeing happening with JPA using Hibernate 4.3.6. I suppose scenario two behavior could change depending on implementation of JPA, since this is undocumented in the specification as far as I know. But definitely scenario 1 and 2 could behave very differently depending on what you are doing. – Edwin Dalorzo Feb 18 '15 at 09:46
  • I was not denying what you said. I just told that changing a bit the scenario2 (thus creating a new scneario3) you will get a JPA-compliant scenario, that behaves as Scenario1. Related to the JPA-compliance of Scenario2: it MUST be so in all JPA-implementations, otherwise it would mean, that the instance `department` get managed, what is not what the `merge()` method does. – V G Feb 18 '15 at 09:57
  • @EdwinDalorzo, see update, my scenario is mix of yours - different reads and merging detached. –  Feb 18 '15 at 09:58
0

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.

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

Community
  • 1
  • 1