Home » apache-openjpa-1.1.0-source » org.apache.openjpa.jdbc » meta » strats » [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.meta.strats;
   20   
   21   import java.sql.SQLException;
   22   import java.util.HashMap;
   23   import java.util.Map;
   24   
   25   import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
   26   import org.apache.openjpa.jdbc.kernel.JDBCStore;
   27   import org.apache.openjpa.jdbc.meta.ClassMapping;
   28   import org.apache.openjpa.jdbc.meta.Embeddable;
   29   import org.apache.openjpa.jdbc.meta.FieldMapping;
   30   import org.apache.openjpa.jdbc.meta.Joinable;
   31   import org.apache.openjpa.jdbc.meta.MappingInfo;
   32   import org.apache.openjpa.jdbc.meta.ValueMapping;
   33   import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
   34   import org.apache.openjpa.jdbc.schema.Column;
   35   import org.apache.openjpa.jdbc.schema.ColumnIO;
   36   import org.apache.openjpa.jdbc.schema.ForeignKey;
   37   import org.apache.openjpa.jdbc.schema.PrimaryKey;
   38   import org.apache.openjpa.jdbc.schema.Table;
   39   import org.apache.openjpa.jdbc.sql.DBDictionary;
   40   import org.apache.openjpa.jdbc.sql.Joins;
   41   import org.apache.openjpa.jdbc.sql.Result;
   42   import org.apache.openjpa.jdbc.sql.Row;
   43   import org.apache.openjpa.jdbc.sql.RowManager;
   44   import org.apache.openjpa.jdbc.sql.SQLBuffer;
   45   import org.apache.openjpa.jdbc.sql.Select;
   46   import org.apache.openjpa.jdbc.sql.SelectExecutor;
   47   import org.apache.openjpa.jdbc.sql.Union;
   48   import org.apache.openjpa.kernel.OpenJPAStateManager;
   49   import org.apache.openjpa.lib.log.Log;
   50   import org.apache.openjpa.lib.util.Localizer;
   51   import org.apache.openjpa.meta.JavaTypes;
   52   import org.apache.openjpa.util.ApplicationIds;
   53   import org.apache.openjpa.util.ImplHelper;
   54   import org.apache.openjpa.util.InternalException;
   55   import org.apache.openjpa.util.MetaDataException;
   56   import org.apache.openjpa.util.OpenJPAId;
   57   import org.apache.openjpa.util.UnsupportedException;
   58   import serp.util.Numbers;
   59   
   60   /**
   61    * Mapping for a single-valued relation to another entity.
   62    *
   63    * @author Abe White
   64    * @since 0.4.0
   65    */
   66   public class RelationFieldStrategy
   67       extends AbstractFieldStrategy
   68       implements Joinable, Embeddable {
   69   
   70       private static final Localizer _loc = Localizer.forPackage
   71           (RelationFieldStrategy.class);
   72   
   73       private Boolean _fkOid = null;
   74   
   75       public void map(boolean adapt) {
   76           if (field.getTypeCode() != JavaTypes.PC || field.isEmbeddedPC())
   77               throw new MetaDataException(_loc.get("not-relation", field));
   78   
   79           field.getKeyMapping().getValueInfo().assertNoSchemaComponents
   80               (field.getKey(), !adapt);
   81           field.getElementMapping().getValueInfo().assertNoSchemaComponents
   82               (field.getElement(), !adapt);
   83           boolean criteria = field.getValueInfo().getUseClassCriteria();
   84   
   85           // check for named inverse
   86           FieldMapping mapped = field.getMappedByMapping();
   87           if (mapped != null) {
   88               field.getMappingInfo().assertNoSchemaComponents(field, !adapt);
   89               field.getValueInfo().assertNoSchemaComponents(field, !adapt);
   90               mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING);
   91   
   92               if (!mapped.isMapped() || mapped.isSerialized())
   93                   throw new MetaDataException(_loc.get("mapped-by-unmapped",
   94                       field, mapped));
   95   
   96               if (mapped.getTypeCode() == JavaTypes.PC) {
   97                   if (mapped.getJoinDirection() == mapped.JOIN_FORWARD) {
   98                       field.setJoinDirection(field.JOIN_INVERSE);
   99                       field.setColumns(mapped.getDefiningMapping().
  100                           getPrimaryKeyColumns());
  101                   } else if (isTypeUnjoinedSubclass(mapped))
  102                       throw new MetaDataException(_loc.get
  103                           ("mapped-inverse-unjoined", field.getName(),
  104                               field.getDefiningMapping(), mapped));
  105   
  106                   field.setForeignKey(mapped.getForeignKey
  107                       (field.getDefiningMapping()));
  108               } else if (mapped.getElement().getTypeCode() == JavaTypes.PC) {
  109                   if (isTypeUnjoinedSubclass(mapped.getElementMapping()))
  110                       throw new MetaDataException(_loc.get
  111                           ("mapped-inverse-unjoined", field.getName(),
  112                               field.getDefiningMapping(), mapped));
  113   
  114                   // warn the user about making the collection side the owner
  115                   Log log = field.getRepository().getLog();
  116                   if (log.isInfoEnabled())
  117                       log.info(_loc.get("coll-owner", field, mapped));
  118                   field.setForeignKey(mapped.getElementMapping().
  119                       getForeignKey());
  120               } else
  121                   throw new MetaDataException(_loc.get("not-inv-relation",
  122                       field, mapped));
  123   
  124               field.setUseClassCriteria(criteria);
  125               return;
  126           }
  127   
  128           // this is necessary to support openjpa 3 mappings, which didn't
  129           // differentiate between secondary table joins and relations built
  130           // around an inverse key: check to see if we're mapped as a secondary
  131           // table join but we're in the table of the related type, and if so
  132           // switch our join mapping info to our value mapping info
  133           String tableName = field.getMappingInfo().getTableName();
  134           Table table = field.getTypeMapping().getTable();
  135           ValueMappingInfo vinfo = field.getValueInfo();
  136           if (tableName != null && table != null
  137               && (tableName.equalsIgnoreCase(table.getName())
  138               || tableName.equalsIgnoreCase(table.getFullName()))) {
  139               vinfo.setJoinDirection(MappingInfo.JOIN_INVERSE);
  140               vinfo.setColumns(field.getMappingInfo().getColumns());
  141               field.getMappingInfo().setTableName(null);
  142               field.getMappingInfo().setColumns(null);
  143           }
  144   
  145           field.mapJoin(adapt, false);
  146           if (field.getTypeMapping().isMapped()) {
  147               ForeignKey fk = vinfo.getTypeJoin(field, field.getName(), true,
  148                   adapt);
  149               field.setForeignKey(fk);
  150               field.setColumnIO(vinfo.getColumnIO());
  151               if (vinfo.getJoinDirection() == vinfo.JOIN_INVERSE)
  152                   field.setJoinDirection(field.JOIN_INVERSE);
  153           } else
  154               RelationStrategies.mapRelationToUnmappedPC(field, field.getName(),
  155                   adapt);
  156   
  157           field.setUseClassCriteria(criteria);
  158           field.mapPrimaryKey(adapt);
  159           PrimaryKey pk = field.getTable().getPrimaryKey();
  160           if (field.isPrimaryKey()) {
  161               Column[] cols = field.getColumns();
  162               if (pk != null && (adapt || pk.isLogical()))
  163                   for (int i = 0; i < cols.length; i++)
  164                       pk.addColumn(cols[i]);
  165               for (int i = 0; i < cols.length; i++)
  166                   field.getDefiningMapping().setJoinable(cols[i], this);
  167           }
  168   
  169           // map constraints after pk so we don't re-index / re-unique pk col
  170           field.mapConstraints(field.getName(), adapt);
  171       }
  172   
  173       /**
  174        * Return whether our defining mapping is an unjoined subclass of
  175        * the type of the given value.
  176        */
  177       private boolean isTypeUnjoinedSubclass(ValueMapping mapped) {
  178           ClassMapping def = field.getDefiningMapping();
  179           for (; def != null; def = def.getJoinablePCSuperclassMapping())
  180               if (def == mapped.getTypeMapping())
  181                   return false;
  182           return true;
  183       }
  184   
  185       public void initialize() {
  186           field.setUsesIntermediate(true);
  187   
  188           ForeignKey fk = field.getForeignKey();
  189           if (fk == null)
  190               _fkOid = Boolean.TRUE;
  191           else if (field.getJoinDirection() != FieldMapping.JOIN_INVERSE)
  192               _fkOid = field.getTypeMapping().isForeignKeyObjectId(fk);
  193       }
  194   
  195       public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
  196           throws SQLException {
  197           if (field.getMappedBy() != null)
  198               return;
  199   
  200           OpenJPAStateManager rel = RelationStrategies.getStateManager
  201               (sm.fetchObjectField(field.getIndex()), store.getContext());
  202           if (field.getJoinDirection() == field.JOIN_INVERSE)
  203               updateInverse(sm, rel, store, rm);
  204           else {
  205               Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT);
  206               if (row != null)
  207                   field.setForeignKey(row, rel);
  208           }
  209       }
  210   
  211       public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
  212           throws SQLException {
  213           if (field.getMappedBy() != null)
  214               return;
  215   
  216           OpenJPAStateManager rel = RelationStrategies.getStateManager
  217               (sm.fetchObjectField(field.getIndex()), store.getContext());
  218   
  219           if (field.getJoinDirection() == field.JOIN_INVERSE) {
  220               nullInverse(sm, rm);
  221               updateInverse(sm, rel, store, rm);
  222           } else {
  223               Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE);
  224               if (row != null)
  225                   field.setForeignKey(row, rel);
  226           }
  227       }
  228   
  229       public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
  230           throws SQLException {
  231           if (field.getMappedBy() != null)
  232               return;
  233   
  234           if (field.getJoinDirection() == field.JOIN_INVERSE) {
  235               if (sm.getLoaded().get(field.getIndex())) {
  236                   OpenJPAStateManager rel = RelationStrategies.getStateManager(sm.
  237                       fetchObjectField(field.getIndex()), store.getContext());
  238                   updateInverse(sm, rel, store, rm);
  239               } else
  240                   nullInverse(sm, rm);
  241           } else {
  242               field.deleteRow(sm, store, rm);
  243   
  244               // if our foreign key has a delete action, we need to set the
  245               // related object so constraints can be evaluated
  246               OpenJPAStateManager rel = RelationStrategies.getStateManager
  247                   (sm.fetchObjectField(field.getIndex()), store.getContext());
  248               if (rel != null) {
  249                   ForeignKey fk = field.getForeignKey((ClassMapping)
  250                       rel.getMetaData());
  251                   if (fk.getDeleteAction() == ForeignKey.ACTION_RESTRICT) {
  252                       Row row = field.getRow(sm, store, rm, Row.ACTION_DELETE);
  253                       row.setForeignKey(fk, null, rel);
  254                   }
  255               }
  256           }
  257       }
  258   
  259       /**
  260        * Null inverse relations that reference the given object.
  261        */
  262       private void nullInverse(OpenJPAStateManager sm, RowManager rm)
  263           throws SQLException {
  264           if (field.getUseClassCriteria())
  265               return;
  266   
  267           ForeignKey fk = field.getForeignKey();
  268           ColumnIO io = field.getColumnIO();
  269           if (!io.isAnyUpdatable(fk, true))
  270               return;
  271   
  272           // null inverse if not already enforced by fk
  273           if (field.getIndependentTypeMappings().length != 1)
  274               throw RelationStrategies.uninversable(field);
  275           Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
  276           row.setForeignKey(fk, io, null);
  277           row.whereForeignKey(fk, sm);
  278           rm.flushAllRows(row);
  279       }
  280   
  281       /**
  282        * This method updates the inverse columns of our relation
  283        * with the given object.
  284        */
  285       private void updateInverse(OpenJPAStateManager sm, OpenJPAStateManager rel,
  286           JDBCStore store, RowManager rm)
  287           throws SQLException {
  288           if (rel == null)
  289               return;
  290   
  291           ForeignKey fk = field.getForeignKey();
  292           ColumnIO io = field.getColumnIO();
  293   
  294           int action;
  295           if (rel.isNew() && !rel.isFlushed()) {
  296               if (sm.isDeleted() || !io.isAnyInsertable(fk, false))
  297                   return;
  298               action = Row.ACTION_INSERT;
  299           } else if (rel.isDeleted()) {
  300               if (rel.isFlushed() || !sm.isDeleted())
  301                   return;
  302               action = Row.ACTION_DELETE;
  303           } else {
  304               if (sm.isDeleted())
  305                   sm = null;
  306               if (!io.isAnyUpdatable(fk, sm == null))
  307                   return;
  308               action = Row.ACTION_UPDATE;
  309           }
  310   
  311           if (field.getIndependentTypeMappings().length != 1)
  312               throw RelationStrategies.uninversable(field);
  313   
  314           // get the row for the inverse object; the row might be in a secondary
  315           // table if there is a field controlling the foreign key
  316           Row row = null;
  317           FieldMapping[] invs = field.getInverseMappings();
  318           for (int i = 0; i < invs.length; i++) {
  319               if (invs[i].getMappedByMetaData() == field
  320                   && invs[i].getTypeCode() == JavaTypes.PC) {
  321                   row = invs[i].getRow(rel, store, rm, action);
  322                   break;
  323               }
  324           }
  325           ClassMapping relMapping = field.getTypeMapping();
  326           if (row == null)
  327               row = rm.getRow(relMapping.getTable(), action, rel, true);
  328   
  329           // if this is an update, this might be the only mod to the row, so
  330           // make sure the where condition is set
  331           if (action == Row.ACTION_UPDATE
  332               && row.getTable() == relMapping.getTable())
  333               row.wherePrimaryKey(rel);
  334   
  335           // update the inverse pointer with our oid value
  336           row.setForeignKey(fk, io, sm);
  337       }
  338   
  339       public int supportsSelect(Select sel, int type, OpenJPAStateManager sm,
  340           JDBCStore store, JDBCFetchConfiguration fetch) {
  341           if (type == Select.TYPE_JOINLESS)
  342               return (field.getJoinDirection() != field.JOIN_INVERSE
  343                   && sel.isSelected(field.getTable())) ? 1 : 0;
  344           if (type == Select.TYPE_TWO_PART)
  345               return 1;
  346   
  347           // already cached?
  348           if (sm != null) {
  349               Object oid = sm.getIntermediate(field.getIndex());
  350               if (store.getContext().findCached(oid, null) != null)
  351                   return 0;
  352           }
  353   
  354           ClassMapping[] clss = field.getIndependentTypeMappings();
  355           switch (type) {
  356               case Select.EAGER_PARALLEL:
  357                   return clss.length;
  358               case Select.EAGER_OUTER:
  359                   return (clss.length == 1 && store.getDBDictionary().canOuterJoin
  360                       (sel.getJoinSyntax(), field.getForeignKey(clss[0]))) ? 1 :
  361                       0;
  362               case Select.EAGER_INNER:
  363                   return (clss.length == 1) ? 1 : 0;
  364               default:
  365                   return 0;
  366           }
  367       }
  368   
  369       public void selectEagerParallel(SelectExecutor sel,
  370           final OpenJPAStateManager sm, final JDBCStore store,
  371           final JDBCFetchConfiguration fetch, final int eagerMode) {
  372           final ClassMapping[] clss = field.getIndependentTypeMappings();
  373           if (!(sel instanceof Union))
  374               selectEagerParallel((Select) sel, clss[0], store, fetch, eagerMode);
  375           else {
  376               Union union = (Union) sel;
  377               if (fetch.getSubclassFetchMode (field.getTypeMapping()) 
  378                   != JDBCFetchConfiguration.EAGER_JOIN)
  379                   union.abortUnion();
  380               union.select(new Union.Selector() {
  381                   public void select(Select sel, int idx) {
  382                       selectEagerParallel(sel, clss[idx], store, fetch,
  383                           eagerMode);
  384                   }
  385               });
  386           }
  387       }
  388   
  389       /**
  390        * Perform an eager parallel select.
  391        */
  392       private void selectEagerParallel(Select sel, ClassMapping cls,
  393           JDBCStore store, JDBCFetchConfiguration fetch, int eagerMode) {
  394           sel.selectPrimaryKey(field.getDefiningMapping());
  395           // set a variable name that does not conflict with any in the query;
  396           // using a variable guarantees that the selected data will use different
  397           // aliases and joins than any existing WHERE conditions on this field
  398           // that might otherwise limit the relations that match
  399           Joins joins = sel.newJoins().setVariable("*");
  400           eagerJoin(joins, cls, true);
  401           sel.select(cls, field.getSelectSubclasses(), store, fetch, eagerMode, 
  402               joins);
  403       }
  404   
  405       public void selectEagerJoin(Select sel, OpenJPAStateManager sm,
  406           JDBCStore store, JDBCFetchConfiguration fetch, int eagerMode) {
  407           // limit the eager mode to single on recursive eager fetching b/c
  408           // at this point the select has been modified and an attempt to
  409           // clone it for a to-many eager select can result in a clone that
  410           // produces invalid SQL
  411           ClassMapping cls = field.getIndependentTypeMappings()[0];
  412           boolean forceInner = fetch.hasFetchInnerJoin(field.getFullName(false)) ?
  413                   true : false;
  414           sel.select(cls, field.getSelectSubclasses(), store, fetch,
  415               JDBCFetchConfiguration.EAGER_JOIN,
  416               eagerJoin(sel.newJoins(), cls, forceInner));
  417       }
  418   
  419       /**
  420        * Add the joins needed to select/load eager data.
  421        */
  422       private Joins eagerJoin(Joins joins, ClassMapping cls, boolean forceInner) {
  423           boolean inverse = field.getJoinDirection() == field.JOIN_INVERSE;
  424           if (!inverse) {
  425               joins = join(joins, false);
  426               joins = setEmbeddedVariable(joins);
  427           }
  428   
  429           // and join into relation
  430           ForeignKey fk = field.getForeignKey(cls);
  431           if (!forceInner && field.getNullValue() != FieldMapping.NULL_EXCEPTION)
  432               return joins.outerJoinRelation(field.getName(), fk, field.
  433                   getTypeMapping(), field.getSelectSubclasses(), inverse, false);
  434           return joins.joinRelation(field.getName(), fk, field.getTypeMapping(), 
  435               field.getSelectSubclasses(), inverse, false);
  436       }
  437   
  438       /**
  439        * If joining from an embedded owner, use variable to create a unique
  440        * alias in case owner contains other same-typed embedded relations.
  441        */
  442       private Joins setEmbeddedVariable(Joins joins) {
  443           if (field.getDefiningMetaData().getEmbeddingMetaData() == null)
  444               return joins;
  445           return joins.setVariable(field.getDefiningMetaData().
  446               getEmbeddingMetaData().getFieldMetaData().getName());
  447       }
  448   
  449       public int select(Select sel, OpenJPAStateManager sm, JDBCStore store,
  450           JDBCFetchConfiguration fetch, int eagerMode) {
  451           if (field.getJoinDirection() == field.JOIN_INVERSE)
  452               return -1;
  453           // already cached oid?
  454           if (sm != null && sm.getIntermediate(field.getIndex()) != null)
  455               return -1;
  456           if (!Boolean.TRUE.equals(_fkOid))
  457               return -1;
  458           sel.select(field.getColumns(), field.join(sel));
  459           return 0;
  460       }
  461   
  462       public Object loadEagerParallel(OpenJPAStateManager sm, JDBCStore store,
  463           JDBCFetchConfiguration fetch, Object res)
  464           throws SQLException {
  465           // process batched results if we haven't already
  466           Map rels;
  467           if (res instanceof Result)
  468               rels = processEagerParallelResult(sm, store, fetch, (Result) res);
  469           else
  470               rels = (Map) res;
  471   
  472           // store object for this oid in instance
  473           sm.storeObject(field.getIndex(), rels.remove(sm.getObjectId()));
  474           return rels;
  475       }
  476   
  477       /**
  478        * Process the given batched result.
  479        */
  480       private Map processEagerParallelResult(OpenJPAStateManager sm,
  481           JDBCStore store, JDBCFetchConfiguration fetch, Result res)
  482           throws SQLException {
  483           // do same joins as for load
  484           //### cheat: we know typical result joins only care about the relation
  485           //### path; thus we can ignore different mappings
  486           ClassMapping[] clss = field.getIndependentTypeMappings();
  487           Joins joins = res.newJoins().setVariable("*");
  488           eagerJoin(joins, clss[0], true);
  489   
  490           Map rels = new HashMap();
  491           ClassMapping owner = field.getDefiningMapping();
  492           ClassMapping cls;
  493           Object oid;
  494           while (res.next()) {
  495               cls = res.getBaseMapping();
  496               if (cls == null)
  497                   cls = clss[0];
  498               oid = owner.getObjectId(store, res, null, true, null);
  499               rels.put(oid, res.load(cls, store, fetch, joins));
  500           }
  501           res.close();
  502   
  503           return rels;
  504       }
  505   
  506       public void loadEagerJoin(OpenJPAStateManager sm, JDBCStore store,
  507           JDBCFetchConfiguration fetch, Result res)
  508           throws SQLException {
  509           ClassMapping cls = field.getIndependentTypeMappings()[0];
  510           sm.storeObject(field.getIndex(), res.load(cls, store, fetch,
  511               eagerJoin(res.newJoins(), cls, false)));
  512       }
  513   
  514       public void load(OpenJPAStateManager sm, JDBCStore store,
  515           JDBCFetchConfiguration fetch, Result res)
  516           throws SQLException {
  517           if (field.getJoinDirection() == field.JOIN_INVERSE)
  518               return;
  519           // cached oid?
  520           if (sm != null && sm.getIntermediate(field.getIndex()) != null)
  521               return;
  522           if (!Boolean.TRUE.equals(_fkOid))
  523               return;
  524           if (!res.containsAll(field.getColumns()))
  525               return;
  526   
  527           // get the related object's oid
  528           ClassMapping relMapping = field.getTypeMapping();
  529           Object oid = null;
  530           if (relMapping.isMapped()) {
  531               oid = relMapping.getObjectId(store, res, field.getForeignKey(),
  532                   field.getPolymorphic() != ValueMapping.POLY_FALSE, null);
  533           } else {
  534               Column[] cols = field.getColumns();
  535               if (relMapping.getIdentityType() == ClassMapping.ID_DATASTORE) {
  536                   long id = res.getLong(cols[0]);
  537                   if (!res.wasNull())
  538                       oid = store.newDataStoreId(id, relMapping, true);
  539               } else { 
  540                   // application id
  541                   if (cols.length == 1) {
  542                       Object val = res.getObject(cols[0], null, null);
  543                       if (val != null)
  544                           oid = ApplicationIds.fromPKValues(new Object[]{ val },
  545                               relMapping);
  546                   } else {
  547                       Object[] vals = new Object[cols.length];
  548                       for (int i = 0; i < cols.length; i++) {
  549                           vals[i] = res.getObject(cols[i], null, null);
  550                           if (vals[i] == null)
  551                               break;
  552                           if (i == cols.length - 1)
  553                               oid = ApplicationIds.fromPKValues(vals, relMapping);
  554                       }
  555                   }
  556               }
  557           }
  558   
  559           if (oid == null)
  560               sm.storeObject(field.getIndex(), null);
  561           else
  562               sm.setIntermediate(field.getIndex(), oid);
  563       }
  564   
  565       public void load(final OpenJPAStateManager sm, final JDBCStore store,
  566           final JDBCFetchConfiguration fetch)
  567           throws SQLException {
  568           // check for cached oid value, or load oid if no way to join
  569           if (Boolean.TRUE.equals(_fkOid)) {
  570               Object oid = sm.getIntermediate(field.getIndex());
  571               if (oid != null) {
  572                   Object val = store.find(oid, field, fetch);
  573                   sm.storeObject(field.getIndex(), val);
  574                   return;
  575               }
  576           }
  577   
  578           final ClassMapping[] rels = field.getIndependentTypeMappings();
  579           final int subs = field.getSelectSubclasses();
  580           final Joins[] resJoins = new Joins[rels.length];
  581   
  582           // select related mapping columns; joining from the related type
  583           // back to our fk table if not an inverse mapping (in which case we
  584           // can just make sure the inverse cols == our pk values)
  585           Union union = store.getSQLFactory().newUnion(rels.length);
  586           union.setExpectedResultCount(1, false);
  587           if (fetch.getSubclassFetchMode(field.getTypeMapping())
  588               != JDBCFetchConfiguration.EAGER_JOIN)
  589               union.abortUnion();
  590           union.select(new Union.Selector() {
  591               public void select(Select sel, int idx) {
  592                   if (field.getJoinDirection() == field.JOIN_INVERSE)
  593                       sel.whereForeignKey(field.getForeignKey(rels[idx]),
  594                           sm.getObjectId(), field.getDefiningMapping(), store);
  595                   else {
  596                       resJoins[idx] = sel.newJoins().joinRelation(field.getName(),
  597                           field.getForeignKey(rels[idx]), rels[idx],
  598                           field.getSelectSubclasses(), false, false);
  599                       field.wherePrimaryKey(sel, sm, store);
  600                   }
  601                   sel.select(rels[idx], subs, store, fetch, fetch.EAGER_JOIN, 
  602                       resJoins[idx]);
  603               }
  604           });
  605   
  606           Result res = union.execute(store, fetch);
  607           try {
  608               Object val = null;
  609               if (res.next())
  610                   val = res.load(rels[res.indexOf()], store, fetch,
  611                       resJoins[res.indexOf()]);
  612               sm.storeObject(field.getIndex(), val);
  613           } finally {
  614               res.close();
  615           }
  616       }
  617   
  618       public Object toDataStoreValue(Object val, JDBCStore store) {
  619           return RelationStrategies.toDataStoreValue(field, val, store);
  620       }
  621   
  622       public void appendIsNull(SQLBuffer sql, Select sel, Joins joins) {
  623           // if no inverse, just join to mapping's table (usually a no-op
  624           // because it'll be in the primary table) and see if fk cols are null;
  625           // if inverse, then we have to do a sub-select to see if any inverse
  626           // objects point back to this field's owner
  627           if (field.getJoinDirection() != field.JOIN_INVERSE) {
  628               //### probably need some sort of subselect here on fk constants
  629               joins = join(joins, false);
  630               Column[] cols = field.getColumns();
  631               if (cols.length == 0)
  632                   sql.append("1 <> 1");
  633               else
  634                   sql.append(sel.getColumnAlias(cols[0], joins)).
  635                       append(" IS ").appendValue(null, cols[0]);
  636           } else
  637               testInverseNull(sql, sel, joins, true);
  638       }
  639   
  640       public void appendIsNotNull(SQLBuffer sql, Select sel, Joins joins) {
  641           // if no inverse, just join to mapping's table (usually a no-op
  642           // because it'll be in the primary table) and see if fk cols aren't
  643           // null; if inverse, then we have to do a sub-select to see if any
  644           // inverse objects point back to this field's owner
  645           if (field.getJoinDirection() != field.JOIN_INVERSE) {
  646               //### probably need some sort of subselect here on fk constants
  647               joins = join(joins, false);
  648               Column[] cols = field.getColumns();
  649               if (cols.length == 0)
  650                   sql.append("1 = 1");
  651               else
  652                   sql.append(sel.getColumnAlias(cols[0], joins)).
  653                       append(" IS NOT ").appendValue(null, cols[0]);
  654           } else
  655               testInverseNull(sql, sel, joins, false);
  656       }
  657   
  658       /**
  659        * Append SQL for a sub-select testing whether an inverse object exists
  660        * for this relation.
  661        */
  662       private void testInverseNull(SQLBuffer sql, Select sel, Joins joins,
  663           boolean empty) {
  664           DBDictionary dict = field.getMappingRepository().getDBDictionary();
  665           dict.assertSupport(dict.supportsSubselect, "SupportsSubselect");
  666   
  667           if (field.getIndependentTypeMappings().length != 1)
  668               throw RelationStrategies.uninversable(field);
  669   
  670           if (empty)
  671               sql.append("0 = ");
  672           else
  673               sql.append("0 < ");
  674   
  675           ForeignKey fk = field.getForeignKey();
  676           ContainerFieldStrategy.appendJoinCount(sql, sel, joins, dict, field,
  677               fk);
  678       }
  679   
  680       public Joins join(Joins joins, boolean forceOuter) {
  681           // if we're not in an inverse object table join normally, otherwise
  682           // already traversed the relation; just join back to owner table
  683           if (field.getJoinDirection() != field.JOIN_INVERSE)
  684               return field.join(joins, forceOuter, false);
  685           ClassMapping[] clss = field.getIndependentTypeMappings();
  686           if (clss.length != 1)
  687               throw RelationStrategies.uninversable(field);
  688           if (forceOuter)
  689               return joins.outerJoinRelation(field.getName(),
  690                   field.getForeignKey(), clss[0], field.getSelectSubclasses(), 
  691                   true, false);
  692           return joins.joinRelation(field.getName(), field.getForeignKey(),
  693               clss[0], field.getSelectSubclasses(), true, false);
  694       }
  695   
  696       public Joins joinRelation(Joins joins, boolean forceOuter,
  697           boolean traverse) {
  698           // if this is an inverse mapping it's already joined to the relation
  699           if (field.getJoinDirection() == field.JOIN_INVERSE)
  700               return joins;
  701           ClassMapping[] clss = field.getIndependentTypeMappings();
  702           if (clss.length != 1) {
  703               if (traverse)
  704                   throw RelationStrategies.unjoinable(field);
  705               return joins;
  706           }
  707   
  708           joins = setEmbeddedVariable(joins);
  709           if (forceOuter)
  710               return joins.outerJoinRelation(field.getName(), 
  711                   field.getForeignKey(clss[0]), clss[0], 
  712                   field.getSelectSubclasses(), false, false);
  713           return joins.joinRelation(field.getName(), field.getForeignKey(clss[0]),
  714               clss[0], field.getSelectSubclasses(), false, false);
  715       }
  716   
  717       ///////////////////////////
  718       // Joinable implementation
  719       ///////////////////////////
  720   
  721       public int getFieldIndex() {
  722           return field.getIndex();
  723       }
  724   
  725       public Object getPrimaryKeyValue(Result res, Column[] cols, ForeignKey fk,
  726           JDBCStore store, Joins joins)
  727           throws SQLException {
  728           ClassMapping relmapping = field.getTypeMapping();
  729           if (relmapping.getIdentityType() == ClassMapping.ID_DATASTORE) {
  730               Column col = cols[0];
  731               if (fk != null)
  732                   col = fk.getColumn(col);   
  733               long id = res.getLong(col, joins);
  734               if (field.getObjectIdFieldTypeCode() == JavaTypes.LONG)
  735                   return Numbers.valueOf(id);
  736               return store.newDataStoreId(id, relmapping, field.getPolymorphic() 
  737                   != ValueMapping.POLY_FALSE);
  738           }
  739   
  740           if (relmapping.isOpenJPAIdentity())
  741               return ((Joinable) relmapping.getPrimaryKeyFieldMappings()[0].
  742                   getStrategy()).getPrimaryKeyValue(res, cols, fk, store, joins);
  743   
  744           if (cols == getColumns() && fk == null)
  745               fk = field.getForeignKey();
  746           else
  747               fk = createTranslatingForeignKey(relmapping, cols, fk); 
  748           return relmapping.getObjectId(store, res, fk,
  749               field.getPolymorphic() != ValueMapping.POLY_FALSE, joins);
  750       }
  751   
  752       /**
  753        * Create a faux foreign key that translates between the columns to pull
  754        * the data from and our related type's primary key columns.
  755        */
  756       private ForeignKey createTranslatingForeignKey(ClassMapping relmapping,
  757           Column[] gcols, ForeignKey gfk) {
  758           ForeignKey fk = field.getForeignKey(); 
  759           Column[] cols = fk.getColumns();
  760   
  761           ForeignKey tfk = null;
  762           Column tcol;
  763           for (int i = 0; i < gcols.length; i++) {
  764               tcol = gcols[i];
  765               if (gfk != null)
  766                   tcol = gfk.getColumn(tcol);
  767               if (tfk == null)
  768                   tfk = new ForeignKey(null, tcol.getTable());
  769               tfk.join(tcol, fk.getPrimaryKeyColumn(cols[i]));
  770           }
  771           return tfk;
  772       }
  773   
  774       public Object getJoinValue(Object fieldVal, Column col, JDBCStore store) {
  775           Object o = field.getForeignKey().getConstant(col);
  776           if (o != null)
  777               return o;
  778           col = field.getForeignKey().getPrimaryKeyColumn(col);
  779           if (col == null)
  780               throw new InternalException();
  781   
  782           ClassMapping relmapping = field.getTypeMapping();
  783           Joinable j = field.getTypeMapping().assertJoinable(col);
  784           if (ImplHelper.isManageable(fieldVal))
  785               fieldVal = store.getContext().getObjectId(fieldVal);
  786           if (fieldVal instanceof OpenJPAId)
  787               fieldVal = ((OpenJPAId) fieldVal).getIdObject();
  788           else if (relmapping.getObjectIdType() != null
  789               && relmapping.getObjectIdType().isInstance(fieldVal)) {
  790               Object[] pks = ApplicationIds.toPKValues(fieldVal, relmapping);
  791               fieldVal = pks[relmapping.getField(j.getFieldIndex()).
  792                   getPrimaryKeyIndex()];
  793           }
  794           return j.getJoinValue(fieldVal, col, store);
  795       }
  796   
  797       public Object getJoinValue(OpenJPAStateManager sm, Column col,
  798           JDBCStore store) {
  799           return getJoinValue(sm.fetch(field.getIndex()), col, store);
  800       }
  801   
  802       public void setAutoAssignedValue(OpenJPAStateManager sm, JDBCStore store,
  803           Column col, Object autoInc) {
  804           throw new UnsupportedException();
  805       }
  806   
  807       /////////////////////////////
  808       // Embeddable implementation
  809       /////////////////////////////
  810   
  811       public Column[] getColumns() {
  812           return field.getColumns();
  813       }
  814   
  815       public ColumnIO getColumnIO() {
  816           return field.getColumnIO();
  817       }
  818   
  819       public Object[] getResultArguments() {
  820           return null;
  821       }
  822   
  823       public Object toEmbeddedDataStoreValue(Object val, JDBCStore store) {
  824           return toDataStoreValue(val, store);
  825       }
  826   
  827       public Object toEmbeddedObjectValue(Object val) {
  828           return UNSUPPORTED;
  829       }
  830   
  831       public void loadEmbedded(OpenJPAStateManager sm, JDBCStore store,
  832           JDBCFetchConfiguration fetch, Object val)
  833           throws SQLException {
  834           ClassMapping relMapping = field.getTypeMapping();
  835           Object oid;
  836           if (val == null)
  837               oid = null;
  838           else if (relMapping.getIdentityType() == ClassMapping.ID_DATASTORE)
  839               oid = store.newDataStoreId(((Number) val).longValue(), relMapping,
  840                   field.getPolymorphic() != ValueMapping.POLY_FALSE);
  841           else {
  842               Object[] pks = (getColumns().length == 1) ? new Object[]{ val }
  843                   : (Object[]) val;
  844               boolean nulls = true;
  845               for (int i = 0; nulls && i < pks.length; i++)
  846                   nulls = pks[i] == null;
  847               if (nulls)
  848                   oid = null;
  849               else {
  850                   oid = ApplicationIds.fromPKValues(pks, relMapping);
  851                   if (field.getPolymorphic() == ValueMapping.POLY_FALSE
  852                       && oid instanceof OpenJPAId) {
  853                       ((OpenJPAId) oid).setManagedInstanceType(relMapping.
  854                           getDescribedType());
  855                   }
  856               }
  857           }
  858   
  859           if (oid == null)
  860               sm.storeObject(field.getIndex(), null);
  861           else
  862               sm.setIntermediate(field.getIndex(), oid);
  863       }
  864   }

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