Non-optimistic transactions will no longer perform a version check when committing dirty objects, unless the NonOptimisticVersionCheck compatibility property is set to true. This allows dirty instances enlisted in a non-optimistic transaction to be allowed to overwrite conflicting versions in the database.

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@446799 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Marc Prud'hommeaux 2006-09-16 00:44:55 +00:00
parent a7cfbfc4c9
commit 944f8ad22c
6 changed files with 92 additions and 33 deletions

View File

@ -35,6 +35,7 @@ import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.PCState; import org.apache.openjpa.kernel.PCState;
import org.apache.openjpa.lib.conf.Configurable; import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration; import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.OpenJPAException; import org.apache.openjpa.util.OpenJPAException;
import org.apache.openjpa.util.OptimisticException; import org.apache.openjpa.util.OptimisticException;
@ -139,6 +140,8 @@ public abstract class AbstractUpdateManager
RowManager rowMgr, JDBCStore store, Collection exceps, RowManager rowMgr, JDBCStore store, Collection exceps,
Collection customs) { Collection customs) {
try { try {
BitSet dirty;
if (sm.getPCState() == PCState.PNEW && !sm.isFlushed()) { if (sm.getPCState() == PCState.PNEW && !sm.isFlushed()) {
insert(sm, (ClassMapping) sm.getMetaData(), rowMgr, store, insert(sm, (ClassMapping) sm.getMetaData(), rowMgr, store,
customs); customs);
@ -146,18 +149,9 @@ public abstract class AbstractUpdateManager
|| sm.getPCState() == PCState.PDELETED) { || sm.getPCState() == PCState.PDELETED) {
delete(sm, (ClassMapping) sm.getMetaData(), rowMgr, store, delete(sm, (ClassMapping) sm.getMetaData(), rowMgr, store,
customs); customs);
} else if ((sm.getPCState() == PCState.PDIRTY && (!sm.isFlushed() || sm } else if ((dirty = ImplHelper.getUpdateFields(sm)) != null) {
.isFlushedDirty())) update(sm, dirty, (ClassMapping) sm.getMetaData(), rowMgr,
|| (sm.getPCState() == PCState.PNEW && sm.isFlushedDirty())) { store, customs);
BitSet dirty = sm.getDirty();
if (sm.isFlushed()) {
dirty = (BitSet) dirty.clone();
dirty.andNot(sm.getFlushed());
}
if (dirty.length() > 0)
update(sm, dirty, (ClassMapping) sm.getMetaData(), rowMgr,
store, customs);
} else if (sm.isVersionUpdateRequired()) { } else if (sm.isVersionUpdateRequired()) {
updateIndicators(sm, (ClassMapping) sm.getMetaData(), rowMgr, updateIndicators(sm, (ClassMapping) sm.getMetaData(), rowMgr,
store, customs, true); store, customs, true);

View File

@ -138,7 +138,7 @@ public abstract class ColumnVersionStrategy
// set where and update conditions on row // set where and update conditions on row
for (int i = 0; i < cols.length; i++) { for (int i = 0; i < cols.length; i++) {
if (curVersion != null) if (curVersion != null && sm.isVersionCheckRequired())
row.whereObject(cols[i], curVersion); row.whereObject(cols[i], curVersion);
if (vers.getColumnIO().isUpdatable(i, nextVersion == null)) if (vers.getColumnIO().isUpdatable(i, nextVersion == null))
row.setObject(cols[i], nextVersion); row.setObject(cols[i], nextVersion);

View File

@ -101,28 +101,30 @@ public class StateComparisonVersionStrategy
// db values match our previous image // db values match our previous image
FieldMapping[] fields = (FieldMapping[]) sm.getMetaData().getFields(); FieldMapping[] fields = (FieldMapping[]) sm.getMetaData().getFields();
Row row; Row row;
for (int i = 0, max = loaded.length(); i < max; i++) { if (sm.isVersionCheckRequired()) {
if (!loaded.get(i)) for (int i = 0, max = loaded.length(); i < max; i++) {
continue; if (!loaded.get(i))
continue;
// update our next state image with the new field value // update our next state image with the new field value
if (sm.getDirty().get(i) && !sm.getFlushed().get(i)) if (sm.getDirty().get(i) && !sm.getFlushed().get(i))
nextState[i] = sm.fetch(fields[i].getIndex()); nextState[i] = sm.fetch(fields[i].getIndex());
// fetch the row for this field; if no row exists, then we can't // fetch the row for this field; if no row exists, then we can't
// add one because we have no updates to perform; that means we // add one because we have no updates to perform; that means we
// won't detect OL exceptions when another transaction changes // won't detect OL exceptions when another transaction changes
// fields that aren't in any of the same tables as fields that // fields that aren't in any of the same tables as fields that
// this transaction changed // this transaction changed
row = rm.getRow(fields[i].getTable(), Row.ACTION_UPDATE, row = rm.getRow(fields[i].getTable(), Row.ACTION_UPDATE,
sm, false); sm, false);
if (row == null) if (row == null)
continue; continue;
// set WHERE criteria matching the previous state image so the // set WHERE criteria matching the previous state image so the
// update will fail if any changes have been made by another trans // update will fail for any changes made by another transaction
fields[i].where(sm, store, rm, state[i]); fields[i].where(sm, store, rm, state[i]);
row.setFailedObject(sm.getManagedInstance()); row.setFailedObject(sm.getManagedInstance());
}
} }
sm.setNextVersion(nextState); sm.setNextVersion(nextState);
} }

View File

@ -26,6 +26,7 @@ public class Compatibility {
private boolean _copyIds = false; private boolean _copyIds = false;
private boolean _closeOnCommit = true; private boolean _closeOnCommit = true;
private boolean _quotedNumbers = false; private boolean _quotedNumbers = false;
private boolean _nonOptimisticVersionCheck = false;
/** /**
* Whether to require exact identity value types when creating object * Whether to require exact identity value types when creating object
@ -136,4 +137,24 @@ public class Compatibility {
public void setCloseOnManagedCommit(boolean close) { public void setCloseOnManagedCommit(boolean close) {
_closeOnCommit = close; _closeOnCommit = close;
} }
/**
* Whether or not to perform a version check on instances being updated
* in a datastore transaction. Version of OpenJPA prior to 4.1 always
* forced a version check.
*/
public void setNonOptimisticVersionCheck
(boolean nonOptimisticVersionCheck) {
_nonOptimisticVersionCheck = nonOptimisticVersionCheck;
}
/**
* Whether or not to perform a version check on instances being updated
* in a datastore transaction. Version of OpenJPA prior to 4.1 always
* forced a version check.
*/
public boolean getNonOptimisticVersionCheck() {
return _nonOptimisticVersionCheck;
}
} }

View File

@ -38,6 +38,7 @@ import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.meta.ValueStrategies; import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ApplicationIds; import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.Exceptions; import org.apache.openjpa.util.Exceptions;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.InvalidStateException; import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.ObjectNotFoundException; import org.apache.openjpa.util.ObjectNotFoundException;
@ -2926,7 +2927,23 @@ public class StateManagerImpl
* Returns whether this instance needs a version check. * Returns whether this instance needs a version check.
*/ */
public boolean isVersionCheckRequired() { public boolean isVersionCheckRequired() {
return (_flags & FLAG_VERSION_CHECK) > 0;
// explicit flag for version check
if ((_flags & FLAG_VERSION_CHECK) > 0)
return true;
// need to check version if we have any dirty fields, unless we
// are in a datastore transaction
if (ImplHelper.getUpdateFields(this) != null) {
if (_broker.getOptimistic()) {
return true;
} else {
return _broker.getConfiguration().
getCompatibilityInstance().getNonOptimisticVersionCheck();
}
}
return false;
} }
/** /**

View File

@ -17,6 +17,7 @@ package org.apache.openjpa.util;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
@ -166,6 +167,30 @@ public class ImplHelper {
} }
} }
/**
* Returns the fields of the state that require an update.
*
* @param sm the state to check
* @return the BitSet of fields that need update, or null if none
*/
public static BitSet getUpdateFields(OpenJPAStateManager sm) {
if ((sm.getPCState() == PCState.PDIRTY
&& (!sm.isFlushed() || sm.isFlushedDirty()))
|| (sm.getPCState() == PCState.PNEW && sm.isFlushedDirty())) {
BitSet dirty = sm.getDirty();
if (sm.isFlushed()) {
dirty = (BitSet) dirty.clone();
dirty.andNot(sm.getFlushed());
}
if (dirty.length() > 0)
return dirty;
}
return null;
}
/** /**
* Close the given resource. The resource can be an extent iterator, * Close the given resource. The resource can be an extent iterator,
* query result, large result set relation, or any closeable OpenJPA * query result, large result set relation, or any closeable OpenJPA