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.SQLException;
   23   import java.util.ArrayList;
   24   import java.util.Collection;
   25   import java.util.Iterator;
   26   
   27   import org.apache.openjpa.jdbc.schema.ForeignKey;
   28   import org.apache.openjpa.jdbc.sql.PrimaryRow;
   29   import org.apache.openjpa.jdbc.sql.Row;
   30   import org.apache.openjpa.jdbc.sql.RowImpl;
   31   import org.apache.openjpa.jdbc.sql.RowManager;
   32   import org.apache.openjpa.jdbc.sql.RowManagerImpl;
   33   import org.apache.openjpa.jdbc.sql.SQLExceptions;
   34   import org.apache.openjpa.kernel.OpenJPAStateManager;
   35   
   36   /**
   37    * Update manager that writes SQL in object-level operation order.
   38    *
   39    * @author Abe White
   40    */
   41   public class OperationOrderUpdateManager
   42       extends AbstractUpdateManager {
   43   
   44       public boolean orderDirty() {
   45           return true;
   46       }
   47   
   48       protected RowManager newRowManager() {
   49           return new RowManagerImpl(true);
   50       }
   51   
   52       protected PreparedStatementManager newPreparedStatementManager
   53           (JDBCStore store, Connection conn) {
   54           return new PreparedStatementManagerImpl(store, conn);
   55       }
   56   
   57       protected Collection flush(RowManager rowMgr,
   58           PreparedStatementManager psMgr, Collection exceps) {
   59           RowManagerImpl rmimpl = (RowManagerImpl) rowMgr;
   60   
   61           // first take care of all secondary table deletes and 'all row' deletes
   62           // (which are probably secondary table deletes), since no foreign
   63           // keys ever rely on secondary table pks
   64           flush(rmimpl.getAllRowDeletes(), psMgr);
   65           flush(rmimpl.getSecondaryDeletes(), psMgr);
   66   
   67           // now do any 'all row' updates, which typically null keys
   68           flush(rmimpl.getAllRowUpdates(), psMgr);
   69   
   70           // gather any updates we need to avoid fk constraints on deletes
   71           Collection constraintUpdates = null;
   72           for (Iterator itr = rmimpl.getDeletes().iterator(); itr.hasNext();) {
   73               try {
   74                   constraintUpdates = analyzeDeleteConstraints(rmimpl,
   75                       (PrimaryRow) itr.next(), constraintUpdates);
   76               } catch (SQLException se) {
   77                   exceps = addException(exceps, SQLExceptions.getStore
   78                       (se, dict));
   79               }
   80           }
   81           if (constraintUpdates != null) {
   82               flush(constraintUpdates, psMgr);
   83               constraintUpdates.clear();
   84           }
   85   
   86           // flush primary rows in order
   87           for (Iterator itr = rmimpl.getOrdered().iterator(); itr.hasNext();) {
   88               try {
   89                   constraintUpdates = flushPrimaryRow(rmimpl, (PrimaryRow)
   90                       itr.next(), psMgr, constraintUpdates);
   91               } catch (SQLException se) {
   92                   exceps = addException(exceps, SQLExceptions.getStore
   93                       (se, dict));
   94               }
   95           }
   96           if (constraintUpdates != null)
   97               flush(constraintUpdates, psMgr);
   98   
   99           // take care of all secondary table inserts and updates last, since
  100           // they may rely on previous inserts or updates, but nothing relies
  101           // on them
  102           flush(rmimpl.getSecondaryUpdates(), psMgr);
  103   
  104           // flush any left over prepared statements
  105           psMgr.flush();
  106           return exceps;
  107       }
  108   
  109       /**
  110        * Analyze the delete constraints on the given row, gathering necessary
  111        * updates to null fks before deleting.
  112        */
  113       private Collection analyzeDeleteConstraints(RowManagerImpl rowMgr,
  114           PrimaryRow row, Collection updates)
  115           throws SQLException {
  116           if (!row.isValid())
  117               return updates;
  118   
  119           ForeignKey[] fks = row.getTable().getForeignKeys();
  120           OpenJPAStateManager sm;
  121           PrimaryRow rel;
  122           RowImpl update;
  123           for (int i = 0; i < fks.length; i++) {
  124               // when deleting ref fks we set the where value instead
  125               sm = row.getForeignKeySet(fks[i]);
  126               if (sm == null)
  127                   sm = row.getForeignKeyWhere(fks[i]);
  128               if (sm == null)
  129                   continue;
  130   
  131               // only need an update if we have an fk to a row that's being
  132               // deleted before we are
  133               rel = (PrimaryRow) rowMgr.getRow(fks[i].getPrimaryKeyTable(),
  134                   Row.ACTION_DELETE, sm, false);
  135               if (rel == null || !rel.isValid()
  136                   || rel.getIndex() >= row.getIndex())
  137                   continue;
  138   
  139               // create an update to null the offending fk before deleting.  use
  140               // a primary row to be sure to copy delayed-flush pks/fks
  141               update = new PrimaryRow(row.getTable(), Row.ACTION_UPDATE, null);
  142               row.copyInto(update, true);
  143               update.setForeignKey(fks[i], row.getForeignKeyIO(fks[i]), null);
  144               if (updates == null)
  145                   updates = new ArrayList();
  146               updates.add(update);
  147           }
  148           return updates;
  149       }
  150   
  151       /**
  152        * Flush the given row, creating deferred updates for dependencies.
  153        */
  154       private Collection flushPrimaryRow(RowManagerImpl rowMgr, PrimaryRow row,
  155           PreparedStatementManager psMgr, Collection updates)
  156           throws SQLException {
  157           if (!row.isValid())
  158               return updates;
  159   
  160           // already analyzed deletes
  161           if (row.getAction() == Row.ACTION_DELETE) {
  162               psMgr.flush(row);
  163               return updates;
  164           }
  165   
  166           ForeignKey[] fks = row.getTable().getForeignKeys();
  167           OpenJPAStateManager sm;
  168           PrimaryRow rel;
  169           PrimaryRow update;
  170           for (int i = 0; i < fks.length; i++) {
  171               sm = row.getForeignKeySet(fks[i]);
  172               if (sm == null)
  173                   continue;
  174   
  175               // only need an update if we have an fk to a row that's being
  176               // inserted after we are; if row is dependent on itself and no
  177               // fk, must be an auto-inc because otherwise we wouldn't have
  178               // recorded it
  179               rel = (PrimaryRow) rowMgr.getRow(fks[i].getPrimaryKeyTable(),
  180                   Row.ACTION_INSERT, sm, false);
  181               if (rel == null || !rel.isValid()
  182                   || rel.getIndex() < row.getIndex()
  183                   || (rel == row && !fks[i].isDeferred() && !fks[i].isLogical()))
  184                   continue;
  185   
  186               // don't insert or update with the given fk; create a deferred
  187               // update for after the rel row has been inserted; use a primary row
  188               // to prevent setting values until after flush to get auto-inc
  189               update = new PrimaryRow(row.getTable(), Row.ACTION_UPDATE, null);
  190               if (row.getAction() == Row.ACTION_INSERT)
  191                   update.wherePrimaryKey(row.getPrimaryKey());
  192               else
  193                   row.copyInto(update, true);
  194               update.setForeignKey(fks[i], row.getForeignKeyIO(fks[i]), sm);
  195               row.clearForeignKey(fks[i]);
  196   
  197               if (updates == null)
  198                   updates = new ArrayList();
  199               updates.add(update);
  200           }
  201   
  202           if (row.isValid()) // if update, maybe no longer needed
  203               psMgr.flush(row);
  204           return updates;
  205       }
  206   
  207       /**
  208        * Flush the given collection of secondary rows.
  209        */
  210       protected void flush(Collection rows, PreparedStatementManager psMgr) {
  211           if (rows.isEmpty())
  212               return;
  213   
  214           RowImpl row;
  215           for (Iterator itr = rows.iterator(); itr.hasNext();) {
  216               row = (RowImpl) itr.next();
  217               if (row.isValid())
  218                   psMgr.flush(row);
  219           }
  220       }
  221   }

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