Currently in our project we are using detached models exclusively, and we ran into this issue which is causing some problems. The issue occurs when a model is updated, after which the same model is found and then detached - All in the same transaction. I wrote a simple test which confirms this:
public class TestSandbox {
private static EntityManager entityManager;
private static long testObjectId;
@BeforeClass
public static void setUpTests() {
EntityManagerFactory sandbox = Persistence.createEntityManagerFactory("Sandbox");
entityManager = sandbox.createEntityManager();
entityManager.getTransaction().begin();
TestModel obj = new TestModel();
obj.setName("abc");
entityManager.persist(obj);
entityManager.getTransaction().commit();
testObjectId = obj.getId();
}
@Test
public void testFindAndDetachAfterUpdate_assertUpdatePersisted() throws Exception {
String newName = Long.toString(new Random().nextLong());
entityManager.getTransaction().begin();
TestModel updateObj = entityManager.find(TestModel.class, testObjectId);
updateObj.setName(newName);
entityManager.merge(updateObj);
TestModel foundObj = entityManager.find(TestModel.class, testObjectId);
entityManager.detach(foundObj);
entityManager.getTransaction().commit();
Assert.assertEquals(newName, entityManager.find(TestModel.class, testObjectId).getName());
}
}
After some experimentation we discovered that if we use (detach -> merge -> detach) on the foundObj, the test will pass. Because this worked we double checked if the dirty state was being kept, and sure enough it was clean after the initial detach, then became dirty again after merge (and stayed dirty after the second detach). We also tested with a CriteriaQuery (just searching by ID) instead of entityManager.find, and the test passed with no problems. This seems like it could be a problem with the first level cache - maybe finding the object is discarding the dirty state somehow? |