diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java index 308b3be52..d0e796191 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractUpdateManager.java @@ -35,6 +35,7 @@ import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.PCState; import org.apache.openjpa.lib.conf.Configurable; import org.apache.openjpa.lib.conf.Configuration; +import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.OpenJPAException; import org.apache.openjpa.util.OptimisticException; @@ -139,6 +140,8 @@ public abstract class AbstractUpdateManager RowManager rowMgr, JDBCStore store, Collection exceps, Collection customs) { try { + BitSet dirty; + if (sm.getPCState() == PCState.PNEW && !sm.isFlushed()) { insert(sm, (ClassMapping) sm.getMetaData(), rowMgr, store, customs); @@ -146,18 +149,9 @@ public abstract class AbstractUpdateManager || sm.getPCState() == PCState.PDELETED) { delete(sm, (ClassMapping) sm.getMetaData(), rowMgr, store, customs); - } else 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) - update(sm, dirty, (ClassMapping) sm.getMetaData(), rowMgr, - store, customs); + } else if ((dirty = ImplHelper.getUpdateFields(sm)) != null) { + update(sm, dirty, (ClassMapping) sm.getMetaData(), rowMgr, + store, customs); } else if (sm.isVersionUpdateRequired()) { updateIndicators(sm, (ClassMapping) sm.getMetaData(), rowMgr, store, customs, true); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java index 1098b3987..d3cb0022c 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ColumnVersionStrategy.java @@ -138,7 +138,7 @@ public abstract class ColumnVersionStrategy // set where and update conditions on row for (int i = 0; i < cols.length; i++) { - if (curVersion != null) + if (curVersion != null && sm.isVersionCheckRequired()) row.whereObject(cols[i], curVersion); if (vers.getColumnIO().isUpdatable(i, nextVersion == null)) row.setObject(cols[i], nextVersion); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StateComparisonVersionStrategy.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StateComparisonVersionStrategy.java index 18d52d67f..0e2b4a598 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StateComparisonVersionStrategy.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/StateComparisonVersionStrategy.java @@ -101,28 +101,30 @@ public class StateComparisonVersionStrategy // db values match our previous image FieldMapping[] fields = (FieldMapping[]) sm.getMetaData().getFields(); Row row; - for (int i = 0, max = loaded.length(); i < max; i++) { - if (!loaded.get(i)) - continue; + if (sm.isVersionCheckRequired()) { + for (int i = 0, max = loaded.length(); i < max; i++) { + if (!loaded.get(i)) + continue; - // update our next state image with the new field value - if (sm.getDirty().get(i) && !sm.getFlushed().get(i)) - nextState[i] = sm.fetch(fields[i].getIndex()); + // update our next state image with the new field value + if (sm.getDirty().get(i) && !sm.getFlushed().get(i)) + nextState[i] = sm.fetch(fields[i].getIndex()); - // 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 - // won't detect OL exceptions when another transaction changes - // fields that aren't in any of the same tables as fields that - // this transaction changed - row = rm.getRow(fields[i].getTable(), Row.ACTION_UPDATE, - sm, false); - if (row == null) - continue; + // 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 + // won't detect OL exceptions when another transaction changes + // fields that aren't in any of the same tables as fields that + // this transaction changed + row = rm.getRow(fields[i].getTable(), Row.ACTION_UPDATE, + sm, false); + if (row == null) + continue; - // set WHERE criteria matching the previous state image so the - // update will fail if any changes have been made by another trans - fields[i].where(sm, store, rm, state[i]); - row.setFailedObject(sm.getManagedInstance()); + // set WHERE criteria matching the previous state image so the + // update will fail for any changes made by another transaction + fields[i].where(sm, store, rm, state[i]); + row.setFailedObject(sm.getManagedInstance()); + } } sm.setNextVersion(nextState); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java index adaa4c93e..8479b87db 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/conf/Compatibility.java @@ -26,6 +26,7 @@ public class Compatibility { private boolean _copyIds = false; private boolean _closeOnCommit = true; private boolean _quotedNumbers = false; + private boolean _nonOptimisticVersionCheck = false; /** * Whether to require exact identity value types when creating object @@ -136,4 +137,24 @@ public class Compatibility { public void setCloseOnManagedCommit(boolean 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; + } + } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index 270b72447..5d2b6211a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -38,6 +38,7 @@ import org.apache.openjpa.meta.ValueMetaData; import org.apache.openjpa.meta.ValueStrategies; import org.apache.openjpa.util.ApplicationIds; import org.apache.openjpa.util.Exceptions; +import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.InvalidStateException; import org.apache.openjpa.util.ObjectNotFoundException; @@ -2926,7 +2927,23 @@ public class StateManagerImpl * Returns whether this instance needs a version check. */ 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; } /** diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java index b816f00f1..caf6814c0 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java @@ -17,6 +17,7 @@ package org.apache.openjpa.util; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; 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, * query result, large result set relation, or any closeable OpenJPA