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.Collection;
   23   import java.util.Iterator;
   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.FieldMapping;
   29   import org.apache.openjpa.jdbc.meta.FieldMappingInfo;
   30   import org.apache.openjpa.jdbc.meta.ValueMapping;
   31   import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
   32   import org.apache.openjpa.jdbc.schema.Column;
   33   import org.apache.openjpa.jdbc.schema.ColumnIO;
   34   import org.apache.openjpa.jdbc.schema.ForeignKey;
   35   import org.apache.openjpa.jdbc.sql.Joins;
   36   import org.apache.openjpa.jdbc.sql.Result;
   37   import org.apache.openjpa.jdbc.sql.Row;
   38   import org.apache.openjpa.jdbc.sql.RowManager;
   39   import org.apache.openjpa.jdbc.sql.Select;
   40   import org.apache.openjpa.kernel.OpenJPAStateManager;
   41   import org.apache.openjpa.kernel.StoreContext;
   42   import org.apache.openjpa.lib.log.Log;
   43   import org.apache.openjpa.lib.util.Localizer;
   44   import org.apache.openjpa.meta.JavaTypes;
   45   import org.apache.openjpa.util.ChangeTracker;
   46   import org.apache.openjpa.util.InternalException;
   47   import org.apache.openjpa.util.MetaDataException;
   48   import org.apache.openjpa.util.Proxies;
   49   import org.apache.openjpa.util.Proxy;
   50   
   51   /**
   52    * Maps a relation to a set of other objects using an inverse
   53    * foreign key in the related object table.
   54    *
   55    * @author Abe White
   56    */
   57   public abstract class RelationToManyInverseKeyFieldStrategy
   58       extends StoreCollectionFieldStrategy {
   59   
   60       private static final Localizer _loc = Localizer.forPackage
   61           (RelationToManyInverseKeyFieldStrategy.class);
   62   
   63       private boolean _orderInsert = false;
   64       private boolean _orderUpdate = false;
   65   
   66       protected ClassMapping[] getIndependentElementMappings(boolean traverse) {
   67           return field.getElementMapping().getIndependentTypeMappings();
   68       }
   69   
   70       protected ForeignKey getJoinForeignKey(ClassMapping elem) {
   71           return field.getElementMapping().getForeignKey(elem);
   72       }
   73   
   74       protected void selectElement(Select sel, ClassMapping elem,
   75           JDBCStore store, JDBCFetchConfiguration fetch, int eagerMode,
   76           Joins joins) {
   77           sel.select(elem, field.getElementMapping().getSelectSubclasses(),
   78               store, fetch, eagerMode, joins);
   79       }
   80   
   81       protected Object loadElement(OpenJPAStateManager sm, JDBCStore store,
   82           JDBCFetchConfiguration fetch, Result res, Joins joins)
   83           throws SQLException {
   84           ClassMapping elem = res.getBaseMapping();
   85           if (elem == null)
   86               elem = field.getElementMapping().getIndependentTypeMappings()[0];
   87           return res.load(elem, store, fetch, joins);
   88       }
   89   
   90       protected Joins join(Joins joins, ClassMapping elem) {
   91           ValueMapping vm = field.getElementMapping();
   92           ForeignKey fk = vm.getForeignKey(elem);
   93           ClassMapping owner = field.getDefiningMapping();
   94           while (fk.getPrimaryKeyTable() != owner.getTable()) {
   95               joins = owner.joinSuperclass(joins, false);
   96               owner = owner.getJoinablePCSuperclassMapping(); 
   97               if (owner == null)
   98                   throw new InternalException();
   99           }
  100           return joins.joinRelation(field.getName(), fk, elem, 
  101               vm.getSelectSubclasses(), true, true);
  102       }
  103   
  104       protected Joins joinElementRelation(Joins joins, ClassMapping elem) {
  105           return joinRelation(joins, false, false);
  106       }
  107   
  108       public void map(boolean adapt) {
  109           field.getValueInfo().assertNoSchemaComponents(field, !adapt);
  110           field.getKeyMapping().getValueInfo().assertNoSchemaComponents
  111               (field.getKey(), !adapt);
  112   
  113           ValueMapping elem = field.getElementMapping();
  114           if (elem.getTypeCode() != JavaTypes.PC || elem.isEmbeddedPC()
  115               || !elem.getTypeMapping().isMapped())
  116               throw new MetaDataException(_loc.get("not-elem-relation", field));
  117   
  118           // check for named inverse
  119           FieldMapping mapped = field.getMappedByMapping();
  120           FieldMappingInfo finfo = field.getMappingInfo();
  121           ValueMappingInfo vinfo = elem.getValueInfo();
  122           boolean criteria = vinfo.getUseClassCriteria();
  123           if (mapped != null) {
  124               mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING);
  125               if (!(mapped.getStrategy() instanceof RelationFieldStrategy))
  126                   throw new MetaDataException(_loc.get("not-inv-relation",
  127                       field, mapped));
  128               vinfo.assertNoSchemaComponents(elem, !adapt);
  129               elem.setForeignKey(mapped.getForeignKey
  130                   (field.getDefiningMapping()));
  131               elem.setColumns(mapped.getDefiningMapping().
  132                   getPrimaryKeyColumns());
  133               elem.setJoinDirection(ValueMapping.JOIN_EXPECTED_INVERSE);
  134               elem.setUseClassCriteria(criteria);
  135   
  136               field.setOrderColumn(finfo.getOrderColumn(field,
  137                   mapped.getForeignKey().getTable(), adapt));
  138               field.setOrderColumnIO(finfo.getColumnIO());
  139               return;
  140           }
  141   
  142           // map inverse foreign key in related table
  143           ForeignKey fk = vinfo.getInverseTypeJoin(elem, field.getName(), adapt);
  144           elem.setForeignKey(fk);
  145           elem.setColumnIO(vinfo.getColumnIO());
  146           elem.setColumns(elem.getTypeMapping().getPrimaryKeyColumns());
  147           elem.setJoinDirection(ValueMapping.JOIN_EXPECTED_INVERSE);
  148           elem.setUseClassCriteria(criteria);
  149           elem.mapConstraints(field.getName(), adapt);
  150   
  151           field.setOrderColumn(finfo.getOrderColumn(field, fk.getTable(),
  152               adapt));
  153           field.setOrderColumnIO(finfo.getColumnIO());
  154       }
  155   
  156       public void initialize() {
  157           Column order = field.getOrderColumn();
  158           _orderInsert = field.getOrderColumnIO().isInsertable(order, false);
  159           _orderUpdate = field.getOrderColumnIO().isUpdatable(order, false);
  160   
  161           ValueMapping elem = field.getElementMapping();
  162           Log log = field.getRepository().getLog();
  163           if (field.getMappedBy() == null
  164               && elem.getUseClassCriteria() && log.isWarnEnabled()) {
  165               ForeignKey fk = elem.getForeignKey();
  166               if (elem.getColumnIO().isAnyUpdatable(fk, false))
  167                   log.warn(_loc.get("class-crit-owner", field));
  168           }
  169       }
  170   
  171       public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
  172           throws SQLException {
  173           if (field.getMappedBy() == null || _orderInsert || _orderUpdate)
  174               insert(sm, rm, sm.fetchObject(field.getIndex()));
  175       }
  176   
  177       private void insert(OpenJPAStateManager sm, RowManager rm, Object vals)
  178           throws SQLException {
  179           if (field.getMappedBy() != null && !_orderInsert && !_orderUpdate)
  180               return;
  181           Collection coll = toCollection(vals);
  182           if (coll == null || coll.isEmpty())
  183               return;
  184   
  185           ClassMapping rel = field.getElementMapping().getTypeMapping();
  186           int idx = 0;
  187           for (Iterator itr = coll.iterator(); itr.hasNext(); idx++)
  188               updateInverse(sm.getContext(), itr.next(), rel, rm, sm, idx);
  189       }
  190   
  191       public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
  192           throws SQLException {
  193           if (field.getMappedBy() != null && !_orderInsert && !_orderUpdate)
  194               return;
  195   
  196           Object obj = sm.fetchObject(field.getIndex());
  197           ChangeTracker ct = null;
  198           if (obj instanceof Proxy) {
  199               Proxy proxy = (Proxy) obj;
  200               if (Proxies.isOwner(proxy, sm, field.getIndex()))
  201                   ct = proxy.getChangeTracker();
  202           }
  203   
  204           // if no fine-grained change tracking then just delete and reinsert
  205           if (ct == null || !ct.isTracking()) {
  206               delete(sm, store, rm);
  207               insert(sm, rm, obj);
  208               return;
  209           }
  210   
  211           // null inverse columns for deletes and update them with our oid for
  212           // inserts
  213           ClassMapping rel = field.getElementMapping().getTypeMapping();
  214           StoreContext ctx = store.getContext();
  215           if (field.getMappedBy() == null) {
  216               Collection rem = ct.getRemoved();
  217               for (Iterator itr = rem.iterator(); itr.hasNext();)
  218                   updateInverse(ctx, itr.next(), rel, rm, null, 0);
  219           }
  220   
  221           Collection add = ct.getAdded();
  222           int seq = ct.getNextSequence();
  223           for (Iterator itr = add.iterator(); itr.hasNext(); seq++)
  224               updateInverse(ctx, itr.next(), rel, rm, sm, seq);
  225           if (field.getOrderColumn() != null)
  226               ct.setNextSequence(seq);
  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 nullable, null any existing inverse columns that refer to this obj
  235           ValueMapping elem = field.getElementMapping();
  236           ColumnIO io = elem.getColumnIO();
  237           ForeignKey fk = elem.getForeignKey();
  238           if (!elem.getUseClassCriteria() && io.isAnyUpdatable(fk, true)) { 
  239               assertInversable();
  240               Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
  241               row.setForeignKey(fk, io, null);
  242               row.whereForeignKey(fk, sm);
  243               rm.flushAllRows(row);
  244               return;
  245           }
  246   
  247           if (!sm.getLoaded().get(field.getIndex()))
  248               return;
  249   
  250           // update fk on each field value row
  251           ClassMapping rel = field.getElementMapping().getTypeMapping();
  252           StoreContext ctx = store.getContext();
  253           Collection objs = toCollection(sm.fetchObject(field.getIndex()));
  254           if (objs != null && !objs.isEmpty())
  255               for (Iterator itr = objs.iterator(); itr.hasNext();)
  256                   updateInverse (ctx, itr.next(), rel, rm, sm, 0);
  257       }
  258   
  259       /**
  260        * This method updates the inverse columns of a 1-M related object
  261        * with the given oid.
  262        */
  263       private void updateInverse(StoreContext ctx, Object inverse,
  264           ClassMapping rel, RowManager rm, OpenJPAStateManager sm, int idx)
  265           throws SQLException {
  266           OpenJPAStateManager invsm = RelationStrategies.getStateManager(inverse,
  267               ctx);
  268           if (invsm == null)
  269               return;
  270   
  271           ValueMapping elem = field.getElementMapping();
  272           ForeignKey fk = elem.getForeignKey();
  273           ColumnIO io = elem.getColumnIO();
  274           Column order = field.getOrderColumn();
  275   
  276           int action;
  277           boolean writeable;
  278           boolean orderWriteable;
  279           if (invsm.isNew() && !invsm.isFlushed()) {
  280               // no need to null inverse columns of new instance
  281               if (sm == null || sm.isDeleted())
  282                   return;
  283               writeable = io.isAnyInsertable(fk, false);
  284               orderWriteable = _orderInsert;
  285               action = Row.ACTION_INSERT;
  286           } else if (invsm.isDeleted()) {
  287               // no need to null inverse columns of deleted instance
  288               if (invsm.isFlushed() || sm == null || !sm.isDeleted())
  289                   return;
  290               writeable = true;
  291               orderWriteable = false;
  292               action = Row.ACTION_DELETE;
  293           } else {
  294               if (sm != null && sm.isDeleted())
  295                   sm = null;
  296               writeable = io.isAnyUpdatable(fk, sm == null);
  297               orderWriteable = field.getOrderColumnIO().isUpdatable
  298                   (order, sm == null);
  299               action = Row.ACTION_UPDATE;
  300           }
  301           if (!writeable && !orderWriteable)
  302               return;
  303   
  304           assertInversable();
  305   
  306           // if this is an update, this might be the only mod to the row, so
  307           // make sure the where condition is set
  308           Row row = rm.getRow(fk.getTable(), action, invsm, true);
  309           if (action == Row.ACTION_UPDATE)
  310               row.wherePrimaryKey(invsm);
  311   
  312           // update the inverse pointer with our oid value
  313           if (writeable)
  314               row.setForeignKey(fk, io, sm);
  315           if (orderWriteable)
  316               row.setInt(order, idx);
  317       }
  318   
  319       public Object toDataStoreValue(Object val, JDBCStore store) {
  320           ClassMapping cm = field.getElementMapping().getTypeMapping();
  321           return cm.toDataStoreValue(val, cm.getPrimaryKeyColumns(), store);
  322       }
  323   
  324       public Joins join(Joins joins, boolean forceOuter) {
  325           ValueMapping elem = field.getElementMapping();
  326           ClassMapping[] clss = elem.getIndependentTypeMappings();
  327           if (clss.length != 1)
  328               throw RelationStrategies.unjoinable(elem);
  329           if (forceOuter)
  330               return joins.outerJoinRelation(field.getName(), 
  331                   elem.getForeignKey(clss[0]), clss[0],
  332                   elem.getSelectSubclasses(), true, true);
  333           return joins.joinRelation(field.getName(), elem.getForeignKey(clss[0]),
  334               clss[0], elem.getSelectSubclasses(), true, true);
  335       }
  336   
  337       private void assertInversable() {
  338           ValueMapping elem = field.getElementMapping();
  339           if (elem.getIndependentTypeMappings().length != 1)
  340               throw RelationStrategies.uninversable(elem);
  341       }
  342   }

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