mirror of https://github.com/apache/openjpa.git
OPENJPA-293. Fixed problem with transactional state maintenance that was preventing lifecycle tests from passing.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@560601 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b83db93229
commit
0243702b35
|
@ -1584,7 +1584,7 @@ public class BrokerImpl
|
||||||
Collection saved = save.rollback(_savepoints.values());
|
Collection saved = save.rollback(_savepoints.values());
|
||||||
if (_savepointCache != null)
|
if (_savepointCache != null)
|
||||||
_savepointCache.clear();
|
_savepointCache.clear();
|
||||||
if (_transCache != null) {
|
if (hasTransactionalObjects()) {
|
||||||
// build up a new collection of states
|
// build up a new collection of states
|
||||||
TransactionalCache oldTransCache = _transCache;
|
TransactionalCache oldTransCache = _transCache;
|
||||||
TransactionalCache newTransCache = new TransactionalCache
|
TransactionalCache newTransCache = new TransactionalCache
|
||||||
|
@ -2156,8 +2156,10 @@ public class BrokerImpl
|
||||||
_fc.setWriteLockLevel(LOCK_NONE);
|
_fc.setWriteLockLevel(LOCK_NONE);
|
||||||
_fc.setLockTimeout(-1);
|
_fc.setLockTimeout(-1);
|
||||||
|
|
||||||
Collection transStates = _transCache;
|
Collection transStates;
|
||||||
if (transStates == null)
|
if (hasTransactionalObjects())
|
||||||
|
transStates = _transCache;
|
||||||
|
else
|
||||||
transStates = Collections.EMPTY_LIST;
|
transStates = Collections.EMPTY_LIST;
|
||||||
|
|
||||||
// fire after rollback/commit event
|
// fire after rollback/commit event
|
||||||
|
@ -3750,16 +3752,26 @@ public class BrokerImpl
|
||||||
* Return a copy of all transactional state managers.
|
* Return a copy of all transactional state managers.
|
||||||
*/
|
*/
|
||||||
protected Collection getTransactionalStates() {
|
protected Collection getTransactionalStates() {
|
||||||
if (_transCache == null)
|
if (!hasTransactionalObjects())
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
return _transCache.copy();
|
return _transCache.copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not there are any transactional objects in the current
|
||||||
|
* persistence context. If there are any instances with untracked state,
|
||||||
|
* this method will cause those instances to be scanned.
|
||||||
|
*/
|
||||||
|
private boolean hasTransactionalObjects() {
|
||||||
|
_cache.dirtyCheck();
|
||||||
|
return _transCache != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a copy of all dirty state managers.
|
* Return a copy of all dirty state managers.
|
||||||
*/
|
*/
|
||||||
protected Collection getDirtyStates() {
|
protected Collection getDirtyStates() {
|
||||||
if (_transCache == null)
|
if (!hasTransactionalObjects())
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
|
|
||||||
return _transCache.copyDirty();
|
return _transCache.copyDirty();
|
||||||
|
@ -3823,7 +3835,7 @@ public class BrokerImpl
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
try {
|
try {
|
||||||
if (_transCache == null)
|
if (!hasTransactionalObjects())
|
||||||
_transCache = new TransactionalCache(_orderDirty);
|
_transCache = new TransactionalCache(_orderDirty);
|
||||||
_transCache.addClean(sm);
|
_transCache.addClean(sm);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -3839,6 +3851,8 @@ public class BrokerImpl
|
||||||
lock();
|
lock();
|
||||||
try {
|
try {
|
||||||
if (_transCache != null)
|
if (_transCache != null)
|
||||||
|
// intentional direct access; we don't want to recompute
|
||||||
|
// dirtiness while removing instances from the transaction
|
||||||
_transCache.remove(sm);
|
_transCache.remove(sm);
|
||||||
if (_derefCache != null && !sm.isPersistent())
|
if (_derefCache != null && !sm.isPersistent())
|
||||||
_derefCache.remove(sm);
|
_derefCache.remove(sm);
|
||||||
|
@ -3866,7 +3880,7 @@ public class BrokerImpl
|
||||||
lock();
|
lock();
|
||||||
try {
|
try {
|
||||||
// cache dirty instance
|
// cache dirty instance
|
||||||
if (_transCache == null)
|
if (!hasTransactionalObjects())
|
||||||
_transCache = new TransactionalCache(_orderDirty);
|
_transCache = new TransactionalCache(_orderDirty);
|
||||||
_transCache.addDirty(sm);
|
_transCache.addDirty(sm);
|
||||||
|
|
||||||
|
@ -4427,6 +4441,12 @@ public class BrokerImpl
|
||||||
* Call this method when a new state manager initializes itself.
|
* Call this method when a new state manager initializes itself.
|
||||||
*/
|
*/
|
||||||
public void add(StateManagerImpl sm) {
|
public void add(StateManagerImpl sm) {
|
||||||
|
if (!sm.isIntercepting()) {
|
||||||
|
if (_untracked == null)
|
||||||
|
_untracked = new HashSet();
|
||||||
|
_untracked.add(sm);
|
||||||
|
}
|
||||||
|
|
||||||
if (!sm.isPersistent() || sm.isEmbedded()) {
|
if (!sm.isPersistent() || sm.isEmbedded()) {
|
||||||
if (_embeds == null)
|
if (_embeds == null)
|
||||||
_embeds = new ReferenceHashSet(ReferenceHashSet.WEAK);
|
_embeds = new ReferenceHashSet(ReferenceHashSet.WEAK);
|
||||||
|
@ -4453,12 +4473,6 @@ public class BrokerImpl
|
||||||
(orig.getManagedInstance()))).
|
(orig.getManagedInstance()))).
|
||||||
setFailedObject(sm.getManagedInstance());
|
setFailedObject(sm.getManagedInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sm.isIntercepting()) {
|
|
||||||
if (_untracked == null)
|
|
||||||
_untracked = new HashSet();
|
|
||||||
_untracked.add(sm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4624,6 +4638,14 @@ public class BrokerImpl
|
||||||
if (_news != null)
|
if (_news != null)
|
||||||
_news.clear();
|
_news.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dirtyCheck() {
|
||||||
|
if (_untracked == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (Iterator iter = _untracked.iterator(); iter.hasNext(); )
|
||||||
|
((StateManagerImpl) iter.next()).dirtyCheck();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -177,12 +177,13 @@ public class SaveFieldManager
|
||||||
// if the field is not available, assume that it has changed.
|
// if the field is not available, assume that it has changed.
|
||||||
if (_saved == null || !_saved.get(field))
|
if (_saved == null || !_saved.get(field))
|
||||||
return false;
|
return false;
|
||||||
if (!(_state.pcGetStateManager() instanceof OpenJPAStateManager))
|
if (!(_state.pcGetStateManager() instanceof StateManagerImpl))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
OpenJPAStateManager sm = (OpenJPAStateManager)
|
StateManagerImpl sm = (StateManagerImpl) _state.pcGetStateManager();
|
||||||
_state.pcGetStateManager();
|
SingleFieldManager single = new SingleFieldManager(sm, sm.getBroker());
|
||||||
Object old = sm.fetch(field);
|
sm.provideField(_state, single, field);
|
||||||
|
Object old = single.fetchObjectField(field);
|
||||||
return current == old || current != null && current.equals(old);
|
return current == old || current != null && current.equals(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -795,8 +795,8 @@ public class StateManagerImpl
|
||||||
for (int i = 0; i < fmds.length; i++) {
|
for (int i = 0; i < fmds.length; i++) {
|
||||||
// pk and version fields cannot be mutated; don't mark them
|
// pk and version fields cannot be mutated; don't mark them
|
||||||
// as such. ##### validate?
|
// as such. ##### validate?
|
||||||
if (!fmds[i].isPrimaryKey()
|
if (!fmds[i].isPrimaryKey() && !fmds[i].isVersion()
|
||||||
&& !fmds[i].isVersion()) {
|
&& _loaded.get(i)) {
|
||||||
if (!saved.isFieldEqual(i, fetch(i))) {
|
if (!saved.isFieldEqual(i, fetch(i))) {
|
||||||
dirty(i);
|
dirty(i);
|
||||||
}
|
}
|
||||||
|
@ -811,9 +811,6 @@ public class StateManagerImpl
|
||||||
return false;
|
return false;
|
||||||
if (isNew() && !isFlushed())
|
if (isNew() && !isFlushed())
|
||||||
return false;
|
return false;
|
||||||
if (getMetaData().getAccessType() != ClassMetaData.ACCESS_FIELD
|
|
||||||
&& !(isNew() && isFlushed()))
|
|
||||||
return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||||
import org.apache.openjpa.meta.ClassMetaData;
|
import org.apache.openjpa.meta.ClassMetaData;
|
||||||
import org.apache.openjpa.util.ImplHelper;
|
import org.apache.openjpa.util.ImplHelper;
|
||||||
|
import org.apache.openjpa.event.AbstractLifecycleListener;
|
||||||
|
import org.apache.openjpa.event.LifecycleEvent;
|
||||||
|
|
||||||
public abstract class AbstractUnenhancedClassTest
|
public abstract class AbstractUnenhancedClassTest
|
||||||
extends SingleEMFTestCase {
|
extends SingleEMFTestCase {
|
||||||
|
@ -60,6 +62,11 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
|
|
||||||
protected abstract UnenhancedSubtype newUnenhancedSubclassInstance();
|
protected abstract UnenhancedSubtype newUnenhancedSubclassInstance();
|
||||||
|
|
||||||
|
private UnenhancedType newInstance(boolean sub) {
|
||||||
|
return sub ? newUnenhancedSubclassInstance()
|
||||||
|
: newUnenhancedInstance();
|
||||||
|
}
|
||||||
|
|
||||||
public void testMetaData() {
|
public void testMetaData() {
|
||||||
ClassMetaData meta = OpenJPAPersistence.getMetaData(emf,
|
ClassMetaData meta = OpenJPAPersistence.getMetaData(emf,
|
||||||
getUnenhancedClass());
|
getUnenhancedClass());
|
||||||
|
@ -136,15 +143,23 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
assertNotNull(em.getObjectId(un));
|
assertNotNull(em.getObjectId(un));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOperations() {
|
public void testOperationsOnUserDefined() {
|
||||||
opsHelper(false);
|
opsHelper(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubclassOperations() {
|
public void testSubclassOperationsOnUserDefined() {
|
||||||
opsHelper(true);
|
opsHelper(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void opsHelper(boolean sub) {
|
public void testOperationsOnOpenJPADefined() {
|
||||||
|
opsHelper(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubclassOperationsOnOpenJPADefined() {
|
||||||
|
opsHelper(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void opsHelper(boolean sub, boolean userDefined) {
|
||||||
OpenJPAEntityManager em = null;
|
OpenJPAEntityManager em = null;
|
||||||
try {
|
try {
|
||||||
UnenhancedType un = newInstance(sub);
|
UnenhancedType un = newInstance(sub);
|
||||||
|
@ -154,22 +169,30 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
em.persist(un);
|
em.persist(un);
|
||||||
un.setStringField("bar");
|
un.setStringField("bar");
|
||||||
assertEquals("bar", un.getStringField());
|
assertEquals("bar", un.getStringField());
|
||||||
|
assertPersistenceContext(em, un, true, true, sub);
|
||||||
em.flush();
|
em.flush();
|
||||||
|
assertPersistenceContext(em, un, true, true, sub);
|
||||||
assertTrue(un.getId() != 0);
|
assertTrue(un.getId() != 0);
|
||||||
UnenhancedType un2 = em.find(getUnenhancedClass(), un.getId());
|
UnenhancedType un2 = em.find(getUnenhancedClass(), un.getId());
|
||||||
assertSame(un, un2);
|
assertSame(un, un2);
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
|
assertPersistenceContext(em, un, false, false, sub);
|
||||||
un2 = em.find(getUnenhancedClass(), un.getId());
|
un2 = em.find(getUnenhancedClass(), un.getId());
|
||||||
assertSame(un, un2);
|
assertSame(un, un2);
|
||||||
em.close();
|
|
||||||
|
|
||||||
em = emf.createEntityManager();
|
if (!userDefined) {
|
||||||
|
em.close();
|
||||||
|
em = emf.createEntityManager();
|
||||||
|
}
|
||||||
|
|
||||||
un = em.find(getUnenhancedClass(), un.getId());
|
un = em.find(getUnenhancedClass(), un.getId());
|
||||||
assertNotNull(un);
|
assertNotNull(un);
|
||||||
assertTrue(un instanceof PersistenceCapable);
|
if (!userDefined)
|
||||||
|
assertTrue(un instanceof PersistenceCapable);
|
||||||
assertEquals("bar", un.getStringField());
|
assertEquals("bar", un.getStringField());
|
||||||
em.getTransaction().begin();
|
em.getTransaction().begin();
|
||||||
un.setStringField("baz");
|
un.setStringField("baz");
|
||||||
|
assertPersistenceContext(em, un, true, true, sub);
|
||||||
assertEquals("baz", un.getStringField());
|
assertEquals("baz", un.getStringField());
|
||||||
|
|
||||||
if (sub)
|
if (sub)
|
||||||
|
@ -178,6 +201,11 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
assertTrue(em.isDirty(un));
|
assertTrue(em.isDirty(un));
|
||||||
|
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
// make sure that the values are still up-to-date after
|
||||||
|
// the commit happens
|
||||||
|
assertEquals("baz", un.getStringField());
|
||||||
|
|
||||||
em.close();
|
em.close();
|
||||||
|
|
||||||
em = emf.createEntityManager();
|
em = emf.createEntityManager();
|
||||||
|
@ -196,6 +224,17 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertPersistenceContext(OpenJPAEntityManager em,
|
||||||
|
UnenhancedType un, boolean transactional, boolean dirty, boolean sub) {
|
||||||
|
assertEquals(transactional, em.getTransactionalObjects().contains(un));
|
||||||
|
assertEquals(dirty, em.getDirtyObjects().contains(un));
|
||||||
|
if (dirty) {
|
||||||
|
Class cls = sub ? getUnenhancedSubclass() : getUnenhancedClass();
|
||||||
|
assertTrue(em.getUpdatedClasses().contains(cls)
|
||||||
|
|| em.getPersistedClasses().contains(cls));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testRelations() {
|
public void testRelations() {
|
||||||
OpenJPAEntityManager em = emf.createEntityManager();
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
em.getTransaction().begin();
|
em.getTransaction().begin();
|
||||||
|
@ -480,9 +519,51 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private UnenhancedType newInstance(boolean sub) {
|
public void testListenersOnUserDefinedInstance()
|
||||||
return sub ? newUnenhancedSubclassInstance()
|
throws IOException, ClassNotFoundException, CloneNotSupportedException {
|
||||||
: newUnenhancedInstance();
|
listenerHelper(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testListenersOnUserDefinedSubclassInstance()
|
||||||
|
throws IOException, ClassNotFoundException, CloneNotSupportedException {
|
||||||
|
listenerHelper(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testListenersOnOpenJPADefinedInstance()
|
||||||
|
throws IOException, ClassNotFoundException, CloneNotSupportedException {
|
||||||
|
listenerHelper(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testListenersOnOpenJPADefinedSubclassInstance()
|
||||||
|
throws IOException, ClassNotFoundException, CloneNotSupportedException {
|
||||||
|
listenerHelper(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void listenerHelper(boolean userDefined, boolean sub)
|
||||||
|
throws IOException, ClassNotFoundException, CloneNotSupportedException {
|
||||||
|
ListenerImpl listener = new ListenerImpl();
|
||||||
|
emf.addLifecycleListener(listener, (Class[]) null);
|
||||||
|
OpenJPAEntityManager em = emf.createEntityManager();
|
||||||
|
UnenhancedType un = newInstance(sub);
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist(un);
|
||||||
|
em.getTransaction().commit();
|
||||||
|
|
||||||
|
if (!userDefined) {
|
||||||
|
em.close();
|
||||||
|
em = emf.createEntityManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.invoked = false;
|
||||||
|
|
||||||
|
un = em.find(getUnenhancedClass(), un.getId());
|
||||||
|
em.getTransaction().begin();
|
||||||
|
un.setStringField("updated");
|
||||||
|
em.getTransaction().commit();
|
||||||
|
assertTrue(listener.invoked);
|
||||||
|
em.close();
|
||||||
|
|
||||||
|
assertEquals("updated", listener.stringField);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetMetaDataOfSubtype() {
|
public void testGetMetaDataOfSubtype() {
|
||||||
|
@ -499,4 +580,17 @@ public abstract class AbstractUnenhancedClassTest
|
||||||
Collections.singleton(getUnenhancedSubclass()), null);
|
Collections.singleton(getUnenhancedSubclass()), null);
|
||||||
assertSame(meta, OpenJPAPersistence.getMetaData(emf, subs.get(0)));
|
assertSame(meta, OpenJPAPersistence.getMetaData(emf, subs.get(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ListenerImpl
|
||||||
|
extends AbstractLifecycleListener {
|
||||||
|
|
||||||
|
String stringField;
|
||||||
|
boolean invoked;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterStore(LifecycleEvent event) {
|
||||||
|
invoked = true;
|
||||||
|
stringField = ((UnenhancedType) event.getSource()).getStringField();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue