Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » kernel » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.jdbc.kernel;
   20   
   21   import java.sql.Connection;
   22   import java.sql.PreparedStatement;
   23   import java.sql.ResultSet;
   24   import java.sql.SQLException;
   25   import java.sql.Statement;
   26   import java.util.ArrayList;
   27   import java.util.BitSet;
   28   import java.util.Collection;
   29   import java.util.Collections;
   30   import java.util.HashSet;
   31   import java.util.Iterator;
   32   import java.util.Set;
   33   import javax.sql.DataSource;
   34   
   35   import org.apache.openjpa.event.OrphanedKeyAction;
   36   import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
   37   import org.apache.openjpa.jdbc.meta.ClassMapping;
   38   import org.apache.openjpa.jdbc.meta.Discriminator;
   39   import org.apache.openjpa.jdbc.meta.FieldMapping;
   40   import org.apache.openjpa.jdbc.meta.ValueMapping;
   41   import org.apache.openjpa.jdbc.sql.DBDictionary;
   42   import org.apache.openjpa.jdbc.sql.JoinSyntaxes;
   43   import org.apache.openjpa.jdbc.sql.Joins;
   44   import org.apache.openjpa.jdbc.sql.Result;
   45   import org.apache.openjpa.jdbc.sql.SQLExceptions;
   46   import org.apache.openjpa.jdbc.sql.SQLFactory;
   47   import org.apache.openjpa.jdbc.sql.Select;
   48   import org.apache.openjpa.jdbc.sql.SelectExecutor;
   49   import org.apache.openjpa.jdbc.sql.Union;
   50   import org.apache.openjpa.kernel.FetchConfiguration;
   51   import org.apache.openjpa.kernel.LockManager;
   52   import org.apache.openjpa.kernel.OpenJPAStateManager;
   53   import org.apache.openjpa.kernel.PCState;
   54   import org.apache.openjpa.kernel.QueryLanguages;
   55   import org.apache.openjpa.kernel.Seq;
   56   import org.apache.openjpa.kernel.StoreContext;
   57   import org.apache.openjpa.kernel.StoreManager;
   58   import org.apache.openjpa.kernel.StoreQuery;
   59   import org.apache.openjpa.kernel.exps.ExpressionParser;
   60   import org.apache.openjpa.lib.jdbc.DelegatingConnection;
   61   import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
   62   import org.apache.openjpa.lib.jdbc.DelegatingStatement;
   63   import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
   64   import org.apache.openjpa.lib.rop.ResultObjectProvider;
   65   import org.apache.openjpa.lib.util.Localizer;
   66   import org.apache.openjpa.meta.ClassMetaData;
   67   import org.apache.openjpa.meta.FieldMetaData;
   68   import org.apache.openjpa.meta.JavaTypes;
   69   import org.apache.openjpa.meta.ValueStrategies;
   70   import org.apache.openjpa.util.ApplicationIds;
   71   import org.apache.openjpa.util.Id;
   72   import org.apache.openjpa.util.ImplHelper;
   73   import org.apache.openjpa.util.InvalidStateException;
   74   import org.apache.openjpa.util.OpenJPAId;
   75   import org.apache.openjpa.util.StoreException;
   76   import org.apache.openjpa.util.UserException;
   77   
   78   /**
   79    * StoreManager plugin that uses JDBC to store persistent data in a
   80    * relational data store.
   81    *
   82    * @author Abe White
   83    * @nojavadoc
   84    */
   85   public class JDBCStoreManager 
   86       implements StoreManager, JDBCStore {
   87   
   88       private static final Localizer _loc = Localizer.forPackage
   89           (JDBCStoreManager.class);
   90   
   91       private StoreContext _ctx = null;
   92       private JDBCConfiguration _conf = null;
   93       private DBDictionary _dict = null;
   94       private SQLFactory _sql = null;
   95       private JDBCLockManager _lm = null;
   96       private DataSource _ds = null;
   97       private RefCountConnection _conn = null;
   98       private boolean _active = false;
   99   
  100       // track the pending statements so we can cancel them
  101       private Set _stmnts = Collections.synchronizedSet(new HashSet());
  102   
  103       public StoreContext getContext() {
  104           return _ctx;
  105       }
  106   
  107       public void setContext(StoreContext ctx) {
  108           setContext(ctx, (JDBCConfiguration) ctx.getConfiguration());
  109       }
  110       
  111       public void setContext(StoreContext ctx, JDBCConfiguration conf) {
  112           _ctx = ctx;
  113           _conf = conf;
  114           _dict = _conf.getDBDictionaryInstance();
  115           _sql = _conf.getSQLFactoryInstance();
  116   
  117           LockManager lm = ctx.getLockManager();
  118           if (lm instanceof JDBCLockManager)
  119               _lm = (JDBCLockManager) lm;
  120   
  121           if (!ctx.isManaged() && _conf.isConnectionFactoryModeManaged())
  122               _ds = _conf.getDataSource2(ctx);
  123           else
  124               _ds = _conf.getDataSource(ctx);
  125   
  126           if (_conf.getUpdateManagerInstance().orderDirty())
  127               ctx.setOrderDirtyObjects(true);
  128       }
  129   
  130       public JDBCConfiguration getConfiguration() {
  131           return _conf;
  132       }
  133   
  134       public DBDictionary getDBDictionary() {
  135           return _dict;
  136       }
  137   
  138       public SQLFactory getSQLFactory() {
  139           return _sql;
  140       }
  141   
  142       public JDBCLockManager getLockManager() {
  143           return _lm;
  144       }
  145   
  146       public JDBCFetchConfiguration getFetchConfiguration() {
  147           return (JDBCFetchConfiguration) _ctx.getFetchConfiguration();
  148       }
  149   
  150       public void beginOptimistic() {
  151       }
  152   
  153       public void rollbackOptimistic() {
  154       }
  155   
  156       public void begin() {
  157           _active = true;
  158           try {
  159               if ((!_ctx.isManaged() || !_conf.isConnectionFactoryModeManaged())
  160                   && _conn.getAutoCommit())
  161                   _conn.setAutoCommit(false);
  162           } catch (SQLException se) {
  163               _active = false;
  164               throw SQLExceptions.getStore(se, _dict);
  165           }
  166       }
  167   
  168       public void commit() {
  169           try {
  170               if (!_ctx.isManaged() || !_conf.isConnectionFactoryModeManaged())
  171                   _conn.commit();
  172           } catch (SQLException se) {
  173               try {
  174                   _conn.rollback();
  175               } catch (SQLException se2) {
  176               }
  177               throw SQLExceptions.getStore(se, _dict);
  178           } finally {
  179               _active = false;
  180           }
  181       }
  182   
  183       public void rollback() {
  184           // already rolled back ourselves?
  185           if (!_active)
  186               return;
  187   
  188           try {
  189               if (_conn != null
  190                   && (!_ctx.isManaged() || !_conf
  191                       .isConnectionFactoryModeManaged()))
  192                   _conn.rollback();
  193           } catch (SQLException se) {
  194               throw SQLExceptions.getStore(se, _dict);
  195           } finally {
  196               _active = false;
  197           }
  198       }
  199   
  200       public void retainConnection() {
  201           connect(false);
  202           _conn.setRetain(true);
  203       }
  204   
  205       public void releaseConnection() {
  206           if (_conn != null)
  207               _conn.setRetain(false);
  208       }
  209   
  210       public Object getClientConnection() {
  211           return new ClientConnection(getConnection());
  212       }
  213   
  214       public Connection getConnection() {
  215           connect(true);
  216           return _conn;
  217       }
  218       
  219       protected DataSource getDataSource() {
  220       	return _ds;
  221       }
  222   
  223       public boolean exists(OpenJPAStateManager sm, Object context) {
  224           // add where conditions on base class to avoid joins if subclass
  225           // doesn't use oid as identifier
  226           ClassMapping mapping = (ClassMapping) sm.getMetaData();
  227           return exists(mapping, sm.getObjectId(), context);
  228       }
  229   
  230       private boolean exists(ClassMapping mapping, Object oid, Object context) {
  231           // add where conditions on base class to avoid joins if subclass
  232           // doesn't use oid as identifier
  233           Select sel = _sql.newSelect();
  234           while (mapping.getJoinablePCSuperclassMapping() != null)
  235               mapping = mapping.getJoinablePCSuperclassMapping();
  236   
  237           sel.wherePrimaryKey(oid, mapping, this);
  238           try {
  239               return sel.getCount(this) != 0;
  240           } catch (SQLException se) {
  241               throw SQLExceptions.getStore(se, _dict);
  242           }
  243       }
  244   
  245       public boolean syncVersion(OpenJPAStateManager sm, Object context) {
  246           ClassMapping mapping = (ClassMapping) sm.getMetaData();
  247           try {
  248               return mapping.getVersion().checkVersion(sm, this, true);
  249           } catch (SQLException se) {
  250               throw SQLExceptions.getStore(se, _dict);
  251           }
  252       }
  253   
  254       public int compareVersion(OpenJPAStateManager state, Object v1, Object v2) {
  255           ClassMapping mapping = (ClassMapping) state.getMetaData();
  256           return mapping.getVersion().compareVersion(v1, v2);
  257       }
  258   
  259       public boolean initialize(OpenJPAStateManager sm, PCState state,
  260           FetchConfiguration fetch, Object context) {
  261           ConnectionInfo info = (ConnectionInfo) context;
  262           try {
  263               return initializeState(sm, state, (JDBCFetchConfiguration) fetch, 
  264                   info);
  265           } catch (ClassNotFoundException cnfe) {
  266               throw new UserException(cnfe);
  267           } catch (SQLException se) {
  268               throw SQLExceptions.getStore(se, _dict);
  269           }
  270       }
  271   
  272       /**
  273        * Initialize a newly-loaded instance.
  274        */
  275       protected boolean initializeState(OpenJPAStateManager sm, PCState state,
  276           JDBCFetchConfiguration fetch, ConnectionInfo info)
  277           throws ClassNotFoundException, SQLException {
  278           Object oid = sm.getObjectId();
  279           ClassMapping mapping = (ClassMapping) sm.getMetaData();
  280           Result res = null;
  281           try {
  282               if (info != null && info.result != null) {
  283                   res = info.result;
  284                   info.sm = sm;
  285                   if (info.mapping == null)
  286                       info.mapping = mapping;
  287                   mapping = info.mapping;
  288               } else if (oid instanceof OpenJPAId
  289                   && !((OpenJPAId) oid).hasSubclasses()) {
  290                   Boolean custom = customLoad(sm, mapping, state, fetch);
  291                   if (custom != null)
  292                       return custom.booleanValue();
  293                   res = getInitializeStateResult(sm, mapping, fetch,
  294                       Select.SUBS_EXACT);
  295                   if (res == null && !selectPrimaryKey(sm, mapping, fetch))
  296                       return false;
  297                   if (isEmptyResult(res))
  298                       return false;
  299               } else {
  300                   ClassMapping[] mappings = mapping.
  301                       getIndependentAssignableMappings();
  302                   if (mappings.length == 1) {
  303                       mapping = mappings[0];
  304                       Boolean custom = customLoad(sm, mapping, state, fetch);
  305                       if (custom != null)
  306                           return custom.booleanValue();
  307                       res = getInitializeStateResult(sm, mapping, fetch,
  308                           Select.SUBS_ANY_JOINABLE);
  309                       if (res == null && !selectPrimaryKey(sm, mapping, fetch))
  310                           return false;
  311                   } else
  312                       res = getInitializeStateUnionResult(sm, mapping, mappings,
  313                           fetch);
  314                   if (isEmptyResult(res))
  315                       return false;
  316               }
  317   
  318               // figure out what type of object this is; the state manager
  319               // only guarantees to provide a base class
  320               Class type;
  321               if ((type = getType(res, mapping)) == null) {
  322                   if (res.getBaseMapping() != null)
  323                       mapping = res.getBaseMapping();
  324                   res.startDataRequest(mapping.getDiscriminator());
  325                   try {
  326                       type = mapping.getDiscriminator().getClass(this, mapping,
  327                           res);
  328                   } finally {
  329                       res.endDataRequest();
  330                   }
  331               }
  332   
  333               // initialize the state manager; this may change the mapping
  334               // and the object id instance if the type as determined
  335               // from the indicator is a subclass of expected type
  336               sm.initialize(type, state);
  337   
  338               // load the selected mappings into the given state manager
  339               if (res != null) {
  340                   // re-get the mapping in case the instance was a subclass
  341                   mapping = (ClassMapping) sm.getMetaData();
  342                   load(mapping, sm, fetch, res);
  343                   getVersion(mapping, sm, res);
  344               }
  345               return true;
  346           } finally {
  347               if (res != null && (info == null || res != info.result))
  348                   res.close();
  349           }
  350       }
  351       
  352       /**
  353        * This method is to provide override for non-JDBC or JDBC-like 
  354        * implementation of getting version from the result set.
  355        */
  356       protected void getVersion(ClassMapping mapping, OpenJPAStateManager sm,
  357           Result res) throws SQLException {
  358           mapping.getVersion().afterLoad(sm, this);
  359       }
  360       
  361       /**
  362        * This method is to provide override for non-JDBC or JDBC-like 
  363        * implementation of checking whether the result set is empty or not.
  364        */
  365       protected boolean isEmptyResult(Result res) throws SQLException {
  366           if (res != null && !res.next())
  367               return true;
  368           return false;
  369       }
  370       
  371       /**
  372        * This method is to provide override for non-JDBC or JDBC-like 
  373        * implementation of getting type from the result set.
  374        */
  375       protected Class getType(Result res, ClassMapping mapping){
  376           if (res == null)
  377               return mapping.getDescribedType();
  378           return null;
  379       }
  380   
  381       /**
  382        * Allow the mapping to custom load data. Return null if the mapping
  383        * does not use custom loading.
  384        */
  385       private Boolean customLoad(OpenJPAStateManager sm, ClassMapping mapping,
  386           PCState state, JDBCFetchConfiguration fetch)
  387           throws ClassNotFoundException, SQLException {
  388           // check to see if the mapping takes care of initialization
  389           if (!mapping.customLoad(sm, this, state, fetch))
  390               return null;
  391           if (sm.getManagedInstance() != null) {
  392               mapping.getVersion().afterLoad(sm, this);
  393               return Boolean.TRUE;
  394           }
  395           return Boolean.FALSE;
  396       }
  397   
  398       /**
  399        * Select the data for the given instance and return the result. Return
  400        * null if there is no data in the current fetch groups to select.
  401        */
  402       private Result getInitializeStateResult(OpenJPAStateManager sm,
  403           ClassMapping mapping, JDBCFetchConfiguration fetch, int subs)
  404           throws SQLException {
  405           Select sel = _sql.newSelect();
  406           if (!select(sel, mapping, subs, sm, null, fetch,
  407               JDBCFetchConfiguration.EAGER_JOIN, true, false))
  408               return null;
  409           sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
  410           sel.setExpectedResultCount(1, false);
  411           return sel.execute(this, fetch);
  412       }
  413   
  414       /**
  415        * Select a union of the data for the given instance from possible concrete
  416        * mappings and return the result.
  417        */
  418       private Result getInitializeStateUnionResult(final OpenJPAStateManager sm,
  419           ClassMapping mapping, final ClassMapping[] mappings,
  420           final JDBCFetchConfiguration fetch) throws SQLException {
  421           final JDBCStoreManager store = this;
  422           final int eager = Math.min(fetch.getEagerFetchMode(),
  423               JDBCFetchConfiguration.EAGER_JOIN);
  424   
  425           Union union = _sql.newUnion(mappings.length);
  426           union.setExpectedResultCount(1, false);
  427           if (fetch.getSubclassFetchMode(mapping) != fetch.EAGER_JOIN)
  428               union.abortUnion();
  429           union.select(new Union.Selector() {
  430               public void select(Select sel, int i) {
  431                   sel.select(mappings[i], Select.SUBS_ANY_JOINABLE, store, fetch,
  432                       eager);
  433                   sel.wherePrimaryKey(sm.getObjectId(), mappings[i], store);
  434               }
  435           });
  436           return union.execute(this, fetch);
  437       }
  438   
  439       /**
  440        * Select primary key data to make sure the given instance exists, locking
  441        * if needed.
  442        */
  443       private boolean selectPrimaryKey(OpenJPAStateManager sm,
  444           ClassMapping mapping, JDBCFetchConfiguration fetch)
  445           throws SQLException {
  446           // select pks from base class record to ensure it exists and lock
  447           // it if needed
  448           ClassMapping base = mapping;
  449           while (base.getJoinablePCSuperclassMapping() != null)
  450               base = base.getJoinablePCSuperclassMapping();
  451   
  452           Select sel = _sql.newSelect();
  453           sel.select(base.getPrimaryKeyColumns());
  454           sel.wherePrimaryKey(sm.getObjectId(), base, this);
  455           Result exists = sel.execute(this, fetch);
  456           try {
  457               if (isEmptyResult(exists))
  458                   return false;
  459   
  460               // record locked?
  461               if (_active && _lm != null && exists.isLocking())
  462                   _lm.loadedForUpdate(sm);
  463               return true;
  464           } finally {
  465               exists.close();
  466           }
  467       }
  468   
  469       public boolean load(OpenJPAStateManager sm, BitSet fields,
  470           FetchConfiguration fetch, int lockLevel, Object context) {
  471           JDBCFetchConfiguration jfetch = (JDBCFetchConfiguration) fetch;
  472   
  473           // get a connection, or reuse current one
  474           ConnectionInfo info = (ConnectionInfo) context;
  475           Result res = null;
  476           if (info != null) {
  477               // if initialize() fails to load required fields, then this method
  478               // is called; make sure not to try to use the given result if it's
  479               // the same one we just failed to completely initialize() with
  480               if (info.sm != sm)
  481                   res = info.result;
  482               info.sm = null;
  483           }
  484           try {
  485               // if there's an existing result, load all we can from it
  486               ClassMapping mapping = (ClassMapping) sm.getMetaData();
  487               if (res != null) {
  488                   load(mapping, sm, jfetch, res);
  489                   removeLoadedFields(sm, fields);
  490               }
  491   
  492               // if the instance is hollow and there's a customized
  493               // get by id method, use it
  494               if (sm.getLoaded().length() == 0 
  495                   && mapping.customLoad(sm, this, null, jfetch))
  496                   removeLoadedFields(sm, fields);
  497   
  498               //### select is kind of a big object, and in some cases we don't
  499               //### use it... would it be worth it to have a small shell select
  500               //### object that only creates a real select when actually used?
  501   
  502               Select sel = _sql.newSelect();
  503               if (select(sel, mapping, Select.SUBS_EXACT, sm, fields, jfetch,
  504                   EagerFetchModes.EAGER_JOIN, true, false)) {
  505                   sel.wherePrimaryKey(sm.getObjectId(), mapping, this);
  506                   res = sel.execute(this, jfetch, lockLevel);
  507                   try {
  508                    	if (isEmptyResult(res))
  509                           return false;
  510                       load(mapping, sm, jfetch, res);
  511                   } finally {
  512                       res.close();
  513                   }
  514               }
  515   
  516               // now allow the fields to load themselves individually too
  517               FieldMapping[] fms = mapping.getFieldMappings();
  518               for (int i = 0; i < fms.length; i++)
  519                   if (fields.get(i) && !sm.getLoaded().get(i))
  520                       fms[i].load(sm, this, jfetch.traverseJDBC(fms[i]));
  521               mapping.getVersion().afterLoad(sm, this);
  522               return true;
  523           } catch (ClassNotFoundException cnfe) {
  524               throw new StoreException(cnfe);
  525           } catch (SQLException se) {
  526               throw SQLExceptions.getStore(se, _dict);
  527           }
  528       }
  529   
  530       /**
  531        * Return a list formed by removing all loaded fields from the given one.
  532        */
  533       private void removeLoadedFields(OpenJPAStateManager sm, BitSet fields) {
  534           for (int i = 0, len = fields.length(); i < len; i++)
  535               if (fields.get(i) && sm.getLoaded().get(i))
  536                   fields.clear(i);
  537       }
  538   
  539       public Collection loadAll(Collection sms, PCState state, int load,
  540           FetchConfiguration fetch, Object context) {
  541           return ImplHelper.loadAll(sms, this, state, load, fetch, context);
  542       }
  543   
  544       public void beforeStateChange(OpenJPAStateManager sm, PCState fromState,
  545           PCState toState) {
  546       }
  547   
  548       public Collection flush(Collection sms) {
  549           return _conf.getUpdateManagerInstance().flush(sms, this);
  550       }
  551   
  552       public boolean cancelAll() {
  553           // note that this method does not lock the context, since
  554           // we want to allow a different thread to be able to cancel the
  555           // outstanding statement on a different context
  556   
  557           Collection stmnts;
  558           synchronized (_stmnts) {
  559               if (_stmnts.isEmpty())
  560                   return false;
  561               stmnts = new ArrayList(_stmnts);
  562           }
  563   
  564           try {
  565               for (Iterator itr = stmnts.iterator(); itr.hasNext();)
  566                   ((Statement) itr.next()).cancel();
  567               return true;
  568           } catch (SQLException se) {
  569               throw SQLExceptions.getStore(se, _dict);
  570           }
  571       }
  572   
  573       public boolean assignObjectId(OpenJPAStateManager sm, boolean preFlush) {
  574           ClassMetaData meta = sm.getMetaData();
  575           if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
  576               return ApplicationIds.assign(sm, this, preFlush);
  577   
  578           // datastore identity
  579           Object val = ImplHelper.generateIdentityValue(_ctx, meta,
  580               JavaTypes.LONG);
  581           if (val == null && meta.getIdentityStrategy() != ValueStrategies.NATIVE)
  582               return false;
  583           if (val == null)
  584               val = getDataStoreIdSequence(meta).next(_ctx, meta);
  585           sm.setObjectId(newDataStoreId(val, meta));
  586           return true;
  587       }
  588   
  589       public boolean assignField(OpenJPAStateManager sm, int field,
  590           boolean preFlush) {
  591           FieldMetaData fmd = sm.getMetaData().getField(field);
  592           Object val = ImplHelper.generateFieldValue(_ctx, fmd);
  593           if (val == null)
  594               return false;
  595           sm.store(field, val);
  596           return true;
  597       }
  598   
  599       public Class getManagedType(Object oid) {
  600           if (oid instanceof Id)
  601               return ((Id) oid).getType();
  602           return null;
  603       }
  604   
  605       public Class getDataStoreIdType(ClassMetaData meta) {
  606           return Id.class;
  607       }
  608   
  609       public Object copyDataStoreId(Object oid, ClassMetaData meta) {
  610           Id id = (Id) oid;
  611           return new Id(meta.getDescribedType(), id.getId(), id.hasSubclasses());
  612       }
  613   
  614       public Object newDataStoreId(Object val, ClassMetaData meta) {
  615           return Id.newInstance(meta.getDescribedType(), val);
  616       }
  617   
  618       public Id newDataStoreId(long id, ClassMapping mapping, boolean subs) {
  619           return new Id(mapping.getDescribedType(), id, subs);
  620       }
  621   
  622       public ResultObjectProvider executeExtent(ClassMetaData meta,
  623           final boolean subclasses, FetchConfiguration fetch) {
  624           ClassMapping mapping = (ClassMapping) meta;
  625           final ClassMapping[] mappings;
  626           if (subclasses)
  627               mappings = mapping.getIndependentAssignableMappings();
  628           else
  629               mappings = new ClassMapping[] { mapping };
  630   
  631           ResultObjectProvider[] rops = null;
  632           final JDBCFetchConfiguration jfetch = (JDBCFetchConfiguration) fetch;
  633           if (jfetch.getSubclassFetchMode(mapping) != jfetch.EAGER_JOIN)
  634               rops = new ResultObjectProvider[mappings.length];
  635   
  636           try {
  637               // check for custom loads
  638               ResultObjectProvider rop;
  639               for (int i = 0; i < mappings.length; i++) {
  640                   rop = mappings[i].customLoad(this, subclasses, jfetch, 0,
  641                       Long.MAX_VALUE);
  642                   if (rop != null) {
  643                       if (rops == null)
  644                           rops = new ResultObjectProvider[mappings.length];
  645                       rops[i] = rop;
  646                   }
  647               }
  648   
  649               // if we're selecting independent mappings separately or have
  650               // custom loads, do individual selects for each class
  651               rop = null;
  652               if (rops != null) {
  653                   for (int i = 0; i < mappings.length; i++) {
  654                       if (rops[i] != null)
  655                           continue;
  656   
  657                       Select sel = _sql.newSelect();
  658                       sel.setLRS(true);
  659                       BitSet paged = selectExtent(sel, mappings[i], jfetch,
  660                           subclasses);
  661                       if (paged == null)
  662                           rops[i] = new InstanceResultObjectProvider(sel,
  663                               mappings[i], this, jfetch);
  664                       else
  665                           rops[i] = new PagingResultObjectProvider(sel,
  666                               mappings[i], this, jfetch, paged, Long.MAX_VALUE);
  667                   }
  668                   if (rops.length == 1)
  669                       return rops[0];
  670                   return new MergedResultObjectProvider(rops);
  671               }
  672   
  673               // perform a union on all independent classes
  674               Union union = _sql.newUnion(mappings.length);
  675               union.setLRS(true);
  676               final BitSet[] paged = new BitSet[mappings.length];
  677               union.select(new Union.Selector() {
  678                   public void select(Select sel, int idx) {
  679                       paged[idx] = selectExtent(sel, mappings[idx], jfetch,
  680                           subclasses);
  681                   }
  682               });
  683   
  684               // using paging rop if any union element has paged fields
  685               for (int i = 0; i < paged.length; i++) {
  686                   if (paged[i] != null)
  687                       return new PagingResultObjectProvider(union, mappings,
  688                           JDBCStoreManager.this, jfetch, paged, Long.MAX_VALUE);
  689               }
  690               return new InstanceResultObjectProvider(union, mappings[0], this,
  691                   jfetch);
  692           } catch (SQLException se) {
  693               throw SQLExceptions.getStore(se, _dict);
  694           }
  695       }
  696   
  697       /**
  698        * Select the given mapping for use in an extent, returning paged fields.
  699        */
  700       private BitSet selectExtent(Select sel, ClassMapping mapping,
  701           JDBCFetchConfiguration fetch, boolean subclasses) {
  702           int subs = (subclasses) ? Select.SUBS_JOINABLE : Select.SUBS_NONE;
  703           // decide between paging and standard iteration
  704           BitSet paged = PagingResultObjectProvider.getPagedFields(sel, mapping,
  705               this, fetch, JDBCFetchConfiguration.EAGER_PARALLEL,
  706               Long.MAX_VALUE);
  707           if (paged == null)
  708               sel.selectIdentifier(mapping, subs, this, fetch,
  709                   JDBCFetchConfiguration.EAGER_PARALLEL);
  710           else
  711               sel.selectIdentifier(mapping, subs, this, fetch,
  712                   JDBCFetchConfiguration.EAGER_JOIN);
  713           return paged;
  714       }
  715   
  716       public StoreQuery newQuery(String language) {
  717           ExpressionParser ep = QueryLanguages.parserForLanguage(language);
  718           if (ep != null)
  719               return new JDBCStoreQuery(this, ep);
  720           if (QueryLanguages.LANG_SQL.equals(language))
  721               return new SQLStoreQuery(this);
  722           return null;
  723       }
  724   
  725       public FetchConfiguration newFetchConfiguration() {
  726           return new JDBCFetchConfigurationImpl();
  727       }
  728   
  729       public Seq getDataStoreIdSequence(ClassMetaData meta) {
  730           if (meta.getIdentityStrategy() == ValueStrategies.NATIVE
  731               || meta.getIdentityStrategy() == ValueStrategies.NONE)
  732               return _conf.getSequenceInstance();
  733           return null;
  734       }
  735   
  736       public Seq getValueSequence(FieldMetaData fmd) {
  737           return null;
  738       }
  739   
  740       public void close() {
  741           if (_conn != null)
  742               _conn.free();
  743       }
  744   
  745       /////////////
  746       // Utilities
  747       /////////////
  748   
  749       /**
  750        * Connect to the db.
  751        */
  752       private void connect(boolean ref) {
  753           _ctx.lock();
  754           try {
  755               // connect if the connection is currently null, or if
  756               // the connection has been closed out from under us
  757               if (_conn == null)
  758                   _conn = connectInternal();
  759               if (ref)
  760                   _conn.ref();
  761           } catch (SQLException se) {
  762               throw SQLExceptions.getStore(se, _dict);
  763           } finally {
  764               _ctx.unlock();
  765           }
  766       }
  767   
  768       /**
  769        * Connect to the database. This method is separated out so that it
  770        * can be overridden.
  771        */
  772       protected RefCountConnection connectInternal() throws SQLException {
  773           return new RefCountConnection(_ds.getConnection());
  774       }
  775   
  776       /**
  777        * Find the object with the given oid.
  778        */
  779       public Object find(Object oid, ValueMapping vm, 
  780           JDBCFetchConfiguration fetch) {
  781           if (oid == null)
  782               return null;
  783           Object pc = _ctx.find(oid, fetch, null, null, 0);
  784           if (pc == null && vm != null) {
  785               OrphanedKeyAction action = _conf.getOrphanedKeyActionInstance();
  786               pc = action.orphan(oid, null, vm);
  787           }
  788           return pc;
  789       }
  790   
  791       /**
  792        * Load the object in the current row of the given result.
  793        */
  794       public Object load(ClassMapping mapping, JDBCFetchConfiguration fetch,
  795           BitSet exclude, Result result) throws SQLException {
  796           if (!mapping.isMapped())
  797               throw new InvalidStateException(_loc.get("virtual-mapping", 
  798                   mapping));
  799   
  800           // get the object id for the row; base class selects pk columns
  801           ClassMapping base = mapping;
  802           while (base.getJoinablePCSuperclassMapping() != null)
  803               base = base.getJoinablePCSuperclassMapping();
  804           Object oid = base.getObjectId(this, result, null, true, null);
  805           if (oid == null)
  806               return null;
  807   
  808           ConnectionInfo info = new ConnectionInfo();
  809           info.result = result;
  810           info.mapping = mapping;
  811           return _ctx.find(oid, fetch, exclude, info, 0);
  812       }
  813   
  814       /**
  815        * Load the given state manager with data from the result set. Only
  816        * mappings originally selected will be loaded.
  817        */
  818       private void load(ClassMapping mapping, OpenJPAStateManager sm,
  819           JDBCFetchConfiguration fetch, Result res) throws SQLException {
  820           FieldMapping eagerToMany = load(mapping, sm, fetch, res, null);
  821           if (eagerToMany != null)
  822               eagerToMany.loadEagerJoin(sm, this, fetch.traverseJDBC(eagerToMany),
  823                   res);
  824           if (_active && _lm != null && res.isLocking())
  825               _lm.loadedForUpdate(sm);
  826       }
  827   
  828       /**
  829        * Load the fields of the given mapping. Return any to-many eager field
  830        * without loading it.
  831        */
  832       private FieldMapping load(ClassMapping mapping, OpenJPAStateManager sm,
  833           JDBCFetchConfiguration fetch, Result res, FieldMapping eagerToMany)
  834           throws SQLException {
  835           if (mapping.customLoad(sm, this, fetch, res))
  836               return eagerToMany;
  837   
  838           // load superclass data; base class loads version
  839           ClassMapping parent = mapping.getJoinablePCSuperclassMapping();
  840           if (parent != null)
  841               eagerToMany = load(parent, sm, fetch, res, eagerToMany);
  842           else if (sm.getVersion() == null)
  843               mapping.getVersion().load(sm, this, res);
  844   
  845           // load unloaded fields
  846           FieldMapping[] fms = mapping.getDefinedFieldMappings();
  847           Object eres, processed;
  848           for (int i = 0; i < fms.length; i++) {
  849               if (fms[i].isPrimaryKey() || sm.getLoaded().get(fms[i].getIndex()))
  850                   continue;
  851               
  852               // check for eager result, and if not present do standard load
  853               eres = res.getEager(fms[i]);
  854               res.startDataRequest(fms[i]);
  855               try {
  856                  if (eres == res) {
  857                       if (eagerToMany == null && fms[i].isEagerSelectToMany())
  858                           eagerToMany = fms[i];
  859                       else
  860                           fms[i].loadEagerJoin(sm, this, 
  861                           	fetch.traverseJDBC(fms[i]), res);
  862                   } else if (eres != null) {
  863                       processed = fms[i].loadEagerParallel(sm, this, 
  864                       	fetch.traverseJDBC(fms[i]), eres);
  865                       if (processed != eres)
  866                           res.putEager(fms[i], processed);
  867                   } else
  868                       fms[i].load(sm, this, fetch.traverseJDBC(fms[i]), res);
  869               } finally {
  870                   res.endDataRequest();
  871               }
  872           }
  873           return eagerToMany;
  874       }
  875   
  876       /**
  877        * For implementation use only.
  878        * Return a select for the proper mappings. Return null if no select is
  879        * needed. The method is designed to be complementary to the load methods.
  880        *
  881        * @param sel select to build on
  882        * @param mapping the mapping for the base type to select for
  883        * @param subs whether the select might include subclasses of the
  884        * given mapping
  885        * @param sm state manager if an instance is being loaded or
  886        * initialized, else null
  887        * @param fields if a state manager is being loaded, the set of
  888        * fields that must be loaded in order, else null
  889        * @param fetch the fetch configuration; used if no specific fields
  890        * must be loaded, and used when selecting relations
  891        * @param eager eager fetch mode to use
  892        * @param ident whether to select primary key columns as distinct
  893        * identifiers
  894        * @param outer whether we're outer-joining to this type
  895        * @return true if the select is required, false otherwise
  896        */
  897       public boolean select(Select sel, ClassMapping mapping, int subs,
  898           OpenJPAStateManager sm, BitSet fields, JDBCFetchConfiguration fetch,
  899           int eager, boolean ident, boolean outer) {
  900           // add class conditions so that they're cloned for any batched selects
  901           boolean joinedSupers = false;
  902           if ((sm == null || sm.getPCState() == PCState.TRANSIENT)
  903               && (subs == Select.SUBS_JOINABLE || subs == Select.SUBS_NONE)) {
  904               loadSubclasses(mapping); 
  905               Joins joins = (outer) ? sel.newOuterJoins() : null;
  906               joinedSupers = mapping.getDiscriminator().addClassConditions(sel,
  907                   subs == Select.SUBS_JOINABLE, joins);
  908           }
  909   
  910           // create all our eager selects so that those fields are reserved
  911           // and cannot be reused during the actual eager select process,
  912           // preventing infinite recursion
  913           eager = Math.min(eager, fetch.getEagerFetchMode());
  914           FieldMapping eagerToMany = createEagerSelects(sel, mapping, sm, fields,
  915               fetch, eager);
  916   
  917           // select all base class mappings; do this after batching so that
  918           // the joins needed by these selects don't get in the WHERE clause
  919           // of the batched selects
  920           int seld = selectBaseMappings(sel, mapping, mapping, sm, fields,
  921               fetch, eager, eagerToMany, ident, joinedSupers);
  922   
  923           // select eager to-many relations last because during load they
  924           // advance the result set and could exhaust it, so no other mappings
  925           // can load afterwords
  926           if (eagerToMany != null)
  927               eagerToMany.selectEagerJoin(sel, sm, this, 
  928                   fetch.traverseJDBC(eagerToMany), eager);
  929   
  930           // optionally select subclass mappings
  931           if (subs == Select.SUBS_JOINABLE || subs == Select.SUBS_ANY_JOINABLE)
  932               selectSubclassMappings(sel, mapping, sm, fetch);
  933           if (sm != null)
  934               sel.setDistinct(false);
  935           return seld > 0;
  936       }
  937   
  938       /**
  939        * Mark the fields of this mapping as reserved so that eager fetches can't
  940        * get into infinite recursive situations.
  941        */
  942       private FieldMapping createEagerSelects(Select sel, ClassMapping mapping,
  943           OpenJPAStateManager sm, BitSet fields, JDBCFetchConfiguration fetch,
  944           int eager) {
  945           if (mapping == null || eager == JDBCFetchConfiguration.EAGER_NONE)
  946               return null;
  947   
  948           FieldMapping eagerToMany = createEagerSelects(sel, 
  949               mapping.getJoinablePCSuperclassMapping(), sm, fields, fetch, eager);
  950   
  951           FieldMapping[] fms = mapping.getDefinedFieldMappings();
  952           boolean inEagerJoin = sel.hasEagerJoin(false);
  953           int sels;
  954           int jtype;
  955           int mode;
  956           for (int i = 0; i < fms.length; i++) {
  957               mode = fms[i].getEagerFetchMode();
  958               if (mode == fetch.EAGER_NONE)
  959                   continue;
  960               if (!requiresSelect(fms[i], sm, fields, fetch))
  961                   continue;
  962   
  963               // try to select with join first
  964               jtype = (fms[i].getNullValue() == fms[i].NULL_EXCEPTION) 
  965                   ? sel.EAGER_INNER : sel.EAGER_OUTER;
  966               if (mode != fetch.EAGER_PARALLEL && !fms[i].isEagerSelectToMany()
  967                   && fms[i].supportsSelect(sel, jtype, sm, this, fetch) > 0
  968                   && sel.eagerClone(fms[i], jtype, false, 1) != null)
  969                   continue;
  970   
  971               boolean hasJoin = fetch.hasJoin(fms[i].getFullName(false));
  972   
  973               // if the field declares a preferred select mode of join or does not
  974               // have a preferred mode and we're doing a by-id lookup, try
  975               // to use a to-many join also.  currently we limit eager
  976               // outer joins to non-LRS, non-ranged selects that don't already
  977               // have an eager to-many join
  978               if ((hasJoin || mode == fetch.EAGER_JOIN 
  979                   || (mode == fetch.DEFAULT && sm != null))
  980                   && fms[i].isEagerSelectToMany()
  981                   && !inEagerJoin
  982                   && !sel.hasEagerJoin(true)
  983                   && (!sel.getAutoDistinct() || (!sel.isLRS()
  984                   && sel.getStartIndex() == 0 
  985                   && sel.getEndIndex() == Long.MAX_VALUE))
  986                   && fms[i].supportsSelect(sel, jtype, sm, this, fetch) > 0) {
  987                   if (sel.eagerClone(fms[i], jtype, true, 1) != null)
  988                       eagerToMany = fms[i];
  989                   else
  990                       continue;
  991               }
  992   
  993               // finally, try parallel
  994               if (eager == fetch.EAGER_PARALLEL
  995                   && (sels = fms[i].supportsSelect(sel, sel.EAGER_PARALLEL, sm,
  996                   this, fetch)) != 0)
  997                   sel.eagerClone(fms[i], Select.EAGER_PARALLEL, 
  998                       fms[i].isEagerSelectToMany(), sels);
  999           }
 1000           return eagerToMany;
 1001       }
 1002   
 1003       /**
 1004        * Determine if the given field needs to be selected.
 1005        */
 1006       private static boolean requiresSelect(FieldMapping fm,
 1007           OpenJPAStateManager sm, BitSet fields, JDBCFetchConfiguration fetch) {
 1008           if (fields != null)
 1009               return fields.get(fm.getIndex());
 1010           if (sm != null && sm.getPCState() != PCState.TRANSIENT
 1011               && sm.getLoaded().get(fm.getIndex()))
 1012               return false;
 1013           return fetch.requiresFetch(fm) == FetchConfiguration.FETCH_LOAD;
 1014       }
 1015   
 1016       /**
 1017        * Select the field mappings of the given class and all its superclasses.
 1018        *
 1019        * @param sel the select to use
 1020        * @param mapping the most-derived type to select for
 1021        * @param orig the original mapping type selected
 1022        * @param sm the instance being selected for, or null if none
 1023        * @param fields the fields to load
 1024        * @param fetch fetch configuration to use for loading relations
 1025        * @param eager the eager fetch mode to use
 1026        * @param joined whether the class has already been joined down to
 1027        * its base class
 1028        * @return &gt; 0 if the select is required, 0 if data was
 1029        * selected but is not required, and &lt; 0 if nothing was selected
 1030        */
 1031       private int selectBaseMappings(Select sel, ClassMapping mapping,
 1032           ClassMapping orig, OpenJPAStateManager sm, BitSet fields,
 1033           JDBCFetchConfiguration fetch, int eager, FieldMapping eagerToMany,
 1034           boolean ident, boolean joined) {
 1035           ClassMapping parent = mapping.getJoinablePCSuperclassMapping();
 1036           if (parent == null && !mapping.isMapped())
 1037               throw new InvalidStateException(_loc.get("virtual-mapping", mapping.
 1038                   getDescribedType()));
 1039   
 1040           int seld = -1;
 1041           int pseld = -1;
 1042   
 1043           // base class selects pks, etc
 1044           if (parent == null) {
 1045               // if no instance, select pks
 1046               if (sm == null) {
 1047                   if (ident)
 1048                       sel.selectIdentifier(mapping.getPrimaryKeyColumns());
 1049                   else
 1050                       sel.select(mapping.getPrimaryKeyColumns());
 1051                   seld = 1;
 1052               }
 1053   
 1054               // if no instance or not initialized and not exact oid, select type
 1055               if ((sm == null || (sm.getPCState() == PCState.TRANSIENT 
 1056                   && (!(sm.getObjectId() instanceof OpenJPAId) 
 1057                   || ((OpenJPAId) sm.getObjectId()).hasSubclasses())))
 1058                   && mapping.getDiscriminator().select(sel, orig))
 1059                   seld = 1;
 1060   
 1061               // if no instance or no version, select version
 1062               if ((sm == null || sm.getVersion() == null)
 1063                   && mapping.getVersion().select(sel, orig))
 1064                   seld = 1;
 1065           } else {
 1066               // recurse on parent
 1067               pseld = selectBaseMappings(sel, parent, orig, sm, fields,
 1068                   fetch, eager, eagerToMany, ident, joined);
 1069           }
 1070   
 1071           // select the mappings in the given fields set, or based on fetch
 1072           // configuration if no fields given
 1073           FieldMapping[] fms = mapping.getDefinedFieldMappings();
 1074           SelectExecutor esel;
 1075           int fseld;
 1076           for (int i = 0; i < fms.length; i++) {
 1077               // skip eager to-many select; we do that separately in calling
 1078               // method
 1079               if (fms[i] == eagerToMany)
 1080                   continue;
 1081   
 1082               // check for eager select
 1083               esel = sel.getEager(fms[i]);
 1084               if (esel != null) {
 1085                   if (esel == sel)
 1086                       fms[i].selectEagerJoin(sel, sm, this, 
 1087                       	fetch.traverseJDBC(fms[i]), eager);
 1088                   else
 1089                       fms[i].selectEagerParallel(esel, sm, this, 
 1090                       	fetch.traverseJDBC(fms[i]), eager);
 1091                   seld = Math.max(0, seld);
 1092               } else if (requiresSelect(fms[i], sm, fields, fetch)) {
 1093                   fseld = fms[i].select(sel, sm, this, 
 1094                   	fetch.traverseJDBC(fms[i]), eager);
 1095                   seld = Math.max(fseld, seld);
 1096               } else if (optSelect(fms[i], sel, sm, fetch)) {
 1097                   fseld = fms[i].select(sel, sm, this, 
 1098                   	fetch.traverseJDBC(fms[i]), fetch.EAGER_NONE);
 1099   
 1100                   // don't upgrade seld to > 0 based on these fields, since
 1101                   // they're not in the calculated field set
 1102                   if (fseld >= 0 && seld < 0)
 1103                       seld = 0;
 1104               }
 1105           }
 1106   
 1107           // join to parent table if the parent / any ancestors have selected
 1108           // anything
 1109           if (!joined && pseld >= 0 && parent.getTable() != mapping.getTable())
 1110               sel.where(mapping.joinSuperclass(sel.newJoins(), false));
 1111   
 1112           // return the highest value
 1113           return Math.max(pseld, seld);
 1114       }
 1115   
 1116       /**
 1117        * When selecting fieldes, a special case is made for mappings that use
 1118        * 2-part selects that aren't explicitly *not* in the dfg so that they
 1119        * can get their primary table data. This method tests for that special
 1120        * case as an optimization.
 1121        */
 1122       private boolean optSelect(FieldMapping fm, Select sel,
 1123           OpenJPAStateManager sm, JDBCFetchConfiguration fetch) {
 1124           return !fm.isInDefaultFetchGroup() 
 1125               && !fm.isDefaultFetchGroupExplicit()
 1126               && (sm == null || sm.getPCState() == PCState.TRANSIENT 
 1127               || !sm.getLoaded().get(fm.getIndex()))
 1128               && fm.supportsSelect(sel, sel.TYPE_TWO_PART, sm, this, fetch) > 0;
 1129       }
 1130   
 1131       /**
 1132        * Select field mappings that match the given fetch configuration for
 1133        * subclasses of the given type.
 1134        *
 1135        * @param sel the select to use
 1136        * @param mapping the type whose subclasses to select
 1137        * @param sm the instance being selected for, or null if none
 1138        * @param fetch the fetch configuration
 1139        */
 1140       private void selectSubclassMappings(Select sel, ClassMapping mapping,
 1141           OpenJPAStateManager sm, JDBCFetchConfiguration fetch) {
 1142           loadSubclasses(mapping);
 1143           ClassMapping[] subMappings = mapping.getJoinablePCSubclassMappings();
 1144           if (subMappings.length == 0)
 1145               return;
 1146   
 1147           // select all subclass mappings that match the fetch configuration
 1148           // and whose table is in the list of those selected so far; this
 1149           // way we select the max possible without selecting any tables that
 1150           // aren't present in all possible query matches; a special case
 1151           // is made for mappings that use 2-part selects that aren't
 1152           // explicitly *not* in the default so that they can get their
 1153           // primary table data
 1154           FieldMapping[] fms;
 1155           boolean joined;
 1156           boolean canJoin = _dict.joinSyntax != JoinSyntaxes.SYNTAX_TRADITIONAL
 1157               && fetch.getSubclassFetchMode(mapping) != fetch.EAGER_NONE;
 1158           for (int i = 0; i < subMappings.length; i++) {
 1159               if (!subMappings[i].supportsEagerSelect(sel, sm, this, mapping,
 1160                   fetch))
 1161                   continue;
 1162   
 1163               // initialize so that if we can't join, we pretend we already have
 1164               joined = !canJoin;
 1165               fms = subMappings[i].getDefinedFieldMappings();
 1166               for (int j = 0; j < fms.length; j++) {
 1167                   // make sure in one of configured fetch groups
 1168               	if (fetch.requiresFetch(fms[j]) != FetchConfiguration.FETCH_LOAD
 1169                       && ((!fms[j].isInDefaultFetchGroup() 
 1170                       && fms[j].isDefaultFetchGroupExplicit())
 1171                       || fms[j].supportsSelect(sel, sel.TYPE_TWO_PART, sm, this, 
 1172                       fetch) <= 0)) 
 1173               		continue;
 1174   
 1175                   // if we can join to the subclass, do so; much better chance
 1176                   // that the field will be able to select itself without joins
 1177                   if (!joined) {
 1178                       // mark joined whether or not we join, so we don't have to
 1179                       // test conditions again for this subclass
 1180                       joined = true;
 1181                       sel.where(joinSubclass(sel, mapping, subMappings[i], null));
 1182                   }
 1183   
 1184                   // if can select with tables already selected, do it
 1185                   if (fms[j].supportsSelect(sel, sel.TYPE_JOINLESS, sm, this,
 1186                       fetch) > 0)
 1187                       fms[j].select(sel, null, this, fetch.traverseJDBC(fms[j]),
 1188                           fetch.EAGER_NONE);
 1189               }
 1190           }
 1191       }
 1192   
 1193       /**
 1194        * Helper method to join from class to its subclass. Recursive to allow
 1195        * for multiple hops, starting from the base class.
 1196        */
 1197       private static Joins joinSubclass(Select sel, ClassMapping base,
 1198           ClassMapping sub, Joins joins) {
 1199           if (sub == base || sub.getTable() == base.getTable()
 1200               || sel.isSelected(sub.getTable()))
 1201               return null;
 1202   
 1203           // recurse first so we go least->most derived
 1204           ClassMapping sup = sub.getJoinablePCSuperclassMapping();
 1205           joins = joinSubclass(sel, base, sup, joins);
 1206           if (joins == null)
 1207               joins = sel.newJoins();
 1208           return sub.joinSuperclass(joins, true);
 1209       }
 1210   
 1211       /**
 1212        * Makes sure all subclasses of the given type are loaded in the JVM.
 1213        * This is usually done automatically.
 1214        */
 1215       public void loadSubclasses(ClassMapping mapping) {
 1216           Discriminator dsc = mapping.getDiscriminator();
 1217           if (dsc.getSubclassesLoaded())
 1218               return;
 1219   
 1220           // if the subclass list is set, no need to load subs
 1221           if (mapping.getRepository().getPersistentTypeNames(false,
 1222               _ctx.getClassLoader()) != null) {
 1223               dsc.setSubclassesLoaded(true);
 1224               return;
 1225           }
 1226   
 1227           try {
 1228               dsc.loadSubclasses(this);
 1229           } catch (ClassNotFoundException cnfe) {
 1230               throw new StoreException(cnfe);
 1231           } catch (SQLException se) {
 1232               throw SQLExceptions.getStore(se, _dict);
 1233           }
 1234       }
 1235   
 1236       /**
 1237        * Make the statement a candidate for cancellation.
 1238        */
 1239       private void beforeExecuteStatement(Statement stmnt) {
 1240           _stmnts.add(stmnt);
 1241       }
 1242   
 1243       /**
 1244        * Remove the statement from the cancellable set.
 1245        */
 1246       private void afterExecuteStatement(Statement stmnt) {
 1247           _stmnts.remove(stmnt);
 1248       }
 1249   
 1250       /**
 1251        * Connection returned to client code. Makes sure its wrapped connection
 1252        * ref count is decremented on finalize.
 1253        */
 1254       private static class ClientConnection extends DelegatingConnection {
 1255   
 1256           private boolean _closed = false;
 1257   
 1258           public ClientConnection(Connection conn) {
 1259               super(conn);
 1260           }
 1261   
 1262           public void close() throws SQLException {
 1263               _closed = true;
 1264               super.close();
 1265           }
 1266   
 1267           protected void finalize() throws SQLException {
 1268               if (!_closed)
 1269                   close();
 1270           }
 1271       }
 1272   
 1273       /**
 1274        * Connection wrapper that keeps an internal ref count so that it knows
 1275        * when to really close.
 1276        */
 1277       protected class RefCountConnection extends DelegatingConnection {
 1278   
 1279           private boolean _retain = false;
 1280           private int _refs = 0;
 1281           private boolean _freed = false;
 1282   
 1283           public RefCountConnection(Connection conn) {
 1284               super(conn);
 1285           }
 1286   
 1287           public boolean getRetain() {
 1288               return _retain;
 1289           }
 1290   
 1291           public void setRetain(boolean retain) {
 1292               if (_retain && !retain && _refs <= 0)
 1293                   free();
 1294               _retain = retain;
 1295           }
 1296   
 1297           public void ref() {
 1298               // don't have to lock; called from connect(), which is locked
 1299               _refs++;
 1300           }
 1301   
 1302           public void close() throws SQLException {
 1303               // lock at broker level to avoid deadlocks
 1304               _ctx.lock();
 1305               try {
 1306                   _refs--;
 1307                   if (_refs <= 0 && !_retain)
 1308                       free();
 1309               } finally {
 1310                   _ctx.unlock();
 1311               }
 1312           }
 1313   
 1314           public void free() {
 1315               // ensure that we do not close the underlying connection
 1316               // multiple times; this could happen if someone (e.g., an
 1317               // Extent) holds a RefConnection, and then closes it (e.g., in
 1318               // the finalizer) after the StoreManager has already been closed.
 1319               if (_freed)
 1320                   return;
 1321   
 1322               try {
 1323                   getDelegate().close();
 1324               } catch (SQLException se) {
 1325               }
 1326               _freed = true;
 1327               _conn = null;
 1328           }
 1329   
 1330           protected Statement createStatement(boolean wrap) throws SQLException {
 1331               return new CancelStatement(super.createStatement(false),
 1332                   RefCountConnection.this);
 1333           }
 1334   
 1335           protected Statement createStatement(int rsType, int rsConcur,
 1336               boolean wrap) throws SQLException {
 1337               return new CancelStatement(super.createStatement(rsType, rsConcur,
 1338                   false), RefCountConnection.this);
 1339           }
 1340   
 1341           protected PreparedStatement prepareStatement(String sql, boolean wrap)
 1342               throws SQLException {
 1343               return new CancelPreparedStatement(super.prepareStatement(sql,
 1344                   false), RefCountConnection.this);
 1345           }
 1346   
 1347           protected PreparedStatement prepareStatement(String sql, int rsType,
 1348               int rsConcur, boolean wrap) throws SQLException {
 1349               return new CancelPreparedStatement(super.prepareStatement(sql,
 1350                   rsType, rsConcur, false), RefCountConnection.this);
 1351           }
 1352       }
 1353   
 1354       /**
 1355        * Statement type that adds and removes itself from the set of active
 1356        * statements so that it can be canceled.
 1357        */
 1358       private class CancelStatement extends DelegatingStatement {
 1359   
 1360           public CancelStatement(Statement stmnt, Connection conn) {
 1361               super(stmnt, conn);
 1362           }
 1363   
 1364           public int executeUpdate(String sql) throws SQLException {
 1365               beforeExecuteStatement(this);
 1366               try {
 1367                   return super.executeUpdate(sql);
 1368               } finally {
 1369                   afterExecuteStatement(this);
 1370               }
 1371           }
 1372   
 1373           protected ResultSet executeQuery(String sql, boolean wrap)
 1374               throws SQLException {
 1375               beforeExecuteStatement(this);
 1376               try {
 1377                   return super.executeQuery(sql, wrap);
 1378               } finally {
 1379                   afterExecuteStatement(this);
 1380               }
 1381           }
 1382       }
 1383   
 1384       /**
 1385        * Statement type that adds and removes itself from the set of active
 1386        * statements so that it can be canceled.
 1387        */
 1388       private class CancelPreparedStatement extends DelegatingPreparedStatement {
 1389   
 1390           public CancelPreparedStatement(PreparedStatement stmnt, 
 1391               Connection conn) {
 1392               super(stmnt, conn);
 1393           }
 1394   
 1395           public int executeUpdate() throws SQLException {
 1396               beforeExecuteStatement(this);
 1397               try {
 1398                   return super.executeUpdate();
 1399               } finally {
 1400                   afterExecuteStatement(this);
 1401               }
 1402           }
 1403   
 1404           protected ResultSet executeQuery(boolean wrap) throws SQLException {
 1405               beforeExecuteStatement(this);
 1406               try {
 1407                   return super.executeQuery(wrap);
 1408               } finally {
 1409                   afterExecuteStatement(this);
 1410               }
 1411           }
 1412   
 1413           public int[] executeBatch() throws SQLException {
 1414               beforeExecuteStatement(this);
 1415               try {
 1416                   return super.executeBatch();
 1417               } finally {
 1418                   afterExecuteStatement(this);
 1419               }
 1420           }
 1421       }
 1422   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » kernel » [javadoc | source]