You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2015/01/25 11:53:06 UTC

[2/4] cayenne git commit: Cleanup

Cleanup

* deleting internally used QueryResult class - it is not really needed,
  and we'll reuse its name for the new response object per separate Jiras


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/9f92e346
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/9f92e346
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/9f92e346

Branch: refs/heads/master
Commit: 9f92e34602d41080b4d832371466df73829798d6
Parents: ce73165
Author: aadamchik <aa...@apache.org>
Authored: Sat Jan 24 11:46:08 2015 +0100
Committer: aadamchik <aa...@apache.org>
Committed: Sat Jan 24 10:52:06 2015 -0500

----------------------------------------------------------------------
 .../org/apache/cayenne/access/DataPort.java     | 533 +++++++++----------
 .../cayenne/access/OperationObserver.java       |   4 +-
 .../org/apache/cayenne/access/QueryResult.java  | 267 ----------
 .../access/util/DoNothingOperationObserver.java |  80 +++
 4 files changed, 348 insertions(+), 536 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/9f92e346/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
index 3ec8b63..0691124 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import org.apache.cayenne.CayenneException;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ResultIterator;
+import org.apache.cayenne.access.util.DoNothingOperationObserver;
 import org.apache.cayenne.access.util.IteratedSelectObserver;
 import org.apache.cayenne.ashwood.AshwoodEntitySorter;
 import org.apache.cayenne.map.DbEntity;
