/** * Test the equivalent of EJB3 LockModeType.WRITE * * <p>From the spec: * * <p>If transaction T1 calls lock(entity, LockModeType.WRITE) on a versioned object, the entity * manager must avoid the phenomena P1 and P2 (as with LockModeType.READ) and must also force an * update (increment) to the entity's version column. A forced version update may be performed * immediately, or may be deferred until a flush or commit. If an entity is removed beforeQuery a * deferred version update was to have been applied, the forced version update is omitted, since * the underlying database row no longer exists. * * <p>The persistence implementation is not required to support calling lock(entity, * LockMode-Type.WRITE) on a non-versioned object. When it cannot support a such lock call, it * must throw the PersistenceException. When supported, whether for versioned or non-versioned * objects, LockMode-Type.WRITE must always prevent the phenomena P1 and P2. For non-versioned * objects, whether or not LockModeType.WRITE has any additional behaviour is vendor-specific. * Applications that call lock(entity, LockModeType.WRITE) on non-versioned objects will not be * portable. * * <p>Due to the requirement that LockModeType.WRITE needs to force a version increment, a new * Hibernate LockMode was added to support this behavior: {@link org.hibernate.LockMode#FORCE}. */ @Test public void testLockModeTypeWrite() { if (!readCommittedIsolationMaintained("ejb3 lock tests")) { return; } final String initialName = "lock test"; // set up some test data Session s1 = sessionFactory().openSession(); Transaction t1 = s1.beginTransaction(); Item item = new Item(); item.setName(initialName); s1.save(item); MyEntity myEntity = new MyEntity(); myEntity.setName("Test"); s1.save(myEntity); t1.commit(); s1.close(); Long itemId = item.getId(); long initialVersion = item.getVersion(); s1 = sessionFactory().openSession(); t1 = s1.beginTransaction(); item = (Item) s1.get(Item.class, itemId); s1.lock(item, LockMode.FORCE); assertEquals("no forced version increment", initialVersion + 1, item.getVersion()); myEntity = (MyEntity) s1.get(MyEntity.class, myEntity.getId()); s1.lock(myEntity, LockMode.FORCE); assertTrue("LockMode.FORCE on a un-versioned entity should degrade nicely to UPGRADE", true); s1.lock(item, LockMode.FORCE); assertEquals("subsequent LockMode.FORCE did not no-op", initialVersion + 1, item.getVersion()); Session s2 = sessionFactory().openSession(); Transaction t2 = s2.beginTransaction(); Item item2 = (Item) s2.get(Item.class, itemId); assertEquals("isolation not maintained", initialName, item2.getName()); item.setName("updated-1"); s1.flush(); // currently an unfortunate side effect... assertEquals(initialVersion + 2, item.getVersion()); t1.commit(); s1.close(); item2.setName("updated"); try { t2.commit(); fail("optimistic lock should have failed"); } catch (Throwable ignore) { // expected behavior t2.rollback(); } finally { s2.close(); } s1 = sessionFactory().openSession(); t1 = s1.beginTransaction(); s1.delete(item); s1.delete(myEntity); t1.commit(); s1.close(); }