You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/19 23:35:07 UTC
svn commit: r423615 [4/44] - in /incubator/openjpa/trunk: ./ openjpa-jdbc-5/
openjpa-jdbc-5/src/ openjpa-jdbc-5/src/main/ openjpa-jdbc-5/src/main/java/
openjpa-jdbc-5/src/main/java/org/ openjpa-jdbc-5/src/main/java/org/apache/
openjpa-jdbc-5/src/main/j...
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStore.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStore.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStore.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStore.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.kernel;
+
+import java.sql.Connection;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.SQLFactory;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.util.Id;
+
+/**
+ * Represents the JDBC store.
+ *
+ * @author Abe White
+ * @since 4.0
+ */
+public interface JDBCStore {
+
+ /**
+ * Current persistence context.
+ */
+ public StoreContext getContext();
+
+ /**
+ * Return the configuration for this runtime.
+ */
+ public JDBCConfiguration getConfiguration();
+
+ /**
+ * Return the dictionary in use.
+ */
+ public DBDictionary getDBDictionary();
+
+ /**
+ * Return the SQL factory for this runtime.
+ */
+ public SQLFactory getSQLFactory();
+
+ /**
+ * If the lock manager in use is a {@link JDBCLockManager}, return it.
+ */
+ public JDBCLockManager getLockManager();
+
+ /**
+ * Return a SQL connection to the database.
+ * The <code>close</code> method should always be called on the connection
+ * to free any resources it is using. When appropriate, the close
+ * method is implemented as a no-op.
+ */
+ public Connection getConnection();
+
+ /**
+ * Return the current default fetch configuration.
+ */
+ public JDBCFetchConfiguration getFetchConfiguration();
+
+ /**
+ * Create a new datastore identity object from the given id value and
+ * mapping.
+ */
+ public Id newDataStoreId(long id, ClassMapping mapping, boolean subs);
+
+ /**
+ * Find the object with the given oid. Convenience method on top of
+ * the store's persistence context.
+ *
+ * @param vm the mapping holding this oid, or null if not applicable
+ */
+ public Object find(Object oid, ValueMapping vm,
+ JDBCFetchState fetchState);
+
+ /**
+ * Makes sure all subclasses of the given type are loaded in the JVM.
+ * This is usually done automatically.
+ */
+ public void loadSubclasses(ClassMapping mapping);
+
+ /**
+ * Add WHERE conditions to the given select limiting the returned results
+ * to the given mapping type, possibly including subclasses.
+ *
+ * @return true if the mapping was joined down to its base class
+ * in order to add the conditions
+ */
+ public boolean addClassConditions(Select sel, ClassMapping mapping,
+ boolean subs, Joins joins);
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStore.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,1458 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.kernel;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import javax.sql.DataSource;
+
+import org.apache.openjpa.event.OrphanedKeyAction;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.Discriminator;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.SQLExceptions;
+import org.apache.openjpa.jdbc.sql.SQLFactory;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.jdbc.sql.SelectExecutor;
+import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.FetchConfiguration;
+import org.apache.openjpa.kernel.FetchState;
+import org.apache.openjpa.kernel.LockManager;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.PCState;
+import org.apache.openjpa.kernel.QueryLanguages;
+import org.apache.openjpa.kernel.Seq;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.kernel.StoreManager;
+import org.apache.openjpa.kernel.StoreQuery;
+import org.apache.openjpa.kernel.exps.ExpressionParser;
+import org.apache.openjpa.lib.jdbc.DelegatingConnection;
+import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
+import org.apache.openjpa.lib.jdbc.DelegatingStatement;
+import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
+import org.apache.openjpa.lib.rop.ResultObjectProvider;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.ValueStrategies;
+import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.Id;
+import org.apache.openjpa.util.ImplHelper;
+import org.apache.openjpa.util.InvalidStateException;
+import org.apache.openjpa.util.OpenJPAId;
+import org.apache.openjpa.util.StoreException;
+import org.apache.openjpa.util.UserException;
+
+/**
+ * StoreManager plugin that uses JDBC to store persistent data in a
+ * relational data store.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class JDBCStoreManager
+ implements StoreManager, JDBCStore {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (JDBCStoreManager.class);
+
+ private StoreContext _ctx = null;
+ private JDBCConfiguration _conf = null;
+ private DBDictionary _dict = null;
+ private SQLFactory _sql = null;
+ private JDBCLockManager _lm = null;
+ private DataSource _ds = null;
+ private RefCountConnection _conn = null;
+ private boolean _active = false;
+
+ // track the pending statements so we can cancel them
+ private Set _stmnts = Collections.synchronizedSet(new HashSet());
+
+ public StoreContext getContext() {
+ return _ctx;
+ }
+
+ public void setContext(StoreContext ctx) {
+ _ctx = ctx;
+ _conf = (JDBCConfiguration) ctx.getConfiguration();
+ _dict = _conf.getDBDictionaryInstance();
+ _sql = _conf.getSQLFactoryInstance();
+
+ LockManager lm = ctx.getLockManager();
+ if (lm instanceof JDBCLockManager)
+ _lm = (JDBCLockManager) lm;
+
+ if (!ctx.isManaged() && _conf.isConnectionFactoryModeManaged())
+ _ds = _conf.getDataSource2(ctx);
+ else
+ _ds = _conf.getDataSource(ctx);
+
+ if (_conf.getUpdateManagerInstance().orderDirty())
+ ctx.setOrderDirtyObjects(true);
+ }
+
+ public JDBCConfiguration getConfiguration() {
+ return _conf;
+ }
+
+ public DBDictionary getDBDictionary() {
+ return _dict;
+ }
+
+ public SQLFactory getSQLFactory() {
+ return _sql;
+ }
+
+ public JDBCLockManager getLockManager() {
+ return _lm;
+ }
+
+ public JDBCFetchConfiguration getFetchConfiguration() {
+ return (JDBCFetchConfiguration) _ctx.getFetchConfiguration();
+ }
+
+ private JDBCFetchConfiguration getFetchConfiguration
+ (JDBCFetchState fetchState) {
+ return (fetchState == null) ? getFetchConfiguration()
+ : fetchState.getJDBCFetchConfiguration();
+ }
+
+ public void beginOptimistic() {
+ }
+
+ public void rollbackOptimistic() {
+ }
+
+ public void begin() {
+ _active = true;
+ try {
+ if ((!_ctx.isManaged() || !_conf.isConnectionFactoryModeManaged())
+ && _conn.getAutoCommit())
+ _conn.setAutoCommit(false);
+ } catch (SQLException se) {
+ _active = false;
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ public void commit() {
+ try {
+ if (!_ctx.isManaged() || !_conf.isConnectionFactoryModeManaged())
+ _conn.commit();
+ } catch (SQLException se) {
+ try {
+ _conn.rollback();
+ } catch (SQLException se2) {
+ }
+ throw SQLExceptions.getStore(se, _dict);
+ } finally {
+ _active = false;
+ }
+ }
+
+ public void rollback() {
+ // already rolled back ourselves?
+ if (!_active)
+ return;
+
+ try {
+ if (_conn != null && (!_ctx.isManaged()
+ || !_conf.isConnectionFactoryModeManaged()))
+ _conn.rollback();
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ } finally {
+ _active = false;
+ }
+ }
+
+ public void retainConnection() {
+ connect(false);
+ _conn.setRetain(true);
+ }
+
+ public void releaseConnection() {
+ if (_conn != null)
+ _conn.setRetain(false);
+ }
+
+ public Object getClientConnection() {
+ return new ClientConnection(getConnection());
+ }
+
+ public Connection getConnection() {
+ connect(true);
+ return _conn;
+ }
+
+ public boolean exists(OpenJPAStateManager sm, Object context) {
+ // add where conditions on base class to avoid joins if subclass
+ // doesn't use oid as identifier
+ ClassMapping mapping = (ClassMapping) sm.getMetaData();
+ return exists(mapping, sm.getObjectId(), context);
+ }
+
+ private boolean exists(ClassMapping mapping, Object oid, Object context) {
+ // add where conditions on base class to avoid joins if subclass
+ // doesn't use oid as identifier
+ Select sel = _sql.newSelect();
+ while (mapping.getJoinablePCSuperclassMapping() != null)
+ mapping = mapping.getJoinablePCSuperclassMapping();
+
+ sel.wherePrimaryKey(oid, mapping, this);
+ try {
+ return sel.getCount(this) != 0;
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ public boolean syncVersion(OpenJPAStateManager sm, Object context) {
+ ClassMapping mapping = (ClassMapping) sm.getMetaData();
+ try {
+ return mapping.getVersion().checkVersion(sm, this, true);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ public int compareVersion(OpenJPAStateManager state, Object v1, Object v2) {
+ ClassMapping mapping = (ClassMapping) state.getMetaData();
+ return mapping.getVersion().compareVersion(v1, v2);
+ }
+
+ public boolean initialize(OpenJPAStateManager sm, PCState state,
+ FetchState fetchState, Object context) {
+ ConnectionInfo info = (ConnectionInfo) context;
+ try {
+ return initializeState(sm, state, (JDBCFetchState) fetchState,
+ info);
+ } catch (ClassNotFoundException cnfe) {
+ throw new UserException(cnfe);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ /**
+ * Initialize a newly-loaded instance.
+ */
+ private boolean initializeState(OpenJPAStateManager sm, PCState state,
+ JDBCFetchState fetchState, ConnectionInfo info)
+ throws ClassNotFoundException, SQLException {
+ Object oid = sm.getObjectId();
+ ClassMapping mapping = (ClassMapping) sm.getMetaData();
+ JDBCFetchConfiguration jfetch = getFetchConfiguration(fetchState);
+ Result res = null;
+ if (info != null && info.result != null) {
+ res = info.result;
+ info.sm = sm;
+ if (info.mapping == null)
+ info.mapping = mapping;
+ mapping = info.mapping;
+ } else
+ if (oid instanceof OpenJPAId && !((OpenJPAId) oid).hasSubclasses()) {
+ Boolean custom = customLoad(sm, mapping, state, jfetch);
+ if (custom != null)
+ return custom.booleanValue();
+ res = getInitializeStateResult(sm, mapping, fetchState,
+ Select.SUBS_EXACT);
+ if (res == null && !selectPrimaryKey(sm, mapping, jfetch))
+ return false;
+ if (res != null && !res.next())
+ return false;
+ } else {
+ ClassMapping[] mappings = mapping.
+ getIndependentAssignableMappings();
+ if (mappings.length == 1) {
+ mapping = mappings[0];
+ Boolean custom = customLoad(sm, mapping, state, jfetch);
+ if (custom != null)
+ return custom.booleanValue();
+ res = getInitializeStateResult(sm, mapping, fetchState,
+ Select.SUBS_ANY_JOINABLE);
+ if (res == null && !selectPrimaryKey(sm, mapping, jfetch))
+ return false;
+ } else
+ res = getInitializeStateUnionResult(sm, mapping, mappings,
+ fetchState);
+ if (res != null && !res.next())
+ return false;
+ }
+
+ try {
+ // figure out what type of object this is; the state manager
+ // only guarantees to provide a base class
+ Class type;
+ if (res == null)
+ type = mapping.getDescribedType();
+ else {
+ if (res.getBaseMapping() != null)
+ mapping = res.getBaseMapping();
+ res.startDataRequest(mapping.getDiscriminator());
+ try {
+ type = mapping.getDiscriminator().getClass(this, mapping,
+ res);
+ } finally {
+ res.endDataRequest();
+ }
+ }
+
+ // initialize the state manager; this may change the mapping
+ // and the object id instance if the type as determined
+ // from the indicator is a subclass of expected type
+ sm.initialize(type, state);
+
+ // load the selected mappings into the given state manager
+ if (res != null) {
+ // re-get the mapping in case the instance was a subclass
+ mapping = (ClassMapping) sm.getMetaData();
+
+ load(mapping, sm, fetchState, res);
+ mapping.getVersion().afterLoad(sm, this);
+ }
+ return true;
+ } finally {
+ if (res != null && (info == null || res != info.result))
+ res.close();
+ }
+ }
+
+ /**
+ * Allow the mapping to custom load data. Return null if the mapping
+ * does not use custom loading.
+ */
+ private Boolean customLoad(OpenJPAStateManager sm, ClassMapping mapping,
+ PCState state, JDBCFetchConfiguration jfetch)
+ throws ClassNotFoundException, SQLException {
+ // check to see if the mapping takes care of initialization
+ if (!mapping.customLoad(sm, this, state, jfetch))
+ return null;
+ if (sm.getManagedInstance() != null) {
+ mapping.getVersion().afterLoad(sm, this);
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ /**
+ * Select the data for the given instance and return the result. Return
+ * null if there is no data in the current fetch groups to select.
+ */
+ private Result getInitializeStateResult(OpenJPAStateManager sm,
+ ClassMapping mapping, JDBCFetchState fetchState, int subs)
+ throws SQLException {
+ Select sel = _sql.newSelect();
+ if (!select(sel, mapping, subs, sm, null, fetchState,
+ JDBCFetchConfiguration.EAGER_JOIN, true))
+ return null;
+
+ sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
+ return sel.execute(this, getFetchConfiguration(fetchState));
+ }
+
+ /**
+ * Select a union of the data for the given instance from possible concrete
+ * mappings and return the result.
+ */
+ private Result getInitializeStateUnionResult(final OpenJPAStateManager sm,
+ ClassMapping mapping, final ClassMapping[] mappings,
+ final JDBCFetchState fetchState)
+ throws SQLException {
+ final JDBCStoreManager store = this;
+ JDBCFetchConfiguration jfetch = getFetchConfiguration(fetchState);
+ final int eager = Math.min(jfetch.getEagerFetchMode(),
+ JDBCFetchConfiguration.EAGER_JOIN);
+
+ Union union = _sql.newUnion(mappings.length);
+ union.setSingleResult(true);
+ if (jfetch.getSubclassFetchMode(mapping) != jfetch.EAGER_JOIN)
+ union.abortUnion();
+ union.select(new Union.Selector() {
+ public void select(Select sel, int i) {
+ sel.select(mappings[i], Select.SUBS_ANY_JOINABLE, store,
+ fetchState, eager);
+ sel.wherePrimaryKey(sm.getObjectId(), mappings[i], store);
+ }
+ });
+ return union.execute(this, jfetch);
+ }
+
+ /**
+ * Select primary key data to make sure the given instance exists, locking
+ * if needed.
+ */
+ private boolean selectPrimaryKey(OpenJPAStateManager sm,
+ ClassMapping mapping,
+ JDBCFetchConfiguration jfetch)
+ throws SQLException {
+ // select pks from base class record to ensure it exists and lock
+ // it if needed
+ ClassMapping base = mapping;
+ while (base.getJoinablePCSuperclassMapping() != null)
+ base = base.getJoinablePCSuperclassMapping();
+
+ Select sel = _sql.newSelect();
+ sel.select(base.getPrimaryKeyColumns());
+ sel.wherePrimaryKey(sm.getObjectId(), base, this);
+ Result exists = sel.execute(this, jfetch);
+ try {
+ if (!exists.next())
+ return false;
+
+ // record locked?
+ if (_active && _lm != null && exists.isLocking())
+ _lm.loadedForUpdate(sm);
+ return true;
+ } finally {
+ exists.close();
+ }
+ }
+
+ public boolean load(OpenJPAStateManager sm, BitSet fields,
+ FetchState fetchState, int lockLevel, Object context) {
+ JDBCFetchState jfetchState = (fetchState == null)
+ ? (JDBCFetchState) getFetchConfiguration().newFetchState()
+ : (JDBCFetchState) fetchState;
+
+ JDBCFetchConfiguration jfetch = getFetchConfiguration(jfetchState);
+
+ // get a connection, or reuse current one
+ ConnectionInfo info = (ConnectionInfo) context;
+ Result res = null;
+ if (info != null) {
+ // if initialize() fails to load required fields, then this method
+ // is called; make sure not to try to use the given result if it's
+ // the same one we just failed to completely initialize() with
+ if (info.sm != sm)
+ res = info.result;
+ info.sm = null;
+ }
+ try {
+ // if there's an existing result, load all we can from it
+ ClassMapping mapping = (ClassMapping) sm.getMetaData();
+ if (res != null) {
+ load(mapping, sm, jfetchState, res);
+ removeLoadedFields(sm, fields);
+ }
+
+ // if the instance is hollow and there's a customized
+ // get by id method, use it
+ if (sm.getLoaded().length() == 0)
+ if (mapping.customLoad(sm, this, null, jfetch))
+ removeLoadedFields(sm, fields);
+
+ //### select is kind of a big object, and in some cases we don't
+ //### use it... would it be worth it to have a small shell select
+ //### object that only creates a real select when actually used?
+
+ Select sel = _sql.newSelect();
+ if (select(sel, mapping, sel.SUBS_EXACT, sm, fields, jfetchState,
+ jfetch.EAGER_JOIN, true)) {
+ sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
+ res = sel.execute(this, jfetch, lockLevel);
+ try {
+ if (!res.next())
+ return false;
+ load(mapping, sm, jfetchState, res);
+ } finally {
+ res.close();
+ }
+ }
+
+ // now allow the fields to load themselves individually too
+ for (int i = 0, len = fields.length(); i < len; i++)
+ if (fields.get(i) && !sm.getLoaded().get(i))
+ mapping.getFieldMapping(i).load(sm, this, jfetchState);
+
+ mapping.getVersion().afterLoad(sm, this);
+ return true;
+ } catch (ClassNotFoundException cnfe) {
+ throw new StoreException(cnfe);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ /**
+ * Return a list formed by removing all loaded fields from the given one.
+ */
+ private void removeLoadedFields(OpenJPAStateManager sm, BitSet fields) {
+ for (int i = 0, len = fields.length(); i < len; i++)
+ if (fields.get(i) && sm.getLoaded().get(i))
+ fields.clear(i);
+ }
+
+ public Collection loadAll(Collection sms, PCState state, int load,
+ FetchState fetchState, Object context) {
+ return ImplHelper.loadAll(sms, this, state, load, fetchState, context);
+ }
+
+ public void beforeStateChange(OpenJPAStateManager sm, PCState fromState,
+ PCState toState) {
+ }
+
+ public Collection flush(Collection sms) {
+ return _conf.getUpdateManagerInstance().flush(sms, this);
+ }
+
+ public boolean cancelAll() {
+ // note that this method does not lock the context, since
+ // we want to allow a different thread to be able to cancel the
+ // outstanding statement on a different context
+
+ Collection stmnts;
+ synchronized (_stmnts) {
+ if (_stmnts.isEmpty())
+ return false;
+ stmnts = new ArrayList(_stmnts);
+ }
+
+ try {
+ for (Iterator itr = stmnts.iterator(); itr.hasNext();)
+ ((Statement) itr.next()).cancel();
+ return true;
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ public boolean assignObjectId(OpenJPAStateManager sm, boolean preFlush) {
+ ClassMetaData meta = sm.getMetaData();
+ if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
+ return ApplicationIds.assign(sm, this, preFlush);
+
+ // datastore identity
+ Object val = ImplHelper.generateIdentityValue(_ctx, meta,
+ JavaTypes.LONG);
+ if (val == null && meta.getIdentityStrategy() != ValueStrategies.NATIVE)
+ return false;
+ if (val == null)
+ val = getDataStoreIdSequence(meta).next(_ctx, meta);
+ sm.setObjectId(newDataStoreId(val, meta));
+ return true;
+ }
+
+ public boolean assignField(OpenJPAStateManager sm, int field,
+ boolean preFlush) {
+ FieldMetaData fmd = sm.getMetaData().getField(field);
+ Object val = ImplHelper.generateFieldValue(_ctx, fmd);
+ if (val == null)
+ return false;
+ sm.store(field, val);
+ return true;
+ }
+
+ public Class getManagedType(Object oid) {
+ if (oid instanceof Id)
+ return ((Id) oid).getType();
+ return null;
+ }
+
+ public Class getDataStoreIdType(ClassMetaData meta) {
+ return Id.class;
+ }
+
+ public Object copyDataStoreId(Object oid, ClassMetaData meta) {
+ Id id = (Id) oid;
+ return new Id(meta.getDescribedType(), id.getId(),
+ id.hasSubclasses());
+ }
+
+ public Object newDataStoreId(Object val, ClassMetaData meta) {
+ return Id.newInstance(meta.getDescribedType(), val);
+ }
+
+ public Id newDataStoreId(long id, ClassMapping mapping, boolean subs) {
+ return new Id(mapping.getDescribedType(), id, subs);
+ }
+
+ public ResultObjectProvider executeExtent(ClassMetaData meta,
+ final boolean subclasses, FetchConfiguration fetch) {
+ ClassMapping mapping = (ClassMapping) meta;
+ final ClassMapping[] mappings;
+ if (subclasses)
+ mappings = mapping.getIndependentAssignableMappings();
+ else
+ mappings = new ClassMapping[]{ mapping };
+
+ ResultObjectProvider[] rops = null;
+ final JDBCFetchConfiguration jfetch = (JDBCFetchConfiguration) fetch;
+ final JDBCFetchState jfetchState = (JDBCFetchState)
+ jfetch.newFetchState();
+ if (jfetch.getSubclassFetchMode(mapping) != jfetch.EAGER_JOIN)
+ rops = new ResultObjectProvider[mappings.length];
+
+ try {
+ // check for custom loads
+ ResultObjectProvider rop;
+ for (int i = 0; i < mappings.length; i++) {
+ rop = mappings[i].customLoad(this, subclasses, jfetch, 0,
+ Long.MAX_VALUE);
+ if (rop != null) {
+ if (rops == null)
+ rops = new ResultObjectProvider[mappings.length];
+ rops[i] = rop;
+ }
+ }
+
+ // if we're selecting independent mappings separately or have
+ // custom loads, do individual selects for each class
+ rop = null;
+ if (rops != null) {
+ for (int i = 0; i < mappings.length; i++) {
+ if (rops[i] != null)
+ continue;
+
+ Select sel = _sql.newSelect();
+ sel.setLRS(true);
+ BitSet paged = selectExtent(sel, mappings[i], jfetchState,
+ subclasses);
+ if (paged == null)
+ rops[i] = new InstanceResultObjectProvider(sel,
+ mappings[i], this, jfetchState);
+ else
+ rops[i] = new PagingResultObjectProvider(sel,
+ mappings[i], this, jfetchState, paged,
+ Long.MAX_VALUE);
+ }
+ if (rops.length == 1)
+ return rops[0];
+ return new MergedResultObjectProvider(rops);
+ }
+
+ // perform a union on all independent classes
+ Union union = _sql.newUnion(mappings.length);
+ union.setLRS(true);
+ final BitSet[] paged = new BitSet[mappings.length];
+ union.select(new Union.Selector() {
+ public void select(Select sel, int idx) {
+ paged[idx] = selectExtent(sel, mappings[idx], jfetchState,
+ subclasses);
+ }
+ });
+
+ // using paging rop if any union element has paged fields
+ for (int i = 0; i < paged.length; i++) {
+ if (paged[i] != null)
+ return new PagingResultObjectProvider(union, mappings,
+ JDBCStoreManager.this, jfetchState, paged,
+ Long.MAX_VALUE);
+ }
+ return new InstanceResultObjectProvider(union, mappings[0],
+ this, jfetchState);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ /**
+ * Select the given mapping for use in an extent, returning paged fields.
+ */
+ private BitSet selectExtent(Select sel, ClassMapping mapping,
+ JDBCFetchState jfetchState, boolean subclasses) {
+ int subs = (subclasses) ? Select.SUBS_JOINABLE : Select.SUBS_NONE;
+ // decide between paging and standard iteration
+ BitSet paged = PagingResultObjectProvider.getPagedFields(sel,
+ mapping, this, jfetchState, JDBCFetchConfiguration.EAGER_PARALLEL,
+ Long.MAX_VALUE);
+ if (paged == null)
+ sel.selectIdentifier(mapping, subs, this, jfetchState,
+ JDBCFetchConfiguration.EAGER_PARALLEL);
+ else
+ sel.selectIdentifier(mapping, subs, this, jfetchState,
+ JDBCFetchConfiguration.EAGER_JOIN);
+ return paged;
+ }
+
+ public StoreQuery newQuery(String language) {
+ ExpressionParser ep = QueryLanguages.parserForLanguage(language);
+ if (ep != null)
+ return new JDBCStoreQuery(this, ep);
+ if (QueryLanguages.LANG_SQL.equals(language))
+ return new SQLStoreQuery(this);
+ return null;
+ }
+
+ public FetchConfiguration newFetchConfiguration() {
+ return new JDBCFetchConfigurationImpl();
+ }
+
+ public Seq getDataStoreIdSequence(ClassMetaData meta) {
+ if (meta.getIdentityStrategy() == ValueStrategies.NATIVE
+ || meta.getIdentityStrategy() == ValueStrategies.NONE)
+ return _conf.getSequenceInstance();
+ return null;
+ }
+
+ public Seq getValueSequence(FieldMetaData fmd) {
+ return null;
+ }
+
+ public void close() {
+ if (_conn != null)
+ _conn.free();
+ }
+
+ /////////////
+ // Utilities
+ /////////////
+
+ /**
+ * Connect to the db.
+ */
+ private void connect(boolean ref) {
+ _ctx.lock();
+ try {
+ // connect if the connection is currently null, or if
+ // the connection has been closed out from under us
+ if (_conn == null)
+ _conn = connectInternal();
+ if (ref)
+ _conn.ref();
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ } finally {
+ _ctx.unlock();
+ }
+ }
+
+ /**
+ * Connect to the database. This method is separated out so that it
+ * can be profiled.
+ */
+ private RefCountConnection connectInternal()
+ throws SQLException {
+ return new RefCountConnection(_ds.getConnection());
+ }
+
+ /**
+ * Find the object with the given oid.
+ */
+ public Object find(Object oid, ValueMapping vm,
+ JDBCFetchState fetchState) {
+ if (oid == null)
+ return null;
+ Object pc = _ctx.find(oid, fetchState, null, null, 0);
+ if (pc == null && vm != null) {
+ OrphanedKeyAction action = _conf.getOrphanedKeyActionInstance();
+ pc = action.orphan(oid, null, vm);
+ }
+ return pc;
+ }
+
+ /**
+ * Load the object in the current row of the given result.
+ */
+ public Object load(ClassMapping mapping, JDBCFetchState fetchState,
+ BitSet exclude, Result result)
+ throws SQLException {
+ if (!mapping.isMapped())
+ throw new InvalidStateException(_loc.get("virtual-mapping",
+ mapping));
+
+ // get the object id for the row; base class selects pk columns
+ ClassMapping base = mapping;
+ while (base.getJoinablePCSuperclassMapping() != null)
+ base = base.getJoinablePCSuperclassMapping();
+ Object oid = base.getObjectId(this, result, null, true, null);
+ if (oid == null)
+ return null;
+
+ ConnectionInfo info = new ConnectionInfo();
+ info.result = result;
+ info.mapping = mapping;
+ return _ctx.find(oid, fetchState, exclude, info, 0);
+ }
+
+ /**
+ * Load the given state manager with data from the result set. Only
+ * mappings originally selected will be loaded.
+ */
+ private void load(ClassMapping mapping, OpenJPAStateManager sm,
+ JDBCFetchState fetchState, Result res)
+ throws SQLException {
+ FieldMapping eagerToMany = load(mapping, sm, fetchState, res, null);
+ if (eagerToMany != null)
+ eagerToMany.loadEagerJoin(sm, this, fetchState, res);
+ if (_active && _lm != null && res.isLocking())
+ _lm.loadedForUpdate(sm);
+ }
+
+ /**
+ * Load the fields of the given mapping. Return any to-many eager field
+ * without loading it.
+ */
+ private FieldMapping load(ClassMapping mapping, OpenJPAStateManager sm,
+ JDBCFetchState fetchState, Result res, FieldMapping eagerToMany)
+ throws SQLException {
+ JDBCFetchConfiguration fetch = getFetchConfiguration(fetchState);
+ if (mapping.customLoad(sm, this, fetch, res))
+ return eagerToMany;
+
+ // load superclass data; base class loads version
+ ClassMapping parent = mapping.getJoinablePCSuperclassMapping();
+ if (parent != null)
+ eagerToMany = load(parent, sm, fetchState, res, eagerToMany);
+ else if (sm.getVersion() == null)
+ mapping.getVersion().load(sm, this, res);
+
+ // load unloaded fields
+ FieldMapping[] fms = mapping.getDefinedFieldMappings();
+ Object eres, processed;
+ for (int i = 0; i < fms.length; i++) {
+ if (fms[i].isPrimaryKey()
+ || sm.getLoaded().get(fms[i].getIndex()))
+ continue;
+
+ // check for eager result, and if not present do standard load
+ eres = res.getEager(fms[i]);
+ res.startDataRequest(fms[i]);
+ try {
+ if (eres == res) {
+ if (eagerToMany == null && fms[i].isEagerSelectToMany())
+ eagerToMany = fms[i];
+ else
+ fms[i].loadEagerJoin(sm, this, fetchState, res);
+ } else if (eres != null) {
+ processed = fms[i].loadEagerParallel(sm, this, fetchState,
+ eres);
+ if (processed != eres)
+ res.putEager(fms[i], processed);
+ } else
+ fms[i].load(sm, this, fetchState, res);
+ } finally {
+ res.endDataRequest();
+ }
+ }
+ return eagerToMany;
+ }
+
+ /**
+ * For implementation use only.
+ * Return a select for the proper mappings. Return null if no select is
+ * needed. The method is designed to be complementary to the load methods.
+ *
+ * @param sel select to build on
+ * @param mapping the mapping for the base type to select for
+ * @param subs whether the select might include subclasses of the
+ * given mapping
+ * @param sm state manager if an instance is being loaded or
+ * initialized, else null
+ * @param fields if a state manager is being loaded, the set of
+ * fields that must be loaded in order, else null
+ * @param fetchState the fetch configuration; used if no specific fields
+ * must be loaded, and used when selecting relations
+ * @param eager eager fetch mode to use
+ * @param ident whether to select primary key columns as distinct
+ * identifiers
+ * @return true if the select is required, false otherwise
+ */
+ public boolean select(Select sel, ClassMapping mapping, int subs,
+ OpenJPAStateManager sm, BitSet fields, JDBCFetchState fetchState,
+ int eager, boolean ident) {
+ // add class conditions so that they're cloned for any batched selects
+ boolean joinedSupers = false;
+ if ((sm == null || sm.getPCState() == PCState.TRANSIENT)
+ && (subs == sel.SUBS_JOINABLE || subs == sel.SUBS_NONE))
+ joinedSupers = addClassConditions(sel, mapping,
+ subs == sel.SUBS_JOINABLE, null);
+
+ // create all our eager selects so that those fields are reserved
+ // and cannot be reused during the actual eager select process,
+ // preventing infinite recursion
+ JDBCFetchConfiguration fetch = getFetchConfiguration(fetchState);
+ eager = Math.min(eager, fetch.getEagerFetchMode());
+ FieldMapping eagerToMany = createEagerSelects(sel, mapping, sm,
+ fields, fetchState, eager);
+
+ // select all base class mappings; do this after batching so that
+ // the joins needed by these selects don't get in the WHERE clause
+ // of the batched selects
+ int seld = selectBaseMappings(sel, mapping, mapping, sm, fields,
+ fetchState, eager, eagerToMany, ident, joinedSupers);
+
+ // select eager to-many relations last because during load they
+ // advance the result set and could exhaust it, so no other mappings
+ // can load afterwords
+ if (eagerToMany != null)
+ eagerToMany.selectEagerJoin(sel, sm, this, fetchState, eager);
+
+ // optionally select subclass mappings
+ if (subs == sel.SUBS_JOINABLE || subs == sel.SUBS_ANY_JOINABLE)
+ selectSubclassMappings(sel, mapping, sm, fetchState);
+ if (sm != null)
+ sel.setDistinct(false);
+ return seld > 0;
+ }
+
+ /**
+ * Mark the fields of this mapping as reserved so that eager fetches can't
+ * get into infinite recursive situations.
+ */
+ private FieldMapping createEagerSelects(Select sel, ClassMapping mapping,
+ OpenJPAStateManager sm, BitSet fields, JDBCFetchState fetchState,
+ int eager) {
+ if (mapping == null || eager == JDBCFetchConfiguration.EAGER_NONE)
+ return null;
+
+ JDBCFetchConfiguration fetch = getFetchConfiguration(fetchState);
+ FieldMapping eagerToMany = createEagerSelects(sel, mapping.
+ getJoinablePCSuperclassMapping(), sm, fields, fetchState, eager);
+
+ FieldMapping[] fms = mapping.getDefinedFieldMappings();
+ boolean inEagerJoin = sel.hasEagerJoin(false);
+ int sels;
+ int jtype;
+ int mode;
+ for (int i = 0; i < fms.length; i++) {
+ if (!requiresSelect(fms[i], sm, fields, fetchState))
+ continue;
+ mode = fms[i].getEagerFetchMode();
+ if (mode == fetch.EAGER_NONE)
+ continue;
+
+ // try to select with join first
+ jtype = (fms[i].getNullValue() == fms[i].NULL_EXCEPTION)
+ ? sel.EAGER_INNER : sel.EAGER_OUTER;
+ if (mode != fetch.EAGER_PARALLEL
+ && !fms[i].isEagerSelectToMany()
+ && fms[i].supportsSelect(sel, jtype, sm, this, fetch) > 0
+ && sel.eagerClone(fms[i], jtype, false, 1) != null)
+ continue;
+
+ boolean hasJoin = fetch.hasJoin(fms[i].getFullName());
+
+ // if the field declares a preferred select mode of join or does not
+ // have a preferred mode and we're doing a by-id lookup, try
+ // to use a to-many join also. currently we limit eager
+ // outer joins to non-LRS, non-ranged selects that don't already
+ // have an eager to-many join
+ if ((hasJoin || mode == fetch.EAGER_JOIN
+ || (mode == fetch.DEFAULT && sm != null))
+ && fms[i].isEagerSelectToMany()
+ && !inEagerJoin && !sel.hasEagerJoin(true)
+ && (!sel.getAutoDistinct() || (!sel.isLRS()
+ && sel.getStartIndex() == 0
+ && sel.getEndIndex() == Long.MAX_VALUE))
+ && fms[i].supportsSelect(sel, jtype, sm, this, fetch) > 0) {
+ if (sel.eagerClone(fms[i], jtype, true, 1) != null)
+ eagerToMany = fms[i];
+ else
+ continue;
+ }
+
+ // finally, try parallel
+ if (eager == fetch.EAGER_PARALLEL && (sels = fms[i].
+ supportsSelect(sel, sel.EAGER_PARALLEL, sm, this, fetch)) != 0)
+ sel.eagerClone(fms[i], Select.EAGER_PARALLEL,
+ fms[i].isEagerSelectToMany(), sels);
+ }
+ return eagerToMany;
+ }
+
+ /**
+ * Determine if the given field needs to be selected.
+ */
+ private static boolean requiresSelect(FieldMapping fm,
+ OpenJPAStateManager sm,
+ BitSet fields, JDBCFetchState fetchState) {
+ if (fields != null)
+ return fields.get(fm.getIndex());
+ if (sm != null && sm.getPCState() != PCState.TRANSIENT
+ && sm.getLoaded().get(fm.getIndex()))
+ return false;
+ return fetchState.requiresSelect(fm, true);
+ }
+
+ /**
+ * Select the field mappings of the given class and all its superclasses.
+ *
+ * @param sel the select to use
+ * @param mapping the most-derived type to select for
+ * @param orig the original mapping type selected
+ * @param sm the instance being selected for, or null if none
+ * @param fields the fields to load
+ * @param fetch fetch configuration to use for loading relations
+ * @param eager the eager fetch mode to use
+ * @param joined whether the class has already been joined down to
+ * its base class
+ * @return > 0 if the select is required, 0 if data was
+ * selected but is not required, and < 0 if nothing was selected
+ */
+ private int selectBaseMappings(Select sel, ClassMapping mapping,
+ ClassMapping orig, OpenJPAStateManager sm, BitSet fields,
+ JDBCFetchState fetchState, int eager, FieldMapping eagerToMany,
+ boolean ident, boolean joined) {
+ JDBCFetchConfiguration fetch = getFetchConfiguration(fetchState);
+ ClassMapping parent = mapping.getJoinablePCSuperclassMapping();
+ if (parent == null && !mapping.isMapped())
+ throw new InvalidStateException(_loc.get("virtual-mapping",
+ mapping.getDescribedType()));
+
+ int seld = -1;
+ int pseld = -1;
+
+ // base class selects pks, etc
+ if (parent == null) {
+ // if no instance, select pks
+ if (sm == null) {
+ if (ident)
+ sel.selectIdentifier(mapping.getPrimaryKeyColumns());
+ else
+ sel.select(mapping.getPrimaryKeyColumns());
+ seld = 1;
+ }
+
+ // if no instance or not initialized and not exact oid, select type
+ if ((sm == null || (sm.getPCState() == PCState.TRANSIENT
+ && (!(sm.getObjectId()instanceof OpenJPAId)
+ || ((OpenJPAId) sm.getObjectId()).hasSubclasses())))
+ && mapping.getDiscriminator().select(sel, orig))
+ seld = 1;
+
+ // if no instance or no version, select version
+ if ((sm == null || sm.getVersion() == null)
+ && mapping.getVersion().select(sel, orig))
+ seld = 1;
+ } else {
+ // recurse on parent
+ pseld = selectBaseMappings(sel, parent, orig, sm, fields,
+ fetchState, eager, eagerToMany, ident, joined);
+ }
+
+ // select the mappings in the given fields set, or based on fetch
+ // configuration if no fields given
+ FieldMapping[] fms = mapping.getDefinedFieldMappings();
+ SelectExecutor esel;
+ int fseld;
+ for (int i = 0; i < fms.length; i++) {
+ // skip eager to-many select; we do that separately in calling
+ // method
+ if (fms[i] == eagerToMany)
+ continue;
+
+ // check for eager select
+ esel = sel.getEager(fms[i]);
+ if (esel != null) {
+ if (esel == sel)
+ fms[i].selectEagerJoin(sel, sm, this, fetchState, eager);
+ else
+ fms[i].selectEagerParallel(esel, sm, this, fetchState,
+ eager);
+ seld = Math.max(0, seld);
+ } else if (requiresSelect(fms[i], sm, fields, fetchState)) {
+ fseld = fms[i].select(sel, sm, this, fetchState, eager);
+ seld = Math.max(fseld, seld);
+ } else if (optSelect(fms[i], sel, sm, fetchState)) {
+ fseld = fms[i].select(sel, sm, this, fetchState,
+ fetch.EAGER_NONE);
+
+ // don't upgrade seld to > 0 based on these fields, since
+ // they're not in the calculated field set
+ if (fseld >= 0 && seld < 0)
+ seld = 0;
+ }
+ }
+
+ // join to parent table if the parent / any ancestors have selected
+ // anything
+ if (!joined && pseld >= 0 && parent.getTable() != mapping.getTable())
+ sel.where(mapping.joinSuperclass(sel.newJoins(), false));
+
+ // return the highest value
+ return Math.max(pseld, seld);
+ }
+
+ /**
+ * When selecting fieldes, a special case is made for mappings that use
+ * 2-part selects that aren't explicitly *not* in the dfg so that they
+ * can get their primary table data. This method tests for that special
+ * case as an optimization.
+ */
+ private boolean optSelect(FieldMapping fm, Select sel,
+ OpenJPAStateManager sm,
+ JDBCFetchState fetchState) {
+ return !fm.isDefaultFetchGroupExplicit()
+ && (sm == null || sm.getPCState() == PCState.TRANSIENT
+ || !sm.getLoaded().get(fm.getIndex()))
+ && fm.supportsSelect(sel, sel.TYPE_TWO_PART, sm, this,
+ getFetchConfiguration(fetchState)) > 0
+ && fetchState.requiresSelect(fm, true);
+ }
+
+ /**
+ * Select field mappings that match the given fetch configuration for
+ * subclasses of the given type.
+ *
+ * @param sel the select to use
+ * @param mapping the type whose subclasses to select
+ * @param sm the instance being selected for, or null if none
+ * @param fetch the fetch configuration
+ */
+ private void selectSubclassMappings(Select sel, ClassMapping mapping,
+ OpenJPAStateManager sm, JDBCFetchState fetchState) {
+ loadSubclasses(mapping);
+ JDBCFetchConfiguration fetch = getFetchConfiguration(fetchState);
+ ClassMapping[] subMappings = mapping.getJoinablePCSubclassMappings();
+ if (subMappings.length == 0)
+ return;
+
+ // select all subclass mappings that match the fetch configuration
+ // and whose table is in the list of those selected so far; this
+ // way we select the max possible without selecting any tables that
+ // aren't present in all possible query matches; a special case
+ // is made for mappings that use 2-part selects that aren't
+ // explicitly *not* in the default so that they can get their
+ // primary table data
+ FieldMapping[] fms;
+ boolean joined;
+ boolean canJoin = _dict.joinSyntax != JoinSyntaxes.SYNTAX_TRADITIONAL
+ && fetch.getSubclassFetchMode(mapping) != fetch.EAGER_NONE;
+ for (int i = 0; i < subMappings.length; i++) {
+ if (!subMappings[i].supportsEagerSelect(sel, sm, this, mapping,
+ fetch))
+ continue;
+
+ // initialize so that if we can't join, we pretend we already have
+ joined = !canJoin;
+ fms = subMappings[i].getDefinedFieldMappings();
+ for (int j = 0; j < fms.length; j++) {
+ // make sure in one of configured fetch groups
+ if (!fms[j].isInDefaultFetchGroup()
+ && !fetch.hasFetchGroup(fms[j].getFetchGroups())
+ && !fetch.hasField(fms[j].getFullName())
+ && (fms[j].isDefaultFetchGroupExplicit()
+ || fms[j].supportsSelect(sel, sel.TYPE_TWO_PART, sm,
+ this, fetch) <= 0))
+ continue;
+
+ // if we can join to the subclass, do so; much better chance
+ // that the field will be able to select itself without joins
+ if (!joined) {
+ // mark joined whether or not we join, so we don't have to
+ // test conditions again for this subclass
+ joined = true;
+ sel.where(joinSubclass(sel, mapping, subMappings[i],
+ null));
+ }
+
+ // if can select with tables already selected, do it
+ if (fms[j].supportsSelect(sel, sel.TYPE_JOINLESS, sm,
+ this, fetch) > 0)
+ fms[j].select(sel, null, this, fetchState,
+ fetch.EAGER_NONE);
+ }
+ }
+ }
+
+ /**
+ * Helper method to join from class to its subclass. Recursive to allow
+ * for multiple hops, starting from the base class.
+ */
+ private static Joins joinSubclass(Select sel, ClassMapping base,
+ ClassMapping sub, Joins joins) {
+ if (sub == base || sub.getTable() == base.getTable()
+ || sel.isSelected(sub.getTable()))
+ return null;
+
+ // recurse first so we go least->most derived
+ ClassMapping sup = sub.getJoinablePCSuperclassMapping();
+ joins = joinSubclass(sel, base, sup, joins);
+ if (joins == null)
+ joins = sel.newJoins();
+ return sub.joinSuperclass(joins, true);
+ }
+
+ /**
+ * Makes sure all subclasses of the given type are loaded in the JVM.
+ * This is usually done automatically.
+ */
+ public void loadSubclasses(ClassMapping mapping) {
+ Discriminator dsc = mapping.getDiscriminator();
+ if (dsc.getSubclassesLoaded())
+ return;
+
+ // if the subclass list is set, no need to load subs
+ if (mapping.getRepository().getPersistentTypeNames(false,
+ _ctx.getClassLoader()) != null) {
+ dsc.setSubclassesLoaded(true);
+ return;
+ }
+
+ try {
+ dsc.loadSubclasses(this);
+ } catch (ClassNotFoundException cnfe) {
+ throw new StoreException(cnfe);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ /**
+ * Add WHERE conditions to the given select limiting the returned results
+ * to the given mapping type, possibly including subclasses.
+ *
+ * @return true if the mapping was joined down to its base class
+ * in order to add the conditions
+ */
+ public boolean addClassConditions(Select sel, ClassMapping mapping,
+ boolean subs, Joins joins) {
+ loadSubclasses(mapping);
+ if (mapping.getJoinablePCSuperclassMapping() == null
+ && mapping.getJoinablePCSubclassMappings().length == 0)
+ return false;
+
+ // join down to base class where the conditions will be added
+ ClassMapping from = mapping;
+ ClassMapping sup = mapping.getJoinablePCSuperclassMapping();
+ for (; sup != null; from = sup,
+ sup = from.getJoinablePCSuperclassMapping()) {
+ if (from.getTable() != sup.getTable()) {
+ if (joins == null)
+ joins = sel.newJoins();
+ joins = from.joinSuperclass(joins, false);
+ }
+ }
+
+ Discriminator dsc = mapping.getDiscriminator();
+ SQLBuffer buf = dsc.getClassConditions(this, sel, joins, mapping, subs);
+ if (buf != null) {
+ sel.where(buf, joins);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Make the statement a candidate for cancellation.
+ */
+ private void beforeExecuteStatement(Statement stmnt) {
+ _stmnts.add(stmnt);
+ }
+
+ /**
+ * Remove the statement from the cancellable set.
+ */
+ private void afterExecuteStatement(Statement stmnt) {
+ _stmnts.remove(stmnt);
+ }
+
+ /**
+ * Connection returned to client code. Makes sure its wrapped connection
+ * ref count is decremented on finalize.
+ */
+ private static class ClientConnection
+ extends DelegatingConnection {
+
+ private boolean _closed = false;
+
+ public ClientConnection(Connection conn) {
+ super(conn);
+ }
+
+ public void close()
+ throws SQLException {
+ _closed = true;
+ super.close();
+ }
+
+ protected void finalize()
+ throws SQLException {
+ if (!_closed)
+ close();
+ }
+ }
+
+ /**
+ * Connection wrapper that keeps an internal ref count so that it knows
+ * when to really close.
+ */
+ private class RefCountConnection
+ extends DelegatingConnection {
+
+ private boolean _retain = false;
+ private int _refs = 0;
+ private boolean _freed = false;
+
+ public RefCountConnection(Connection conn) {
+ super(conn);
+ }
+
+ public boolean getRetain() {
+ return _retain;
+ }
+
+ public void setRetain(boolean retain) {
+ if (_retain && !retain && _refs <= 0)
+ free();
+ _retain = retain;
+ }
+
+ public void ref() {
+ // don't have to lock; called from connect(), which is locked
+ _refs++;
+ }
+
+ public void close()
+ throws SQLException {
+ // lock at broker level to avoid deadlocks
+ _ctx.lock();
+ try {
+ _refs--;
+ if (_refs <= 0 && !_retain)
+ free();
+ } finally {
+ _ctx.unlock();
+ }
+ }
+
+ public void free() {
+ // ensure that we do not close the underlying connection
+ // multiple times; this could happen if someone (e.g., an
+ // Extent) holds a RefConnection, and then closes it (e.g., in
+ // the finalizer) after the StoreManager has already been closed.
+ if (_freed)
+ return;
+
+ try {
+ getDelegate().close();
+ } catch (SQLException se) {
+ }
+ _freed = true;
+ _conn = null;
+ }
+
+ protected Statement createStatement(boolean wrap)
+ throws SQLException {
+ return new CancelStatement(super.createStatement(false),
+ RefCountConnection.this);
+ }
+
+ protected Statement createStatement(int rsType, int rsConcur,
+ boolean wrap)
+ throws SQLException {
+ return new CancelStatement(super.createStatement(rsType,
+ rsConcur, false), RefCountConnection.this);
+ }
+
+ protected PreparedStatement prepareStatement(String sql, boolean wrap)
+ throws SQLException {
+ return new CancelPreparedStatement(super.prepareStatement
+ (sql, false), RefCountConnection.this);
+ }
+
+ protected PreparedStatement prepareStatement(String sql, int rsType,
+ int rsConcur, boolean wrap)
+ throws SQLException {
+ return new CancelPreparedStatement(super.prepareStatement
+ (sql, rsType, rsConcur, false), RefCountConnection.this);
+ }
+ }
+
+ /**
+ * Statement type that adds and removes itself from the set of active
+ * statements so that it can be canceled.
+ */
+ private class CancelStatement
+ extends DelegatingStatement {
+
+ public CancelStatement(Statement stmnt, Connection conn) {
+ super(stmnt, conn);
+ }
+
+ public int executeUpdate(String sql)
+ throws SQLException {
+ beforeExecuteStatement(this);
+ try {
+ return super.executeUpdate(sql);
+ } finally {
+ afterExecuteStatement(this);
+ }
+ }
+
+ protected ResultSet executeQuery(String sql, boolean wrap)
+ throws SQLException {
+ beforeExecuteStatement(this);
+ try {
+ return super.executeQuery(sql, wrap);
+ } finally {
+ afterExecuteStatement(this);
+ }
+ }
+ }
+
+ /**
+ * Statement type that adds and removes itself from the set of active
+ * statements so that it can be canceled.
+ */
+ private class CancelPreparedStatement
+ extends DelegatingPreparedStatement {
+
+ public CancelPreparedStatement(PreparedStatement stmnt,
+ Connection conn) {
+ super(stmnt, conn);
+ }
+
+ public int executeUpdate()
+ throws SQLException {
+ beforeExecuteStatement(this);
+ try {
+ return super.executeUpdate();
+ } finally {
+ afterExecuteStatement(this);
+ }
+ }
+
+ protected ResultSet executeQuery(boolean wrap)
+ throws SQLException {
+ beforeExecuteStatement(this);
+ try {
+ return super.executeQuery(wrap);
+ } finally {
+ afterExecuteStatement(this);
+ }
+ }
+
+ public int[] executeBatch()
+ throws SQLException {
+ beforeExecuteStatement(this);
+ try {
+ return super.executeBatch();
+ } finally {
+ afterExecuteStatement(this);
+ }
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,636 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.kernel;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.openjpa.event.LifecycleEventManager;
+import org.apache.openjpa.jdbc.kernel.exps.GetColumn;
+import org.apache.openjpa.jdbc.kernel.exps.JDBCExpressionFactory;
+import org.apache.openjpa.jdbc.kernel.exps.JDBCStringContains;
+import org.apache.openjpa.jdbc.kernel.exps.JDBCWildcardMatch;
+import org.apache.openjpa.jdbc.kernel.exps.SQLEmbed;
+import org.apache.openjpa.jdbc.kernel.exps.SQLExpression;
+import org.apache.openjpa.jdbc.kernel.exps.SQLValue;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.SQLExceptions;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.ExpressionStoreQuery;
+import org.apache.openjpa.kernel.OrderingMergedResultObjectProvider;
+import org.apache.openjpa.kernel.exps.ExpressionFactory;
+import org.apache.openjpa.kernel.exps.ExpressionParser;
+import org.apache.openjpa.kernel.exps.FilterListener;
+import org.apache.openjpa.kernel.exps.QueryExpressions;
+import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
+import org.apache.openjpa.lib.rop.RangeResultObjectProvider;
+import org.apache.openjpa.lib.rop.ResultObjectProvider;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.ValueMetaData;
+import org.apache.openjpa.util.UserException;
+import serp.util.Numbers;
+
+/**
+ * JDBC query implementation.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class JDBCStoreQuery
+ extends ExpressionStoreQuery {
+
+ private static final Table INVALID = new Table();
+
+ // add all standard filter and aggregate listeners to these maps
+ private static final Map _listeners = new HashMap();
+
+ static {
+ // deprecated extensions
+ _listeners.put(JDBCStringContains.TAG, new JDBCStringContains());
+ _listeners.put(JDBCWildcardMatch.TAG, new JDBCWildcardMatch());
+ _listeners.put(SQLExpression.TAG, new SQLExpression());
+ _listeners.put(SQLValue.TAG, new SQLValue());
+
+ // jdbc-specific extensions
+ _listeners.put(GetColumn.TAG, new GetColumn());
+ _listeners.put(SQLEmbed.TAG, new SQLEmbed());
+ }
+
+ private final transient JDBCStore _store;
+
+ /**
+ * Constructor. Supply store manager.
+ */
+ public JDBCStoreQuery(JDBCStore store, ExpressionParser parser) {
+ super(parser);
+ _store = store;
+ }
+
+ /**
+ * Return the store.
+ */
+ public JDBCStore getStore() {
+ return _store;
+ }
+
+ public FilterListener getFilterListener(String tag) {
+ return (FilterListener) _listeners.get(tag);
+ }
+
+ public Object newCompilationKey() {
+ JDBCFetchConfiguration fetch = (JDBCFetchConfiguration)
+ ctx.getFetchConfiguration();
+ return Numbers.valueOf(fetch.getJoinSyntax());
+ }
+
+ public boolean supportsDataStoreExecution() {
+ return true;
+ }
+
+ protected ClassMetaData[] getIndependentExpressionCandidates
+ (ClassMetaData meta, boolean subclasses) {
+ if (!subclasses)
+ return new ClassMapping[]{ (ClassMapping) meta };
+ return ((ClassMapping) meta).getIndependentAssignableMappings();
+ }
+
+ protected ExpressionFactory getExpressionFactory(ClassMetaData meta) {
+ return new JDBCExpressionFactory((ClassMapping) meta);
+ }
+
+ protected ResultObjectProvider executeQuery(Executor ex,
+ ClassMetaData base, ClassMetaData[] metas, boolean subclasses,
+ ExpressionFactory[] facts, QueryExpressions[] exps, Object[] params,
+ boolean lrs, long startIdx, long endIdx) {
+ if (metas.length > 1 && exps[0].aggregate)
+ throw new UserException(Localizer.forPackage(JDBCStoreQuery.class).
+ get("mult-mapping-aggregate", Arrays.asList(metas)));
+
+ ClassMapping[] mappings = (ClassMapping[]) metas;
+ JDBCFetchConfiguration fetch = (JDBCFetchConfiguration)
+ ctx.getFetchConfiguration();
+ JDBCFetchState fetchState = (JDBCFetchState) fetch.newFetchState();
+ if (exps[0].fetchPaths != null) {
+ fetch.addFields(Arrays.asList(exps[0].fetchPaths));
+ fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
+ }
+
+ int eager = calculateEagerMode(exps[0], startIdx, endIdx);
+ int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
+ DBDictionary dict = _store.getDBDictionary();
+ long start = (mappings.length == 1 && dict.supportsSelectStartIndex)
+ ? startIdx : 0L;
+ long end = (dict.supportsSelectEndIndex) ? endIdx : Long.MAX_VALUE;
+
+ // add selects with populate WHERE conditions to list
+ List sels = new ArrayList(mappings.length);
+ List selMappings = new ArrayList(mappings.length);
+ BitSet subclassBits = new BitSet();
+ BitSet nextBits = new BitSet();
+ boolean unionable = createWhereSelects(sels, mappings, selMappings,
+ subclasses, subclassBits, nextBits, facts, exps, params, fetchState,
+ subclassMode)
+ && subclassMode == JDBCFetchConfiguration.EAGER_JOIN
+ && start == 0 && end == Long.MAX_VALUE;
+
+ // we might want to use lrs settings if we can't use the range
+ if (sels.size() > 1)
+ start = 0L;
+ lrs = lrs || (fetch.getFetchBatchSize() >= 0
+ && (start != startIdx || end != endIdx));
+
+ ResultObjectProvider[] rops = null;
+ ResultObjectProvider rop = null;
+ if (unionable) {
+ Union union = _store.getSQLFactory().newUnion((Select[])
+ sels.toArray(new Select[sels.size()]));
+ BitSet[] paged = populateUnion(union, mappings, subclasses,
+ facts, exps, params, fetchState, lrs, eager, start, end);
+ union.setLRS(lrs);
+ rop = executeUnion(union, mappings, exps, paged, fetchState);
+ } else {
+ if (sels.size() > 1)
+ rops = new ResultObjectProvider[sels.size()];
+
+ Select sel;
+ BitSet paged;
+ for (int i = 0, idx = 0; i < sels.size(); i++) {
+ sel = (Select) sels.get(i);
+ paged = populateSelect(sel, (ClassMapping) selMappings.get(i),
+ subclassBits.get(i), (JDBCExpressionFactory) facts[idx],
+ exps[idx], params, fetchState, lrs, eager, start, end);
+
+ rop = executeSelect(sel, (ClassMapping) selMappings.get(i),
+ exps[idx], paged, fetchState, start, end);
+ if (rops != null)
+ rops[i] = rop;
+
+ if (nextBits.get(i))
+ idx++;
+ }
+ }
+
+ if (rops != null) {
+ if (exps[0].ascending.length == 0)
+ rop = new MergedResultObjectProvider(rops);
+ else {
+ rop = new OrderingMergedResultObjectProvider(rops,
+ exps[0].ascending, ex, this, params);
+ }
+ }
+
+ // need to fake result range?
+ if ((rops != null && endIdx != Long.MAX_VALUE)
+ || start != startIdx || end != endIdx)
+ rop = new RangeResultObjectProvider(rop, startIdx, endIdx);
+ return rop;
+ }
+
+ /**
+ * Select data for the given union, returning paged fields.
+ */
+ private BitSet[] populateUnion(Union union, final ClassMapping[] mappings,
+ final boolean subclasses, final ExpressionFactory[] facts,
+ final QueryExpressions[] exps, final Object[] params,
+ final JDBCFetchState fetchState, final boolean lrs, final int eager,
+ final long start, final long end) {
+ final BitSet[] paged = (exps[0].projections.length > 0) ? null
+ : new BitSet[mappings.length];
+ union.select(new Union.Selector() {
+ public void select(Select sel, int idx) {
+ BitSet bits = populateSelect(sel, mappings[idx], subclasses,
+ (JDBCExpressionFactory) facts[idx], exps[idx], params,
+ fetchState, lrs, eager, start, end);
+ if (paged != null)
+ paged[idx] = bits;
+ }
+ });
+ return paged;
+ }
+
+ /**
+ * Select data for the given select, returning paged fields.
+ */
+ private BitSet populateSelect(Select sel, ClassMapping mapping,
+ boolean subclasses, JDBCExpressionFactory fact, QueryExpressions exps,
+ Object[] params, JDBCFetchState fetchState, boolean lrs, int eager,
+ long start, long end) {
+ sel.setLRS(lrs);
+ sel.setRange(start, end);
+
+ BitSet paged = null;
+ if (exps.projections.length == 0) {
+ paged = PagingResultObjectProvider.getPagedFields(sel, mapping,
+ _store, fetchState, eager, end - start);
+ if (paged != null)
+ eager = JDBCFetchConfiguration.EAGER_JOIN;
+ }
+
+ fact.select(this, mapping, subclasses, sel, exps, params, fetchState,
+ eager);
+ return paged;
+ }
+
+ /**
+ * Execute the given union.
+ */
+ private ResultObjectProvider executeUnion(Union union,
+ ClassMapping[] mappings, QueryExpressions[] exps, BitSet[] paged,
+ JDBCFetchState fetchState) {
+ if (exps[0].projections.length > 0)
+ return new ProjectionResultObjectProvider(union, _store, fetchState,
+ exps);
+
+ if (paged != null)
+ for (int i = 0; i < paged.length; i++)
+ if (paged[i] != null)
+ return new PagingResultObjectProvider(union, mappings,
+ _store, fetchState, paged, Long.MAX_VALUE);
+
+ return new InstanceResultObjectProvider(union, mappings[0], _store,
+ fetchState);
+ }
+
+ /**
+ * Execute the given select.
+ */
+ private ResultObjectProvider executeSelect(Select sel,
+ ClassMapping mapping, QueryExpressions exps, BitSet paged,
+ JDBCFetchState fetchState, long start, long end) {
+ if (exps.projections.length > 0)
+ return new ProjectionResultObjectProvider(sel, _store, fetchState,
+ exps);
+ if (paged != null)
+ return new PagingResultObjectProvider(sel, mapping, _store,
+ fetchState, paged, end - start);
+ return new InstanceResultObjectProvider(sel, mapping, _store,
+ fetchState);
+ }
+
+ /**
+ * Generate the selects with WHERE conditions needed to execute the query
+ * for the given mappings.
+ */
+ private boolean createWhereSelects(List sels, ClassMapping[] mappings,
+ List selMappings, boolean subclasses, BitSet subclassBits,
+ BitSet nextBits, ExpressionFactory[] facts, QueryExpressions[] exps,
+ Object[] params, JDBCFetchState fetchState, int subclassMode) {
+ Select sel;
+ ClassMapping[] verts;
+ boolean unionable = true;
+ for (int i = 0; i < mappings.length; i++) {
+ // determine vertical mappings to select separately
+ verts = getVerticalMappings(mappings[i], subclasses, exps[i],
+ subclassMode);
+ if (verts.length == 1 && subclasses)
+ subclassBits.set(sels.size());
+
+ // create criteria select and clone for each vert mapping
+ sel = ((JDBCExpressionFactory) facts[i]).evaluate(this, fetchState,
+ exps[i], params);
+ for (int j = 0; j < verts.length; j++) {
+ selMappings.add(verts[j]);
+ if (j == verts.length - 1) {
+ nextBits.set(sels.size());
+ sels.add(sel);
+ } else
+ sels.add(sel.fullClone(1));
+ }
+
+ // turn off unioning if a given independent mapping requires
+ // multiple selects, or if we're using FROM selects
+ if (verts.length > 1 || sel.getFromSelect() != null)
+ unionable = false;
+ }
+ return unionable;
+ }
+
+ /**
+ * Return all the vertical mappings to select separately. Depends on
+ * subclass fetch mode and the type of query.
+ */
+ private ClassMapping[] getVerticalMappings(ClassMapping mapping,
+ boolean subclasses, QueryExpressions exps, int subclassMode) {
+ if (!subclasses || exps.projections.length > 0)
+ return new ClassMapping[]{ mapping };
+
+ if (subclassMode != JDBCFetchConfiguration.EAGER_PARALLEL
+ || !hasVerticalSubclasses(mapping))
+ return new ClassMapping[]{ mapping };
+
+ List subs = new ArrayList(4);
+ addSubclasses(mapping, subs);
+ return (ClassMapping[]) subs.toArray(new ClassMapping[subs.size()]);
+ }
+
+ /**
+ * Recursive helper to add mappings for subclasses to the given list.
+ */
+ private void addSubclasses(ClassMapping mapping, Collection subs) {
+ // possible future optimizations:
+ // - if no fields in meta or its subclasses (and not in an
+ // already-selected table) are in the current fetch
+ // configuration, stop creating new executors
+ // - allow an executor to select a range of subclasses, rather
+ // than just all subclasses / no subclasses; this would
+ // allow us to do just one query per actual vertically-mapped
+ // subclass, rather than one per mapped subclass, as is happening now
+
+ subs.add(mapping);
+ if (!hasVerticalSubclasses(mapping))
+ return;
+
+ // recurse on immediate subclasses
+ ClassMapping[] subMappings = mapping.getJoinablePCSubclassMappings();
+ for (int i = 0; i < subMappings.length; i++)
+ if (subMappings[i].getJoinablePCSuperclassMapping() == mapping)
+ addSubclasses(subMappings[i], subs);
+ }
+
+ /**
+ * Return whether the given class has any vertical subclasses.
+ */
+ private static boolean hasVerticalSubclasses(ClassMapping mapping) {
+ ClassMapping[] subs = mapping.getJoinablePCSubclassMappings();
+ for (int i = 0; i < subs.length; i++)
+ if (subs[i].getStrategy()instanceof VerticalClassStrategy)
+ return true;
+ return false;
+ }
+
+ /**
+ * The eager mode depends on the unique setting and range. If the range
+ * produces 0 results, use eager setting of none. If it produces 1 result
+ * or the query is unique, use an eager setting of single. Otherwise use
+ * an eager mode of multiple.
+ */
+ private int calculateEagerMode(QueryExpressions exps, long startIdx,
+ long endIdx) {
+ if (exps.projections.length > 0 || startIdx >= endIdx)
+ return EagerFetchModes.EAGER_NONE;
+ if (endIdx - startIdx == 1 || ctx.isUnique())
+ return EagerFetchModes.EAGER_JOIN;
+ return EagerFetchModes.EAGER_PARALLEL;
+ }
+
+ protected Number executeDelete(Executor ex, ClassMetaData base,
+ ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
+ QueryExpressions[] exps, Object[] params) {
+ return executeBulkOperation(ex, base, metas, subclasses, facts,
+ exps, params, null);
+ }
+
+ protected Number executeUpdate(Executor ex, ClassMetaData base,
+ ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
+ QueryExpressions[] exps, Object[] params) {
+ return executeBulkOperation(ex, base, metas, subclasses, facts,
+ exps, params, exps[0].updates);
+ }
+
+ private Number executeBulkOperation(Executor ex, ClassMetaData base,
+ ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
+ QueryExpressions[] exps, Object[] params, Map updates) {
+ // we cannot execute a bulk delete statement when have mappings in
+ // multiple tables, so indicate we want to use in-memory with null
+ ClassMapping[] mappings = (ClassMapping[]) metas;
+ boolean isUpdate = updates != null && updates.size() > 0;
+ for (int i = 0; i < mappings.length; i++) {
+ if (!isSingleTableMapping(mappings[i], subclasses) && !isUpdate)
+ return null;
+
+ if (!isUpdate) {
+ // if there are any delete callbacks, we need to
+ // execute in-memory so the callbacks are invoked
+ LifecycleEventManager mgr = ctx.getStoreContext().getBroker().
+ getLifecycleEventManager();
+ if (mgr.hasDeleteListeners(null, mappings[i]))
+ return null;
+ }
+ }
+
+ JDBCFetchConfiguration fetch = (JDBCFetchConfiguration)
+ ctx.getFetchConfiguration();
+ DBDictionary dict = _store.getDBDictionary();
+ int subs = (subclasses) ? Select.SUBS_JOINABLE : Select.SUBS_NONE;
+
+ SQLBuffer[] sql = new SQLBuffer[mappings.length];
+ JDBCExpressionFactory jdbcFactory;
+ Select sel;
+ for (int i = 0; i < mappings.length; i++) {
+ jdbcFactory = (JDBCExpressionFactory) facts[i];
+ JDBCFetchState fetchState = (JDBCFetchState) fetch.newFetchState();
+ sel = jdbcFactory.evaluate(this, fetchState, exps[i], params);
+ jdbcFactory.select(this, mappings[i], subclasses, sel,
+ exps[i], params, fetchState, JDBCFetchConfiguration.EAGER_NONE);
+
+ // specification of the "udpates" map indicates that this is
+ // an update query; otherwise, this is a delete statement
+ // The bulk operation will return null to indicate that the database
+ // does not support the request bulk delete operation; in
+ // this case, we need to perform the query in-memory and
+ // manually delete the instances
+ if (updates == null)
+ sql[i] = dict.toDelete(mappings[i], sel, _store, params);
+ else
+ sql[i] = dict.toUpdate(mappings[i], sel, _store, params,
+ updates);
+
+ if (sql[i] == null)
+ return null;
+ }
+
+ // we need to make sure we have an active store connection
+ ctx.getStoreContext().beginStore();
+
+ Connection conn = _store.getConnection();
+ long count = 0;
+ try {
+ PreparedStatement stmnt;
+ for (int i = 0; i < sql.length; i++) {
+ stmnt = null;
+ try {
+ stmnt = sql[i].prepareStatement(conn);
+ count += stmnt.executeUpdate();
+ } finally {
+ if (stmnt != null)
+ try {
+ stmnt.close();
+ } catch (SQLException se) {
+ }
+ }
+ }
+ }
+ catch (SQLException se) {
+ throw SQLExceptions.getStore(se, ctx, _store.getDBDictionary());
+ } finally {
+ try {
+ conn.close();
+ } catch (SQLException se) {
+ }
+ }
+ return Numbers.valueOf(count);
+ }
+
+ /**
+ * Whether the given mapping occupies only one table.
+ */
+ private boolean isSingleTableMapping(ClassMapping mapping,
+ boolean subclasses) {
+ ClassMapping root = mapping;
+ while (root.getJoinablePCSuperclassMapping() != null)
+ root = root.getJoinablePCSuperclassMapping();
+ if (hasVerticalSubclasses(root))
+ return false;
+
+ // we cannot execute a bulk delete if any of the
+ // field mappings for the candidates have columns
+ // in any other table, since bulk deleting just from the
+ // class will leave dangling relations; we might be able
+ // to issue bulk deletes separately for the joins (possibly
+ // using a temporary table to select the primary keys for
+ // all the related tables and then issing a delete against those
+ // keys), but that logic is not currently implemented
+ Table table = getTable(mapping.getFieldMappings(), null);
+ if (table == INVALID)
+ return false;
+
+ if (subclasses) {
+ // if we are including subclasses, we also need to gather
+ // all the mappings for all known subclasses
+ ClassMapping[] subs = mapping.getJoinablePCSubclassMappings();
+ for (int i = 0; subs != null && i < subs.length; i++) {
+ table = getTable(subs[i].getDefinedFieldMappings(), table);
+ if (table == INVALID)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the single table for the given fields, or INVALID if they
+ * use multiple tables.
+ */
+ private Table getTable(FieldMapping[] fields, Table table) {
+ for (int i = 0; i < fields.length; i++) {
+ table = getTable(fields[i], table);
+ if (table == INVALID)
+ break;
+ }
+ return table;
+ }
+
+ /**
+ * Return the table for the field if the given table hasn't been set
+ * yet, or if the tables match. If the field uses a different table,
+ * returns INVALID. Also returns INVALID if field is dependent.
+ */
+ private Table getTable(FieldMapping fm, Table table) {
+ if (fm.getCascadeDelete() != ValueMetaData.CASCADE_NONE)
+ return INVALID;
+
+ Column[] columns = fm.getColumns();
+ for (int i = 0; columns != null && i < columns.length; i++) {
+ if (table == null)
+ table = columns[i].getTable();
+ else if (table != columns[i].getTable())
+ return INVALID;
+ }
+ return table;
+ }
+
+ protected Number executeUpdate(ClassMetaData base, ClassMetaData[] metas,
+ boolean subclasses, ExpressionFactory[] facts,
+ QueryExpressions[] parsed, Object[] params) {
+ return null;
+ }
+
+ protected String[] getDataStoreActions(ClassMetaData base,
+ ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
+ QueryExpressions[] exps, Object[] params, long startIdx, long endIdx) {
+ ClassMapping[] mappings = (ClassMapping[]) metas;
+ JDBCFetchConfiguration fetch = (JDBCFetchConfiguration)
+ ctx.getFetchConfiguration();
+ JDBCFetchState fetchState = (JDBCFetchState) fetch.newFetchState();
+ if (exps[0].fetchPaths != null) {
+ fetch.addFields(Arrays.asList(exps[0].fetchPaths));
+ fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
+ }
+
+ int eager = calculateEagerMode(exps[0], startIdx, endIdx);
+ eager = Math.min(eager, JDBCFetchConfiguration.EAGER_JOIN);
+ int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
+ DBDictionary dict = _store.getDBDictionary();
+ long start = (mappings.length == 1 && dict.supportsSelectStartIndex)
+ ? startIdx : 0L;
+ long end = (dict.supportsSelectEndIndex) ? endIdx : Long.MAX_VALUE;
+
+ // add selects with populate WHERE conditions to list
+ List sels = new ArrayList(mappings.length);
+ List selMappings = new ArrayList(mappings.length);
+ BitSet subclassBits = new BitSet();
+ BitSet nextBits = new BitSet();
+ boolean unionable = createWhereSelects(sels, mappings, selMappings,
+ subclasses, subclassBits, nextBits, facts, exps, params, fetchState,
+ subclassMode)
+ && subclassMode == JDBCFetchConfiguration.EAGER_JOIN;
+ if (sels.size() > 1)
+ start = 0L;
+
+ if (unionable) {
+ Union union = _store.getSQLFactory().newUnion((Select[])
+ sels.toArray(new Select[sels.size()]));
+ populateUnion(union, mappings, subclasses, facts, exps, params,
+ fetchState, false, eager, start, end);
+ if (union.isUnion())
+ return new String[]{ union.toSelect(false, fetch).
+ getSQL(true) };
+ sels = Arrays.asList(union.getSelects());
+ } else {
+ Select sel;
+ for (int i = 0, idx = 0; i < sels.size(); i++) {
+ sel = (Select) sels.get(i);
+ populateSelect(sel, (ClassMapping) selMappings.get(i),
+ subclassBits.get(i), (JDBCExpressionFactory) facts[idx],
+ exps[idx], params, fetchState, false, eager, start, end);
+ if (nextBits.get(i))
+ idx++;
+ }
+ }
+
+ String[] sql = new String[sels.size()];
+ for (int i = 0; i < sels.size(); i++)
+ sql[i] = ((Select) sels.get(i)).toSelect(false, fetch).
+ getSQL(true);
+ return sql;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/LRSSizes.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/LRSSizes.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/LRSSizes.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/LRSSizes.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.jdbc.kernel;
+
+import java.sql.ResultSet;
+
+/**
+ * Ways of calculating the size of large result sets.
+ *
+ * @author Abe White
+ */
+public interface LRSSizes {
+
+ /**
+ * Mode for returning {@link Integer#MAX_VALUE} for the size of
+ * large result sets.
+ */
+ public static final int SIZE_UNKNOWN = 0;
+
+ /**
+ * Mode for using {@link ResultSet#last} to calcualte the size of
+ * large result sets.
+ */
+ public static final int SIZE_LAST = 1;
+
+ /**
+ * Mode for using a query to calculate the size of large result sets.
+ */
+ public static final int SIZE_QUERY = 2;
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/LRSSizes.java
------------------------------------------------------------------------------
svn:executable = *