@@ -59,271 +60,269 @@ import org.apache.cayenne.query.SelectQuery;
 @Deprecated
 public class DataPort {
 
-    public static final int INSERT_BATCH_SIZE = 1000;
-
-    protected DataNode sourceNode;
-    protected DataNode destinationNode;
-    protected Collection entities;
-    protected boolean cleaningDestination;
-    protected DataPortDelegate delegate;
-    protected int insertBatchSize;
-
-    public DataPort() {
-        this.insertBatchSize = INSERT_BATCH_SIZE;
-    }
-
-    /**
-     * Creates a new DataPort instance, setting its delegate.
-     */
-    public DataPort(DataPortDelegate delegate) {
-        this.delegate = delegate;
-    }
-
-    /**
-     * Runs DataPort. The instance must be fully configured by the time this
-     * method is invoked, having its delegate, source and destinatio nodes, and
-     * a list of entities set up.
-     */
-    public void execute() throws CayenneException {
-        // sanity check
-        if (sourceNode == null) {
-            throw new CayenneException("Can't port data, source node is null.");
-        }
-
-        if (destinationNode == null) {
-            throw new CayenneException("Can't port data, destination node is null.");
-        }
-
-        // the simple equality check may actually detect problems with
-        // misconfigred nodes
-        // it is not as dumb as it may look at first
-        if (sourceNode == destinationNode) {
-            throw new CayenneException("Can't port data, source and target nodes are the same.");
-        }
-
-        if (entities == null || entities.isEmpty()) {
-            return;
-        }
-
-        // sort entities for insertion
-        List sorted = new ArrayList(entities);
-        EntitySorter sorter = new AshwoodEntitySorter();
-        sorter.setEntityResolver(new EntityResolver(destinationNode.getDataMaps()));
-        sorter.sortDbEntities(sorted, false);
-
-        if (cleaningDestination) {
-            // reverse insertion order for deletion
-            List entitiesInDeleteOrder = new ArrayList(sorted.size());
-            entitiesInDeleteOrder.addAll(sorted);
-            Collections.reverse(entitiesInDeleteOrder);
-            processDelete(entitiesInDeleteOrder);
-        }
-
-        processInsert(sorted);
-    }
-
-    /**
-     * Cleans up destination tables data.
-     */
-    protected void processDelete(List entities) {
-        // Allow delegate to modify the list of entities
-        // any way it wants. For instance delegate may filter
-        // or sort the list (though it doesn't have to, and can simply
-        // pass through the original list).
-        if (delegate != null) {
-            entities = delegate.willCleanData(this, entities);
-        }
-
-        if (entities == null || entities.isEmpty()) {
-            return;
-        }
-
-        // Using QueryResult as observer for the data cleanup.
-        // This allows to collect query statistics and pass it to the delegate.
-        QueryResult observer = new QueryResult();
-
-        // Delete data from entities one by one
-        Iterator it = entities.iterator();
-        while (it.hasNext()) {
-            DbEntity entity = (DbEntity) it.next();
-
-            Query query = new SQLTemplate(entity, "DELETE FROM " + entity.getFullyQualifiedName());
-
-            // notify delegate that delete is about to happen
-            if (delegate != null) {
-                query = delegate.willCleanData(this, entity, query);
-            }
-
-            // perform delete query
-            observer.clear();
-            destinationNode.performQueries(Collections.singletonList(query), observer);
-
-            // notify delegate that delete just happened
-            if (delegate != null) {
-                // observer will store query statistics
-                int count = observer.getFirstUpdateCount(query);
-                delegate.didCleanData(this, entity, count);
-            }
-        }
-    }
-
-    /**
-     * Reads source data from source, saving it to destination.
-     */
-    protected void processInsert(List entities) throws CayenneException {
-        // Allow delegate to modify the list of entities
-        // any way it wants. For instance delegate may filter
-        // or sort the list (though it doesn't have to, and can simply
-        // pass through the original list).
-        if (delegate != null) {
-            entities = delegate.willCleanData(this, entities);
-        }
-
-        if (entities == null || entities.isEmpty()) {
-            return;
-        }
-
-        // Create an observer for to get the iterated result
-        // instead of getting each table as a list
-        IteratedSelectObserver observer = new IteratedSelectObserver();
-
-        // Using QueryResult as observer for the data insert.
-        // This allows to collect query statistics and pass it to the delegate.
-        QueryResult insertObserver = new QueryResult();
-
-        // process ordered list of entities one by one
-        Iterator it = entities.iterator();
-        while (it.hasNext()) {
-            insertObserver.clear();
-
-            DbEntity entity = (DbEntity) it.next();
-
-            SelectQuery<DataRow> select = new SelectQuery<DataRow>(entity);
-            select.setFetchingDataRows(true);
-
-            // delegate is allowed to substitute query
-            Query query = (delegate != null) ? delegate.willPortEntity(this, entity, select) : select;
-
-            sourceNode.performQueries(Collections.singletonList(query), observer);
-            ResultIterator result = observer.getResultIterator();
-            InsertBatchQuery insert = new InsertBatchQuery(entity, INSERT_BATCH_SIZE);
-
-            try {
-
-                // Split insertions into the same table into batches.
-                // This will allow to process tables of arbitrary size
-                // and not run out of memory.
-                int currentRow = 0;
-
-                // even if we don't use intermediate batch commits, we still
-                // need to
-                // estimate batch insert size
-                int batchSize = insertBatchSize > 0 ? insertBatchSize : INSERT_BATCH_SIZE;
-
-                while (result.hasNextRow()) {
-                    if (insertBatchSize > 0 && currentRow > 0 && currentRow % insertBatchSize == 0) {
-                        // end of the batch detected... commit and start a new
-                        // insert
-                        // query
-                        destinationNode.performQueries(Collections.singletonList((Query) insert), insertObserver);
-                        insert = new InsertBatchQuery(entity, batchSize);
-                        insertObserver.clear();
-                    }
-
-                    currentRow++;
-
-                    Map<String, Object> nextRow = (DataRow) result.nextRow();
-                    insert.add(nextRow);
-                }
-
-                // commit remaining batch if needed
-                if (insert.getRows().size() > 0) {
-                    destinationNode.performQueries(Collections.singletonList((Query) insert), insertObserver);
-                }
-
-                if (delegate != null) {
-                    delegate.didPortEntity(this, entity, currentRow);
-                }
-            } finally {
-
-                // don't forget to close ResultIterator
-                result.close();
-            }
-        }
-    }
-
-    public Collection getEntities() {
-        return entities;
-    }
-
-    public DataNode getSourceNode() {
-        return sourceNode;
-    }
-
-    public DataNode getDestinationNode() {
-        return destinationNode;
-    }
-
-    /**
-     * Sets the initial list of entities to process. This list can be later
-     * modified by the delegate.
-     */
-    public void setEntities(Collection entities) {
-        this.entities = entities;
-    }
-
-    /**
-     * Sets the DataNode serving as a source of the ported data.
-     */
-    public void setSourceNode(DataNode sourceNode) {
-        this.sourceNode = sourceNode;
-    }
-
-    /**
-     * Sets the DataNode serving as a destination of the ported data.
-     */
-    public void setDestinationNode(DataNode destinationNode) {
-        this.destinationNode = destinationNode;
-    }
-
-    /**
-     * Returns previously initialized DataPortDelegate object.
-     */
-    public DataPortDelegate getDelegate() {
-        return delegate;
-    }
-
-    public void setDelegate(DataPortDelegate delegate) {
-        this.delegate = delegate;
-    }
-
-    /**
-     * Returns true if a DataPort was configured to delete all data from the
-     * destination tables.
-     */
-    public boolean isCleaningDestination() {
-        return cleaningDestination;
-    }
-
-    /**
-     * Defines whether DataPort should delete all data from destination tables
-     * before doing the port.
-     */
-    public void setCleaningDestination(boolean cleaningDestination) {
-        this.cleaningDestination = cleaningDestination;
-    }
-
-    public int getInsertBatchSize() {
-        return insertBatchSize;
-    }
-
-    /**
-     * Sets a parameter used for tuning insert batches. If set to a value
-     * greater than zero, DataPort will commit every N rows. If set to value
-     * less or equal to zero, DataPort will commit only once at the end of the
-     * insert.
-     */
-    public void setInsertBatchSize(int insertBatchSize) {
-        this.insertBatchSize = insertBatchSize;
-    }
+	public static final int INSERT_BATCH_SIZE = 1000;
+
+	protected DataNode sourceNode;
+	protected DataNode destinationNode;
+	protected Collection entities;
+	protected boolean cleaningDestination;
+	protected DataPortDelegate delegate;
+	protected int insertBatchSize;
+
+	public DataPort() {
+		this.insertBatchSize = INSERT_BATCH_SIZE;
+	}
+
+	/**
+	 * Creates a new DataPort instance, setting its delegate.
+	 */
+	public DataPort(DataPortDelegate delegate) {
+		this.delegate = delegate;
+	}
+
+	/**
+	 * Runs DataPort. The instance must be fully configured by the time this
+	 * method is invoked, having its delegate, source and destinatio nodes, and
+	 * a list of entities set up.
+	 */
+	public void execute() throws CayenneException {
+		// sanity check
+		if (sourceNode == null) {
+			throw new CayenneException("Can't port data, source node is null.");
+		}
+
+		if (destinationNode == null) {
+			throw new CayenneException("Can't port data, destination node is null.");
+		}
+
+		// the simple equality check may actually detect problems with
+		// misconfigred nodes
+		// it is not as dumb as it may look at first
+		if (sourceNode == destinationNode) {
+			throw new CayenneException("Can't port data, source and target nodes are the same.");
+		}
+
+		if (entities == null || entities.isEmpty()) {
+			return;
+		}
+
+		// sort entities for insertion
+		List sorted = new ArrayList(entities);
+		EntitySorter sorter = new AshwoodEntitySorter();
+		sorter.setEntityResolver(new EntityResolver(destinationNode.getDataMaps()));
+		sorter.sortDbEntities(sorted, false);
+
+		if (cleaningDestination) {
+			// reverse insertion order for deletion
+			List entitiesInDeleteOrder = new ArrayList(sorted.size());
+			entitiesInDeleteOrder.addAll(sorted);
+			Collections.reverse(entitiesInDeleteOrder);
+			processDelete(entitiesInDeleteOrder);
+		}
+
+		processInsert(sorted);
+	}
+
+	/**
+	 * Cleans up destination tables data.
+	 */
+	protected void processDelete(List entities) {
+		// Allow delegate to modify the list of entities
+		// any way it wants. For instance delegate may filter
+		// or sort the list (though it doesn't have to, and can simply
+		// pass through the original list).
+		if (delegate != null) {
+			entities = delegate.willCleanData(this, entities);
+		}
+
+		if (entities == null || entities.isEmpty()) {
+			return;
+		}
+
+		// Delete data from entities one by one
+		Iterator it = entities.iterator();
+		while (it.hasNext()) {
+			DbEntity entity = (DbEntity) it.next();
+
+			Query query = new SQLTemplate(entity, "DELETE FROM " + entity.getFullyQualifiedName());
+
+			// notify delegate that delete is about to happen
+			if (delegate != null) {
+				query = delegate.willCleanData(this, entity, query);
+			}
+			
+			final int[] count = new int[] { -1 };
+
+			// perform delete query
+			OperationObserver observer = new DoNothingOperationObserver() {
+
+				@Override
+				public void nextCount(Query query, int resultCount) {
+					count[0] = resultCount;
+				}
+			};
+			destinationNode.performQueries(Collections.singletonList(query), observer);
+
+			// notify delegate that delete just happened
+			if (delegate != null) {
+				delegate.didCleanData(this, entity, count[0]);
+			}
+		}
+	}
+
+	/**
+	 * Reads source data from source, saving it to destination.
+	 */
+	protected void processInsert(List entities) throws CayenneException {
+		// Allow delegate to modify the list of entities
+		// any way it wants. For instance delegate may filter
+		// or sort the list (though it doesn't have to, and can simply
+		// pass through the original list).
+		if (delegate != null) {
+			entities = delegate.willCleanData(this, entities);
+		}
+
+		if (entities == null || entities.isEmpty()) {
+			return;
+		}
+
+		// Create an observer for to get the iterated result
+		// instead of getting each table as a list
+		IteratedSelectObserver observer = new IteratedSelectObserver();
+
+		OperationObserver insertObserver = new DoNothingOperationObserver();
+
+		// process ordered list of entities one by one
+		Iterator it = entities.iterator();
+		while (it.hasNext()) {
+
+			DbEntity entity = (DbEntity) it.next();
+
+			SelectQuery<DataRow> select = new SelectQuery<DataRow>(entity);
+			select.setFetchingDataRows(true);
+
+			// delegate is allowed to substitute query
+			Query query = (delegate != null) ? delegate.willPortEntity(this, entity, select) : select;
+
+			sourceNode.performQueries(Collections.singletonList(query), observer);
+			ResultIterator result = observer.getResultIterator();
+			InsertBatchQuery insert = new InsertBatchQuery(entity, INSERT_BATCH_SIZE);
+
+			try {
+
+				// Split insertions into the same table into batches.
+				// This will allow to process tables of arbitrary size
+				// and not run out of memory.
+				int currentRow = 0;
+
+				// even if we don't use intermediate batch commits, we still
+				// need to
+				// estimate batch insert size
+				int batchSize = insertBatchSize > 0 ? insertBatchSize : INSERT_BATCH_SIZE;
+
+				while (result.hasNextRow()) {
+					if (insertBatchSize > 0 && currentRow > 0 && currentRow % insertBatchSize == 0) {
+						// end of the batch detected... commit and start a new
+						// insert
+						// query
+						destinationNode.performQueries(Collections.singletonList((Query) insert), insertObserver);
+						insert = new InsertBatchQuery(entity, batchSize);
+					}
+
+					currentRow++;
+
+					Map<String, Object> nextRow = (DataRow) result.nextRow();
+					insert.add(nextRow);
+				}
+
+				// commit remaining batch if needed
+				if (insert.getRows().size() > 0) {
+					destinationNode.performQueries(Collections.singletonList((Query) insert), insertObserver);
+				}
+
+				if (delegate != null) {
+					delegate.didPortEntity(this, entity, currentRow);
+				}
+			} finally {
+
+				// don't forget to close ResultIterator
+				result.close();
+			}
+		}
+	}
+
+	public Collection getEntities() {
+		return entities;
+	}
+
+	public DataNode getSourceNode() {
+		return sourceNode;
+	}
+
+	public DataNode getDestinationNode() {
+		return destinationNode;
+	}
+
+	/**
+	 * Sets the initial list of entities to process. This list can be later
+	 * modified by the delegate.
+	 */
+	public void setEntities(Collection entities) {
+		this.entities = entities;
+	}
+
+	/**
+	 * Sets the DataNode serving as a source of the ported data.
+	 */
+	public void setSourceNode(DataNode sourceNode) {
+		this.sourceNode = sourceNode;
+	}
+
+	/**
+	 * Sets the DataNode serving as a destination of the ported data.
+	 */
+	public void setDestinationNode(DataNode destinationNode) {
+		this.destinationNode = destinationNode;
+	}
+
+	/**
+	 * Returns previously initialized DataPortDelegate object.
+	 */
+	public DataPortDelegate getDelegate() {
+		return delegate;
+	}
+
+	public void setDelegate(DataPortDelegate delegate) {
+		this.delegate = delegate;
+	}
+
+	/**
+	 * Returns true if a DataPort was configured to delete all data from the
+	 * destination tables.
+	 */
+	public boolean isCleaningDestination() {
+		return cleaningDestination;
+	}
+
+	/**
+	 * Defines whether DataPort should delete all data from destination tables
+	 * before doing the port.
+	 */
+	public void setCleaningDestination(boolean cleaningDestination) {
+		this.cleaningDestination = cleaningDestination;
+	}
+
+	public int getInsertBatchSize() {
+		return insertBatchSize;
+	}
+
+	/**
+	 * Sets a parameter used for tuning insert batches. If set to a value
+	 * greater than zero, DataPort will commit every N rows. If set to value
+	 * less or equal to zero, DataPort will commit only once at the end of the
+	 * insert.
+	 */
+	public void setInsertBatchSize(int insertBatchSize) {
+		this.insertBatchSize = insertBatchSize;
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/9f92e346/cayenne-server/src/main/java/org/apache/cayenne/access/OperationObserver.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/OperationObserver.java b/cayenne-server/src/main/java/org/apache/cayenne/access/OperationObserver.java
index 7f292d2..2c7734a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/OperationObserver.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/OperationObserver.java
@@ -55,7 +55,7 @@ public interface OperationObserver extends OperationHints {
      * 
      * @since 3.0
      */
-    void nextRows(Query q, ResultIterator it);
+    void nextRows(Query q, ResultIterator<?> it);
 
     /**
      * Callback method invoked after each batch of generated values is read during an
@@ -63,7 +63,7 @@ public interface OperationObserver extends OperationHints {
      * 
      * @since 4.0
      */
-    void nextGeneratedRows(Query query, ResultIterator keys, ObjectId idToUpdate);
+    void nextGeneratedRows(Query query, ResultIterator<?> keys, ObjectId idToUpdate);
 
     /**
      * Callback method invoked on exceptions that happen during an execution of a specific

http://git-wip-us.apache.org/repos/asf/cayenne/blob/9f92e346/cayenne-server/src/main/java/org/apache/cayenne/access/QueryResult.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/QueryResult.java b/cayenne-server/src/main/java/org/apache/cayenne/access/QueryResult.java
deleted file mode 100644
index 2451ecb..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/QueryResult.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.access;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.ResultIterator;
-import org.apache.cayenne.access.util.DefaultOperationObserver;
-import org.apache.cayenne.query.Query;
-import org.apache.cayenne.util.Util;
-
-/**
- * QueryResult encapsulates a result of execution of zero or more queries using
- * QueryEngine. QueryResult supports queries with multiple mixed selects and updates, such
- * as ProcedureQueries.
- */
-public class QueryResult extends DefaultOperationObserver {
-
-    // a map with order of iteration == to the order of insertion
-    protected Map queries = new LinkedHashMap();
-
-    /**
-     * Clears any previously collected information.
-     */
-    public void clear() {
-        queries.clear();
-    }
-
-    /**
-     * Returns an iterator over all executed queries in the order they were executed.
-     */
-    public Iterator getQueries() {
-        return queries.keySet().iterator();
-    }
-
-    /**
-     * Returns a list of all results of a given query. This is potentially a mix of
-     * java.lang.Integer values for update operations and java.util.List for select
-     * operations. Results are returned in the order they were obtained.
-     */
-    public List getResults(Query query) {
-        List list = (List) queries.get(query);
-        return (list != null) ? list : Collections.EMPTY_LIST;
-    }
-
-    /**
-     * Returns the first update count for the query. This is a shortcut for <code>(Integer)getUpdates(query).get(0)</code>, kind of like Google's "I'm feeling lucky".
-     * Returns -1 if no update count is found for the query.
-     */
-    public int getFirstUpdateCount(Query query) {
-        List allResults = getResults(query);
-        int size = allResults.size();
-        if (size > 0) {
-            Iterator it = allResults.iterator();
-            while (it.hasNext()) {
-                Object object = it.next();
-
-                // if int
-                if (object instanceof Number) {
-                    return ((Number) object).intValue();
-                }
-                // if batch...
-                else if (object instanceof int[]) {
-                    int[] counts = (int[]) object;
-                    return counts.length > 0 ? counts[0] : -1;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Returns the first update count. Returns int[0] if there was no update results for
-     * the query.
-     * 
-     * @since 1.2
-     */
-    public int[] getFirstUpdateCounts(Query query) {
-        List allResults = getResults(query);
-        int size = allResults.size();
-
-        if (size > 0) {
-            Iterator it = allResults.iterator();
-            while (it.hasNext()) {
-                Object object = it.next();
-
-                // if int
-                if (object instanceof Number) {
-                    return new int[] {
-                        ((Number) object).intValue()
-                    };
-                }
-                // if batch...
-                else if (object instanceof int[]) {
-                    return (int[]) object;
-                }
-            }
-        }
-
-        return new int[0];
-    }
-
-    /**
-     * Returns the first results for the query. This is a shortcut for <code>(List)getRows(query).get(0)</code>, kind of like Google's "I'm feeling lucky".
-     */
-    public List getFirstRows(Query query) {
-        List allResults = getResults(query);
-        int size = allResults.size();
-        if (size == 0) {
-            return Collections.EMPTY_LIST;
-        }
-        else {
-            for (Object obj : allResults) {
-                if (obj instanceof List) {
-                    return (List) obj;
-                }
-            }
-        }
-
-        return Collections.EMPTY_LIST;
-    }
-
-    /**
-     * Returns a List that itself contains Lists of data rows for each ResultSet returned
-     * by the query. ResultSets are returned in the oder they were obtained. Any updates
-     * that were performed are not included.
-     */
-    public List<?> getRows(Query query) {
-        List allResults = getResults(query);
-        int size = allResults.size();
-        if (size == 0) {
-            return Collections.EMPTY_LIST;
-        }
-
-        List<Object> list = new ArrayList<Object>(size);
-        for (Object obj : allResults) {
-            if (obj instanceof List) {
-                list.add(obj);
-            }
-        }
-
-        return list;
-    }
-
-    /**
-     * Returns a List that contains java.lang.Integer objects for each one of the update
-     * counts returned by the query. Update counts are returned in the order they were
-     * obtained. Batched and regular updates are combined together.
-     */
-    public List getUpdates(Query query) {
-        List allResults = getResults(query);
-        int size = allResults.size();
-        if (size == 0) {
-            return Collections.EMPTY_LIST;
-        }
-
-        List list = new ArrayList(size);
-        Iterator it = allResults.iterator();
-        while (it.hasNext()) {
-            Object object = it.next();
-            if (object instanceof Number) {
-                list.add(object);
-            }
-            else if (object instanceof int[]) {
-                int[] ints = (int[]) object;
-                for (int anInt : ints) {
-                    list.add(Integer.valueOf(anInt));
-                }
-            }
-        }
-
-        return list;
-    }
-
-    /**
-     * Overrides superclass implementation to rethrow an exception immediately.
-     */
-    @Override
-    public void nextQueryException(Query query, Exception ex) {
-        super.nextQueryException(query, ex);
-        throw new CayenneRuntimeException("Query exception.", Util.unwindException(ex));
-    }
-
-    /**
-     * Overrides superclass implementation to rethrow an exception immediately.
-     */
-    @Override
-    public void nextGlobalException(Exception ex) {
-        super.nextGlobalException(ex);
-        throw new CayenneRuntimeException("Global exception.", Util.unwindException(ex));
-    }
-
-    /**
-     * Always returns <code>false</code>, iterated results are not supported.
-     */
-    @Override
-    public boolean isIteratedResult() {
-        return false;
-    }
-
-    @Override
-    public void nextBatchCount(Query query, int[] resultCount) {
-        List list = (List) queries.get(query);
-        if (list == null) {
-            list = new ArrayList(5);
-            queries.put(query, list);
-        }
-
-        list.add(resultCount);
-    }
-
-    @Override
-    public void nextCount(Query query, int resultCount) {
-        super.nextCount(query, resultCount);
-
-        List list = (List) queries.get(query);
-        if (list == null) {
-            list = new ArrayList(5);
-            queries.put(query, list);
-        }
-
-        list.add(Integer.valueOf(resultCount));
-    }
-
-    @Override
-    public void nextRows(Query query, List<?> dataRows) {
-        super.nextRows(query, dataRows);
-
-        List list = (List) queries.get(query);
-        if (list == null) {
-            list = new ArrayList(5);
-            queries.put(query, list);
-        }
-
-        list.add(dataRows);
-    }
-
-    @Override
-    public void nextRows(Query q, ResultIterator it) {
-        throw new CayenneRuntimeException("Iterated results are not supported by "
-                + this.getClass().getName());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/9f92e346/cayenne-server/src/main/java/org/apache/cayenne/access/util/DoNothingOperationObserver.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/util/DoNothingOperationObserver.java b/cayenne-server/src/main/java/org/apache/cayenne/access/util/DoNothingOperationObserver.java
new file mode 100644
index 0000000..f55d31a
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/util/DoNothingOperationObserver.java
@@ -0,0 +1,80 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.access.util;
+
+import java.util.List;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.ResultIterator;
+import org.apache.cayenne.access.OperationObserver;
+import org.apache.cayenne.query.Query;
+
+/**
+ * A very simple observer that does nothing with provided data, and rethrows any
+ * reported exceptions. Can be used as a base superclass for custom observers.
+ * 
+ * @since 4.0
+ */
+public class DoNothingOperationObserver implements OperationObserver {
+
+	@Override
+	public boolean isIteratedResult() {
+		return false;
+	}
+
+	@Override
+	public void nextCount(Query query, int resultCount) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void nextBatchCount(Query query, int[] resultCount) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void nextRows(Query query, List<?> dataRows) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void nextRows(Query q, ResultIterator<?> it) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void nextGeneratedRows(Query query, ResultIterator<?> keys, ObjectId idToUpdate) {
+		// do
+	}
+
+	@Override
+	public void nextQueryException(Query query, Exception ex) {
+		throw new CayenneRuntimeException(ex);
+	}
+
+	@Override
+	public void nextGlobalException(Exception ex) {
+		throw new CayenneRuntimeException(ex);
+	}
+}