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.SQLException;
   24   import java.util.ArrayList;
   25   import java.util.Arrays;
   26   import java.util.BitSet;
   27   import java.util.Collection;
   28   import java.util.HashMap;
   29   import java.util.List;
   30   import java.util.Map;
   31   
   32   import org.apache.openjpa.event.LifecycleEventManager;
   33   import org.apache.openjpa.jdbc.kernel.exps.ExpContext;
   34   import org.apache.openjpa.jdbc.kernel.exps.GetColumn;
   35   import org.apache.openjpa.jdbc.kernel.exps.JDBCExpressionFactory;
   36   import org.apache.openjpa.jdbc.kernel.exps.JDBCStringContains;
   37   import org.apache.openjpa.jdbc.kernel.exps.JDBCWildcardMatch;
   38   import org.apache.openjpa.jdbc.kernel.exps.QueryExpressionsState;
   39   import org.apache.openjpa.jdbc.kernel.exps.SQLEmbed;
   40   import org.apache.openjpa.jdbc.kernel.exps.SQLExpression;
   41   import org.apache.openjpa.jdbc.kernel.exps.SQLValue;
   42   import org.apache.openjpa.jdbc.meta.ClassMapping;
   43   import org.apache.openjpa.jdbc.meta.FieldMapping;
   44   import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
   45   import org.apache.openjpa.jdbc.schema.Column;
   46   import org.apache.openjpa.jdbc.schema.Table;
   47   import org.apache.openjpa.jdbc.sql.DBDictionary;
   48   import org.apache.openjpa.jdbc.sql.SQLBuffer;
   49   import org.apache.openjpa.jdbc.sql.SQLExceptions;
   50   import org.apache.openjpa.jdbc.sql.Select;
   51   import org.apache.openjpa.jdbc.sql.Union;
   52   import org.apache.openjpa.kernel.ExpressionStoreQuery;
   53   import org.apache.openjpa.kernel.OrderingMergedResultObjectProvider;
   54   import org.apache.openjpa.kernel.QueryHints;
   55   import org.apache.openjpa.kernel.exps.ExpressionFactory;
   56   import org.apache.openjpa.kernel.exps.ExpressionParser;
   57   import org.apache.openjpa.kernel.exps.FilterListener;
   58   import org.apache.openjpa.kernel.exps.QueryExpressions;
   59   import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
   60   import org.apache.openjpa.lib.rop.RangeResultObjectProvider;
   61   import org.apache.openjpa.lib.rop.ResultObjectProvider;
   62   import org.apache.openjpa.lib.util.Localizer;
   63   import org.apache.openjpa.meta.ClassMetaData;
   64   import org.apache.openjpa.meta.ValueMetaData;
   65   import org.apache.openjpa.util.UserException;
   66   import serp.util.Numbers;
   67   
   68   /**
   69    * JDBC query implementation.
   70    *
   71    * @author Abe White
   72    * @nojavadoc
   73    */
   74   public class JDBCStoreQuery 
   75       extends ExpressionStoreQuery {
   76   
   77       private static final Table INVALID = new Table();
   78   
   79       // add all standard filter and aggregate listeners to these maps
   80       private static final Map _listeners = new HashMap();
   81   
   82       static {
   83           // deprecated extensions
   84           _listeners.put(JDBCStringContains.TAG, new JDBCStringContains());
   85           _listeners.put(JDBCWildcardMatch.TAG, new JDBCWildcardMatch());
   86           _listeners.put(SQLExpression.TAG, new SQLExpression());
   87           _listeners.put(SQLValue.TAG, new SQLValue());
   88   
   89           // jdbc-specific extensions
   90           _listeners.put(GetColumn.TAG, new GetColumn());
   91           _listeners.put(SQLEmbed.TAG, new SQLEmbed());
   92       }
   93   
   94       private final transient JDBCStore _store;
   95   
   96       /**
   97        * Constructor. Supply store manager.
   98        */
   99       public JDBCStoreQuery(JDBCStore store, ExpressionParser parser) {
  100           super(parser);
  101           _store = store;
  102       }
  103   
  104       /**
  105        * Return the store.
  106        */
  107       public JDBCStore getStore() {
  108           return _store;
  109       }
  110   
  111       public FilterListener getFilterListener(String tag) {
  112           return (FilterListener) _listeners.get(tag);
  113       }
  114   
  115       public Object newCompilationKey() {
  116           JDBCFetchConfiguration fetch = (JDBCFetchConfiguration) ctx
  117               .getFetchConfiguration();
  118           return Numbers.valueOf(fetch.getJoinSyntax());
  119       }
  120   
  121       public boolean supportsDataStoreExecution() {
  122           return true;
  123       }
  124   
  125       protected ClassMetaData[] getIndependentExpressionCandidates(
  126           ClassMetaData meta, boolean subclasses) {
  127           if (!subclasses)
  128               return new ClassMapping[] { (ClassMapping) meta };
  129           return ((ClassMapping) meta).getIndependentAssignableMappings();
  130       }
  131   
  132       protected ExpressionFactory getExpressionFactory(ClassMetaData meta) {
  133           return new JDBCExpressionFactory((ClassMapping) meta);
  134       }
  135   
  136       protected ResultObjectProvider executeQuery(Executor ex,
  137           ClassMetaData base, ClassMetaData[] metas, boolean subclasses,
  138           ExpressionFactory[] facts, QueryExpressions[] exps, Object[] params,
  139           Range range) {
  140           if (metas.length > 1 && exps[0].isAggregate())
  141               throw new UserException(Localizer.forPackage(JDBCStoreQuery.class).
  142                   get("mult-mapping-aggregate", Arrays.asList(metas)));
  143   
  144           ClassMapping[] mappings = (ClassMapping[]) metas;
  145           JDBCFetchConfiguration fetch = (JDBCFetchConfiguration) 
  146               ctx.getFetchConfiguration();
  147           if (exps[0].fetchPaths != null) {
  148               fetch.addFields(Arrays.asList(exps[0].fetchPaths));
  149               fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
  150           }
  151           if (exps[0].fetchInnerPaths != null)
  152               fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
  153   
  154           int eager = calculateEagerMode(exps[0], range.start, range.end);
  155           int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
  156           DBDictionary dict = _store.getDBDictionary();
  157           long start = (mappings.length == 1 && dict.supportsSelectStartIndex) 
  158               ? range.start : 0L;
  159           long end = (dict.supportsSelectEndIndex) ? range.end : Long.MAX_VALUE;
  160   
  161           QueryExpressionsState[] states = new QueryExpressionsState[exps.length];
  162           for (int i = 0; i < states.length; i++)
  163               states[i] = new QueryExpressionsState();
  164           ExpContext ctx = new ExpContext(_store, params, fetch);
  165   
  166           // add selects with populate WHERE conditions to list
  167           List sels = new ArrayList(mappings.length);
  168           List selMappings = new ArrayList(mappings.length);
  169           BitSet subclassBits = new BitSet();
  170           BitSet nextBits = new BitSet();
  171           boolean unionable = createWhereSelects(sels, mappings, selMappings,
  172               subclasses, subclassBits, nextBits, facts, exps, states, ctx,
  173               subclassMode)
  174               && subclassMode == JDBCFetchConfiguration.EAGER_JOIN
  175               && start == 0
  176               && end == Long.MAX_VALUE;
  177   
  178           // we might want to use lrs settings if we can't use the range
  179           if (sels.size() > 1)
  180               start = 0L;
  181           boolean lrs = range.lrs || (fetch.getFetchBatchSize() >= 0 
  182               && (start != range.start || end != range.end));
  183   
  184           ResultObjectProvider[] rops = null;
  185           ResultObjectProvider rop = null;
  186           if (unionable) {
  187               Union union = _store.getSQLFactory().newUnion(
  188                   (Select[]) sels.toArray(new Select[sels.size()]));
  189               BitSet[] paged = populateUnion(union, mappings, subclasses, facts,
  190                   exps, states, ctx, lrs, eager, start, end);
  191               union.setLRS(lrs);
  192               rop = executeUnion(union, mappings, exps, states, ctx, paged);
  193           } else {
  194               if (sels.size() > 1)
  195                   rops = new ResultObjectProvider[sels.size()];
  196   
  197               Select sel;
  198               BitSet paged;
  199               for (int i = 0, idx = 0; i < sels.size(); i++) {
  200                   sel = (Select) sels.get(i);
  201                   paged = populateSelect(sel, (ClassMapping) selMappings.get(i),
  202                       subclassBits.get(i), (JDBCExpressionFactory) facts[idx],
  203                       exps[idx], states[idx], ctx, lrs, eager, start, end);
  204   
  205                   rop = executeSelect(sel, (ClassMapping) selMappings.get(i),
  206                       exps[idx], states[idx], ctx, paged, start, end);
  207                   if (rops != null)
  208                       rops[i] = rop;
  209   
  210                   if (nextBits.get(i))
  211                       idx++;
  212               }
  213           }
  214   
  215           if (rops != null) {
  216               if (exps[0].ascending.length == 0)
  217                   rop = new MergedResultObjectProvider(rops);
  218               else {
  219                   rop = new OrderingMergedResultObjectProvider(rops,
  220                       exps[0].ascending, ex, this, params);
  221               }
  222           }
  223   
  224           // need to fake result range?
  225           if ((rops != null && range.end != Long.MAX_VALUE) 
  226               || start != range.start || end != range.end)
  227               rop = new RangeResultObjectProvider(rop, range.start, range.end);
  228           return rop;
  229       }
  230   
  231       /**
  232        * Select data for the given union, returning paged fields.
  233        */
  234       private BitSet[] populateUnion(Union union, final ClassMapping[] mappings,
  235           final boolean subclasses, final ExpressionFactory[] facts,
  236           final QueryExpressions[] exps, final QueryExpressionsState[] states,
  237           final ExpContext ctx, final boolean lrs, final int eager,
  238           final long start, final long end) {
  239           final BitSet[] paged = (exps[0].projections.length > 0) ? null
  240               : new BitSet[mappings.length];
  241           union.select(new Union.Selector() {
  242               public void select(Select sel, int idx) {
  243                   BitSet bits = populateSelect(sel, mappings[idx], subclasses,
  244                       (JDBCExpressionFactory) facts[idx], exps[idx], states[idx],
  245                       ctx,  lrs, eager, start, end);
  246                   if (paged != null)
  247                       paged[idx] = bits;
  248               }
  249           });
  250           return paged;
  251       }
  252   
  253       /**
  254        * Select data for the given select, returning paged fields.
  255        */
  256       private BitSet populateSelect(Select sel, ClassMapping mapping,
  257           boolean subclasses, JDBCExpressionFactory fact, QueryExpressions exps,
  258           QueryExpressionsState state, ExpContext ctx, boolean lrs, int eager,
  259           long start, long end) {
  260           sel.setLRS(lrs);
  261           sel.setRange(start, end);
  262   
  263           BitSet paged = null;
  264           if (exps.projections.length == 0) {
  265               paged = PagingResultObjectProvider.getPagedFields(sel, mapping,
  266                   _store, ctx.fetch, eager, end - start);
  267               if (paged != null)
  268                   eager = JDBCFetchConfiguration.EAGER_JOIN;
  269           }
  270   
  271           fact.getSelectConstructor().select(sel, ctx, mapping, subclasses, exps,
  272               state, eager);
  273           return paged;
  274       }
  275   
  276       /**
  277        * Execute the given union.
  278        */
  279       private ResultObjectProvider executeUnion(Union union,
  280           ClassMapping[] mappings, QueryExpressions[] exps, 
  281           QueryExpressionsState[] states, ExpContext ctx, BitSet[] paged) {
  282           if (exps[0].projections.length > 0)
  283               return new ProjectionResultObjectProvider(union, exps, states, ctx);
  284   
  285           if (paged != null)
  286               for (int i = 0; i < paged.length; i++)
  287                   if (paged[i] != null)
  288                       return new PagingResultObjectProvider(union, mappings,
  289                           _store, ctx.fetch, paged, Long.MAX_VALUE);
  290   
  291           return new InstanceResultObjectProvider(union, mappings[0], _store,
  292               ctx.fetch);
  293       }
  294   
  295       /**
  296        * Execute the given select.
  297        */
  298       private ResultObjectProvider executeSelect(Select sel, ClassMapping mapping,
  299           QueryExpressions exps, QueryExpressionsState state, ExpContext ctx, 
  300           BitSet paged, long start, long end) {
  301           if (exps.projections.length > 0)
  302               return new ProjectionResultObjectProvider(sel, exps, state, ctx);
  303           if (paged != null)
  304               return new PagingResultObjectProvider(sel, mapping, _store, 
  305                   ctx.fetch, paged, end - start);
  306           return new InstanceResultObjectProvider(sel, mapping, _store, 
  307               ctx.fetch);
  308       }
  309   
  310       /**
  311        * Generate the selects with WHERE conditions needed to execute the query
  312        * for the given mappings.
  313        */
  314       private boolean createWhereSelects(List sels, ClassMapping[] mappings,
  315           List selMappings, boolean subclasses, BitSet subclassBits,
  316           BitSet nextBits, ExpressionFactory[] facts, QueryExpressions[] exps,
  317           QueryExpressionsState[] states, ExpContext ctx, int subclassMode) {
  318           Number optHint = (Number) ctx.fetch.getHint
  319               (QueryHints.HINT_RESULT_COUNT);
  320           ClassMapping[] verts;
  321           boolean unionable = true;
  322           Select sel;
  323           for (int i = 0; i < mappings.length; i++) {
  324               // determine vertical mappings to select separately
  325               verts = getVerticalMappings(mappings[i], subclasses, exps[i],
  326                   subclassMode);
  327               if (verts.length == 1 && subclasses)
  328                   subclassBits.set(sels.size());
  329   
  330               // create criteria select and clone for each vert mapping
  331               sel = ((JDBCExpressionFactory) facts[i]).getSelectConstructor().
  332                   evaluate(ctx, null, null, exps[i], states[i]);
  333               if (optHint != null)
  334                  sel.setExpectedResultCount(optHint.intValue(), true);
  335               else if (this.ctx.isUnique())
  336                   sel.setExpectedResultCount(1, false);
  337               for (int j = 0; j < verts.length; j++) {
  338                   selMappings.add(verts[j]);
  339                   if (j == verts.length - 1) {
  340                       nextBits.set(sels.size());
  341                       sels.add(sel);
  342                   } else
  343                       sels.add(sel.fullClone(1));
  344               }
  345   
  346               // turn off unioning if a given independent mapping requires
  347               // multiple selects, or if we're using FROM selects
  348               if (verts.length > 1 || sel.getFromSelect() != null)
  349                   unionable = false;
  350           }
  351           return unionable;
  352       }
  353   
  354       /**
  355        * Return all the vertical mappings to select separately. Depends on
  356        * subclass fetch mode and the type of query.
  357        */
  358       private ClassMapping[] getVerticalMappings(ClassMapping mapping,
  359           boolean subclasses, QueryExpressions exps, int subclassMode) {
  360           if (!subclasses || exps.projections.length > 0)
  361               return new ClassMapping[] { mapping };
  362   
  363           if (subclassMode != JDBCFetchConfiguration.EAGER_PARALLEL
  364               || !hasVerticalSubclasses(mapping))
  365               return new ClassMapping[] { mapping };
  366   
  367           List subs = new ArrayList(4);
  368           addSubclasses(mapping, subs);
  369           return (ClassMapping[]) subs.toArray(new ClassMapping[subs.size()]);
  370       }
  371   
  372       /**
  373        * Recursive helper to add mappings for subclasses to the given list.
  374        */
  375       private void addSubclasses(ClassMapping mapping, Collection subs) {
  376           // possible future optimizations:
  377           // - if no fields in meta or its subclasses (and not in an
  378           //   already-selected table) are in the current fetch
  379           //   configuration, stop creating new executors
  380           // - allow an executor to select a range of subclasses, rather
  381           //   than just all subclasses / no subclasses; this would
  382           //   allow us to do just one query per actual vertically-mapped
  383           //   subclass, rather than one per mapped subclass, as is happening now
  384   
  385           subs.add(mapping);
  386           if (!hasVerticalSubclasses(mapping))
  387               return;
  388   
  389           // recurse on immediate subclasses
  390           ClassMapping[] subMappings = mapping.getJoinablePCSubclassMappings();
  391           for (int i = 0; i < subMappings.length; i++)
  392               if (subMappings[i].getJoinablePCSuperclassMapping() == mapping)
  393                   addSubclasses(subMappings[i], subs);
  394       }
  395   
  396       /**
  397        * Return whether the given class has any vertical subclasses.
  398        */
  399       private static boolean hasVerticalSubclasses(ClassMapping mapping) {
  400           ClassMapping[] subs = mapping.getJoinablePCSubclassMappings();
  401           for (int i = 0; i < subs.length; i++)
  402               if (subs[i].getStrategy() instanceof VerticalClassStrategy)
  403                   return true;
  404           return false;
  405       }
  406   
  407       /**
  408        * The eager mode depends on the unique setting and range. If the range
  409        * produces 0 results, use eager setting of none. If it produces 1 result
  410        * or the query is unique, use an eager setting of single. Otherwise use
  411        * an eager mode of multiple.
  412        */
  413       private int calculateEagerMode(QueryExpressions exps, long start,
  414           long end) {
  415           if (exps.projections.length > 0 || start >= end)
  416               return EagerFetchModes.EAGER_NONE;
  417           if (end - start == 1 || ctx.isUnique())
  418               return EagerFetchModes.EAGER_JOIN;
  419           return EagerFetchModes.EAGER_PARALLEL;
  420       }
  421   
  422       protected Number executeDelete(Executor ex, ClassMetaData base,
  423           ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
  424           QueryExpressions[] exps, Object[] params) {
  425           return executeBulkOperation(metas, subclasses, facts, exps,
  426               params, null);
  427       }
  428   
  429       protected Number executeUpdate(Executor ex, ClassMetaData base,
  430           ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
  431           QueryExpressions[] exps, Object[] params) {
  432           return executeBulkOperation(metas, subclasses, facts, exps,
  433               params, exps[0].updates);
  434       }
  435   
  436       private Number executeBulkOperation(ClassMetaData[] metas,
  437           boolean subclasses, ExpressionFactory[] facts, QueryExpressions[] exps,
  438           Object[] params, Map updates) {
  439           // we cannot execute a bulk delete statement when have mappings in
  440           // multiple tables, so indicate we want to use in-memory with null
  441           ClassMapping[] mappings = (ClassMapping[]) metas;
  442   
  443           // specification of the "updates" map indicates that this is
  444           // an update query; otherwise, this is a delete statement
  445           boolean isUpdate = updates != null && updates.size() > 0;
  446   
  447           for (int i = 0; i < mappings.length; i++) {
  448               if (!isSingleTableMapping(mappings[i], subclasses) && !isUpdate)
  449                   return null;
  450   
  451               if (!isUpdate) {
  452                   // if there are any delete callbacks, we need to
  453                   // execute in-memory so the callbacks are invoked
  454                   LifecycleEventManager mgr = ctx.getStoreContext().getBroker()
  455                       .getLifecycleEventManager();
  456                   if (mgr.hasDeleteListeners(null, mappings[i]))
  457                       return null;
  458               }
  459           }
  460   
  461           JDBCFetchConfiguration fetch = (JDBCFetchConfiguration) 
  462               ctx.getFetchConfiguration();
  463           ExpContext ctx = new ExpContext(_store, params, fetch);
  464           DBDictionary dict = _store.getDBDictionary();
  465           QueryExpressionsState[] state = new QueryExpressionsState[exps.length];
  466           for (int i = 0; i < state.length; i++)
  467               state[i] = new QueryExpressionsState();
  468   
  469           SQLBuffer[] sql = new SQLBuffer[mappings.length];
  470           JDBCExpressionFactory jdbcFactory;
  471           Select sel;
  472           for (int i = 0; i < mappings.length; i++) {
  473               jdbcFactory = (JDBCExpressionFactory) facts[i];
  474               sel = jdbcFactory.getSelectConstructor().evaluate(ctx, null, null,
  475                   exps[i], state[i]);
  476               jdbcFactory.getSelectConstructor().select(sel, ctx, mappings[i], 
  477                   subclasses, exps[i], state[i], 
  478                   JDBCFetchConfiguration.EAGER_NONE);
  479   
  480               // The bulk operation will return null to indicate that the database
  481               // does not support the request bulk delete operation; in
  482               // this case, we need to perform the query in-memory and
  483               // manually delete the instances
  484               if (!isUpdate)
  485                   sql[i] = dict.toDelete(mappings[i], sel, params);
  486               else
  487                   sql[i] = dict.toUpdate(mappings[i], sel, _store, params,
  488                       updates);
  489   
  490               if (sql[i] == null)
  491                   return null;
  492           }
  493   
  494           // we need to make sure we have an active store connection
  495           _store.getContext().beginStore();
  496   
  497           Connection conn = _store.getConnection();
  498           long count = 0;
  499           try {
  500               PreparedStatement stmnt;
  501               for (int i = 0; i < sql.length; i++) {
  502                   stmnt = null;
  503                   try {
  504                       stmnt = prepareStatement(conn, sql[i]);
  505                       count += executeUpdate(conn, stmnt, sql[i], isUpdate);                    
  506                   } catch (SQLException se) {
  507                       throw SQLExceptions.getStore(se, sql[i].getSQL(), 
  508                           _store.getDBDictionary());
  509                   } finally {
  510                       if (stmnt != null)
  511                           try { stmnt.close(); } catch (SQLException se) {}
  512                   }
  513               }
  514           } finally {
  515               try { conn.close(); } catch (SQLException se) {}
  516           }
  517           return Numbers.valueOf(count);
  518       }
  519   
  520       /**
  521        * Whether the given mapping occupies only one table.
  522        */
  523       private boolean isSingleTableMapping(ClassMapping mapping,
  524           boolean subclasses) {
  525           ClassMapping root = mapping;
  526           while (root.getJoinablePCSuperclassMapping() != null)
  527               root = root.getJoinablePCSuperclassMapping();
  528           if (hasVerticalSubclasses(root))
  529               return false;
  530   
  531           // we cannot execute a bulk delete if any of the
  532           // field mappings for the candidates have columns
  533           // in any other table, since bulk deleting just from the
  534           // class will leave dangling relations; we might be able
  535           // to issue bulk deletes separately for the joins (possibly
  536           // using a temporary table to select the primary keys for
  537           // all the related tables and then issing a delete against those
  538           // keys), but that logic is not currently implemented
  539           Table table = getTable(mapping.getFieldMappings(), null);
  540           if (table == INVALID)
  541               return false;
  542   
  543           if (subclasses) {
  544               // if we are including subclasses, we also need to gather
  545               // all the mappings for all known subclasses
  546               ClassMapping[] subs = mapping.getJoinablePCSubclassMappings();
  547               for (int i = 0; subs != null && i < subs.length; i++) {
  548                   table = getTable(subs[i].getDefinedFieldMappings(), table);
  549                   if (table == INVALID)
  550                       return false;
  551               }
  552           }
  553           return true;
  554       }
  555   
  556       /**
  557        * Return the single table for the given fields, or INVALID if they
  558        * use multiple tables.
  559        */
  560       private Table getTable(FieldMapping[] fields, Table table) {
  561           for (int i = 0; i < fields.length; i++) {
  562               table = getTable(fields[i], table);
  563               if (table == INVALID)
  564                   break;
  565           }
  566           return table;
  567       }
  568   
  569       /**
  570        * Return the table for the field if the given table hasn't been set
  571        * yet, or if the tables match. If the field uses a different table,
  572        * returns INVALID. Also returns INVALID if field is dependent.
  573        */
  574       private Table getTable(FieldMapping fm, Table table) {
  575           if (fm.getCascadeDelete() != ValueMetaData.CASCADE_NONE)
  576               return INVALID;
  577   
  578           Column[] columns = fm.getColumns();
  579           for (int i = 0; columns != null && i < columns.length; i++) {
  580               if (table == null)
  581                   table = columns[i].getTable();
  582               else if (table != columns[i].getTable())
  583                   return INVALID;
  584           }
  585           return table;
  586       }
  587   
  588       protected Number executeUpdate(ClassMetaData base, ClassMetaData[] metas,
  589           boolean subclasses, ExpressionFactory[] facts,
  590           QueryExpressions[] parsed, Object[] params) {
  591           return null;
  592       }
  593   
  594       protected String[] getDataStoreActions(ClassMetaData base,
  595           ClassMetaData[] metas, boolean subclasses, ExpressionFactory[] facts,
  596           QueryExpressions[] exps, Object[] params, Range range) {
  597           ClassMapping[] mappings = (ClassMapping[]) metas;
  598           JDBCFetchConfiguration fetch = (JDBCFetchConfiguration) ctx.
  599               getFetchConfiguration();
  600           if (exps[0].fetchPaths != null) {
  601               fetch.addFields(Arrays.asList(exps[0].fetchPaths));
  602               fetch.addJoins(Arrays.asList(exps[0].fetchPaths));
  603           }
  604           if (exps[0].fetchInnerPaths != null)
  605               fetch.addFetchInnerJoins(Arrays.asList(exps[0].fetchInnerPaths));
  606   
  607           int eager = calculateEagerMode(exps[0], range.start, range.end);
  608           eager = Math.min(eager, JDBCFetchConfiguration.EAGER_JOIN);
  609           int subclassMode = fetch.getSubclassFetchMode((ClassMapping) base);
  610           DBDictionary dict = _store.getDBDictionary();
  611           long start = (mappings.length == 1 && dict.supportsSelectStartIndex) 
  612               ? range.start : 0L;
  613           long end = (dict.supportsSelectEndIndex) ? range.end : Long.MAX_VALUE;
  614   
  615           QueryExpressionsState[] states = new QueryExpressionsState[exps.length];
  616           for (int i = 0; i < states.length; i++)
  617               states[i] = new QueryExpressionsState();
  618           ExpContext ctx = new ExpContext(_store, params, fetch);
  619   
  620           // add selects with populate WHERE conditions to list
  621           List sels = new ArrayList(mappings.length);
  622           List selMappings = new ArrayList(mappings.length);
  623           BitSet subclassBits = new BitSet();
  624           BitSet nextBits = new BitSet();
  625           boolean unionable = createWhereSelects(sels, mappings, selMappings,
  626               subclasses, subclassBits, nextBits, facts, exps, states, ctx, 
  627               subclassMode) && subclassMode == JDBCFetchConfiguration.EAGER_JOIN;
  628           if (sels.size() > 1)
  629               start = 0L;
  630   
  631           if (unionable) {
  632               Union union = _store.getSQLFactory().newUnion(
  633                   (Select[]) sels.toArray(new Select[sels.size()]));
  634               populateUnion(union, mappings, subclasses, facts, exps, states, ctx,
  635                   false, eager, start, end);
  636               if (union.isUnion())
  637                   return new String[] {union.toSelect(false, fetch).getSQL(true)};
  638               sels = Arrays.asList(union.getSelects());
  639           } else {
  640               Select sel;
  641               for (int i = 0, idx = 0; i < sels.size(); i++) {
  642                   sel = (Select) sels.get(i);
  643                   populateSelect(sel, (ClassMapping) selMappings.get(i),
  644                       subclassBits.get(i), (JDBCExpressionFactory) facts[idx],
  645                       exps[idx], states[idx], ctx, false, eager, start, end);
  646                   if (nextBits.get(i))
  647                       idx++;
  648               }
  649           }
  650   
  651           String[] sql = new String[sels.size()];
  652           for (int i = 0; i < sels.size(); i++)
  653               sql[i] = ((Select) sels.get(i)).toSelect(false, fetch).getSQL(true);
  654           return sql;
  655       }
  656       
  657       /**
  658        * This method is to provide override for non-JDBC or JDBC-like 
  659        * implementation of executing update.
  660        */
  661       protected int executeUpdate(Connection conn, PreparedStatement stmnt, 
  662           SQLBuffer sqlBuf, boolean isUpdate) throws SQLException {
  663           return stmnt.executeUpdate();
  664       }
  665               
  666       /**
  667        * This method is to provide override for non-JDBC or JDBC-like 
  668        * implementation of preparing statement.
  669        */
  670       protected PreparedStatement prepareStatement(Connection conn, SQLBuffer sql)
  671           throws SQLException {
  672           return sql.prepareStatement(conn);
  673       }    
  674   }

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