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 2014/11/02 08:10:19 UTC

[31/48] Installing Maven Failsafe Plugin

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryTest.java
deleted file mode 100644
index 367d8d5..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextProcedureQueryTest.java
+++ /dev/null
@@ -1,429 +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.math.BigDecimal;
-import java.sql.Types;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.cayenne.DataRow;
-import org.apache.cayenne.access.jdbc.ColumnDescriptor;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.log.JdbcEventLogger;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.query.CapsStrategy;
-import org.apache.cayenne.query.ProcedureQuery;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.test.jdbc.DBHelper;
-import org.apache.cayenne.testdo.testmap.Artist;
-import org.apache.cayenne.testdo.testmap.Painting;
-import org.apache.cayenne.tx.BaseTransaction;
-import org.apache.cayenne.tx.ExternalTransaction;
-import org.apache.cayenne.unit.UnitDbAdapter;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-
-@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
-public class DataContextProcedureQueryTest extends ServerCase {
-
-    public static final String UPDATE_STORED_PROCEDURE = "cayenne_tst_upd_proc";
-    public static final String UPDATE_STORED_PROCEDURE_NOPARAM = "cayenne_tst_upd_proc2";
-    public static final String SELECT_STORED_PROCEDURE = "cayenne_tst_select_proc";
-    public static final String OUT_STORED_PROCEDURE = "cayenne_tst_out_proc";
-
-    @Inject
-    private DataContext context;
-
-    @Inject
-    private UnitDbAdapter accessStackAdapter;
-
-    @Inject
-    protected DBHelper dbHelper;
-
-    @Inject
-    private JdbcEventLogger jdbcEventLogger;
-
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        dbHelper.deleteAll("PAINTING_INFO");
-        dbHelper.deleteAll("PAINTING");
-        dbHelper.deleteAll("ARTIST_EXHIBIT");
-        dbHelper.deleteAll("ARTIST_GROUP");
-        dbHelper.deleteAll("ARTIST");
-    }
-
-    public void testUpdate() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-
-        ProcedureQuery q = new ProcedureQuery(UPDATE_STORED_PROCEDURE);
-        q.addParameter("paintingPrice", new Integer(3000));
-
-        // since stored procedure commits its stuff, we must use an explicit
-        // non-committing transaction
-
-        BaseTransaction t = new ExternalTransaction(jdbcEventLogger);
-        BaseTransaction.bindThreadTransaction(t);
-
-        try {
-            context.performGenericQuery(q);
-        } finally {
-            BaseTransaction.bindThreadTransaction(null);
-            t.commit();
-        }
-
-        // check that price have doubled
-        SelectQuery select = new SelectQuery(Artist.class);
-        select.addPrefetch("paintingArray");
-
-        List<?> artists = context.performQuery(select);
-        assertEquals(1, artists.size());
-
-        Artist a = (Artist) artists.get(0);
-        Painting p = a.getPaintingArray().get(0);
-        assertEquals(2000, p.getEstimatedPrice().intValue());
-    }
-
-    public void testUpdateNoParam() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-
-        ProcedureQuery q = new ProcedureQuery(UPDATE_STORED_PROCEDURE_NOPARAM);
-
-        // since stored procedure commits its stuff, we must use an explicit
-        // non-committing transaction
-
-        BaseTransaction t = new ExternalTransaction(jdbcEventLogger);
-        BaseTransaction.bindThreadTransaction(t);
-
-        try {
-            context.performGenericQuery(q);
-        } finally {
-            BaseTransaction.bindThreadTransaction(null);
-            t.commit();
-        }
-
-        // check that price have doubled
-        SelectQuery select = new SelectQuery(Artist.class);
-        select.addPrefetch("paintingArray");
-
-        List<?> artists = context.performQuery(select);
-        assertEquals(1, artists.size());
-
-        Artist a = (Artist) artists.get(0);
-        Painting p = a.getPaintingArray().get(0);
-        assertEquals(2000, p.getEstimatedPrice().intValue());
-    }
-
-    public void testSelect1() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-
-        ProcedureQuery q = new ProcedureQuery(SELECT_STORED_PROCEDURE);
-        q.addParameter("aName", "An Artist");
-        q.addParameter("paintingPrice", new Integer(3000));
-        List<?> artists = runProcedureSelect(q);
-
-        // check the results
-        assertNotNull("Null result from StoredProcedure.", artists);
-        assertEquals(1, artists.size());
-        DataRow artistRow = (DataRow) artists.get(0);
-        Artist a = context.objectFromDataRow(Artist.class, uppercaseConverter(artistRow));
-        Painting p = a.getPaintingArray().get(0);
-
-        // invalidate painting, it may have been updated in the proc
-        context.invalidateObjects(p);
-        assertEquals(2000, p.getEstimatedPrice().intValue());
-    }
-
-    public void testSelect2() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-
-        ProcedureQuery q = new ProcedureQuery(SELECT_STORED_PROCEDURE);
-        q.addParameter("aName", "An Artist");
-        q.addParameter("paintingPrice", new Integer(3000));
-
-        List<?> artists = runProcedureSelect(q);
-
-        // check the results
-        assertNotNull("Null result from StoredProcedure.", artists);
-        assertEquals(1, artists.size());
-        DataRow artistRow = (DataRow) artists.get(0);
-        Artist a = context.objectFromDataRow(Artist.class, uppercaseConverter(artistRow));
-        Painting p = a.getPaintingArray().get(0);
-
-        // invalidate painting, it may have been updated in the proc
-        context.invalidateObjects(p);
-        assertEquals(2000, p.getEstimatedPrice().intValue());
-    }
-
-    public void testSelect3() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-
-        // test ProcedureQuery with Procedure as root
-        Procedure proc = context.getEntityResolver().getProcedure(SELECT_STORED_PROCEDURE);
-        ProcedureQuery q = new ProcedureQuery(proc);
-        q.addParameter("aName", "An Artist");
-        q.addParameter("paintingPrice", new Integer(3000));
-
-        List<?> artists = runProcedureSelect(q);
-
-        // check the results
-        assertNotNull("Null result from StoredProcedure.", artists);
-        assertEquals(1, artists.size());
-        DataRow artistRow = (DataRow) artists.get(0);
-        Artist a = context.objectFromDataRow(Artist.class, uppercaseConverter(artistRow));
-        Painting p = a.getPaintingArray().get(0);
-
-        // invalidate painting, it may have been updated in the proc
-        context.invalidateObjects(p);
-        assertEquals(2000, p.getEstimatedPrice().intValue());
-    }
-
-    public void testFetchLimit() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-        createArtist(2000.0);
-        createArtist(3000.0);
-
-        ProcedureQuery q = new ProcedureQuery(SELECT_STORED_PROCEDURE);
-        q.addParameter("aName", "An Artist");
-        q.addParameter("paintingPrice", new Integer(3000));
-        q.setFetchLimit(2);
-        List<?> artists = runProcedureSelect(q);
-
-        assertEquals(2, artists.size());
-    }
-
-    public void testFetchOffset() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-        createArtist(2000.0);
-        createArtist(3000.0);
-
-        ProcedureQuery q = new ProcedureQuery(SELECT_STORED_PROCEDURE);
-        q.addParameter("aName", "An Artist");
-        q.addParameter("paintingPrice", new Integer(3000));
-        q.setFetchOffset(2);
-        List<?> artists = runProcedureSelect(q);
-
-        assertEquals(1, artists.size());
-    }
-
-    public void testColumnNameCapitalization() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-        ProcedureQuery q = new ProcedureQuery(SELECT_STORED_PROCEDURE);
-
-        q.setColumnNamesCapitalization(CapsStrategy.LOWER);
-        q.addParameter("aName", "An Artist");
-        List<DataRow> artists = runProcedureSelect(q);
-
-        ProcedureQuery q1 = new ProcedureQuery(SELECT_STORED_PROCEDURE);
-
-        q1.setColumnNamesCapitalization(CapsStrategy.UPPER);
-        q1.addParameter("aName", "An Artist");
-        List<DataRow> artists1 = runProcedureSelect(q1);
-
-        assertTrue(artists.get(0).containsKey("date_of_birth"));
-        assertFalse(artists.get(0).containsKey("DATE_OF_BIRTH"));
-
-        assertFalse(artists1.get(0).containsKey("date_of_birth"));
-        assertTrue(artists1.get(0).containsKey("DATE_OF_BIRTH"));
-
-    }
-
-    public void testOutParams() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        ProcedureQuery q = new ProcedureQuery(OUT_STORED_PROCEDURE);
-        q.addParameter("in_param", new Integer(20));
-
-        List<?> rows = runProcedureSelect(q);
-
-        assertEquals(1, rows.size());
-        Object row = rows.get(0);
-        assertNotNull(row);
-        assertTrue("Unexpected row class: " + row.getClass().getName(), row instanceof Map<?, ?>);
-        Map<?, ?> outParams = (Map<?, ?>) row;
-        Number price = (Number) outParams.get("out_param");
-        assertNotNull("Null result... row content: " + row, price);
-        assertEquals(40, price.intValue());
-    }
-
-    public void testSelectDataObject() throws Exception {
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        if (!accessStackAdapter.canMakeObjectsOutOfProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1101.01);
-
-        ProcedureQuery q = new ProcedureQuery(SELECT_STORED_PROCEDURE, Artist.class);
-        q.addParameter("aName", "An Artist");
-
-        List<?> artists = runProcedureSelect(q);
-
-        // check the results
-        assertNotNull("Null result from StoredProcedure.", artists);
-        assertEquals(1, artists.size());
-        Artist a = (Artist) artists.get(0);
-        Painting p = a.getPaintingArray().get(0);
-
-        // invalidate painting, it may have been updated in the proc
-        context.invalidateObjects(p);
-        assertEquals(1101.01, p.getEstimatedPrice().doubleValue(), 0.02);
-    }
-
-    public void testSelectWithRowDescriptor() throws Exception {
-
-        if (!accessStackAdapter.supportsStoredProcedures()) {
-            return;
-        }
-
-        // create an artist with painting in the database
-        createArtist(1000.0);
-
-        // test ProcedureQuery with Procedure as root
-        Procedure proc = context.getEntityResolver().getProcedure(SELECT_STORED_PROCEDURE);
-        ProcedureQuery q = new ProcedureQuery(proc);
-        q.setFetchingDataRows(true);
-        q.addParameter("aName", "An Artist");
-        q.addParameter("paintingPrice", new Integer(3000));
-
-        // TESTING THIS ***
-        // A.ARTIST_ID, A.DATE_OF_BIRTH, A.ARTIST_NAME
-        ColumnDescriptor[] columns = new ColumnDescriptor[3];
-
-        // read ID as Long, and everything else as default types
-        columns[0] = new ColumnDescriptor("ARTIST_ID", Types.BIGINT);
-        columns[1] = new ColumnDescriptor("ARTIST_NAME", Types.CHAR);
-        columns[2] = new ColumnDescriptor("DATE_OF_BIRTH", Types.DATE);
-        q.addResultDescriptor(columns);
-
-        List<?> rows = runProcedureSelect(q);
-
-        // check the results
-        assertNotNull("Null result from StoredProcedure.", rows);
-        assertEquals(1, rows.size());
-        DataRow artistRow = (DataRow) rows.get(0);
-
-        assertEquals(3, artistRow.size());
-
-        artistRow = uppercaseConverter(artistRow);
-
-        Object id = artistRow.get("ARTIST_ID");
-        assertNotNull(id);
-        assertTrue("Expected Long, got: " + id.getClass().getName(), id instanceof Long);
-    }
-
-    protected List<DataRow> runProcedureSelect(ProcedureQuery q) throws Exception {
-        // Sybase blows whenever a transaction wraps a SP, so turn off
-        // transactions
-
-        // TODO: it is quite the opposite with PostgreSQL. If an SP returns an
-        // open refcursor, it actually expects a TX in progress, so while we
-        // don't have refcursor unit tests, this is something to keep in mind
-        // e.g.
-        // http://stackoverflow.com/questions/16921942/porting-apache-cayenne-from-oracle-to-postgresql
-
-        BaseTransaction t = new ExternalTransaction(jdbcEventLogger);
-        BaseTransaction.bindThreadTransaction(t);
-
-        try {
-            return context.performQuery(q);
-        } finally {
-            BaseTransaction.bindThreadTransaction(null);
-            t.commit();
-        }
-    }
-
-    protected void createArtist(double paintingPrice) {
-        Artist a = context.newObject(Artist.class);
-        a.setArtistName("An Artist");
-
-        Painting p = context.newObject(Painting.class);
-        p.setPaintingTitle("A Painting");
-        // converting double to string prevents rounding weirdness...
-        p.setEstimatedPrice(new BigDecimal("" + paintingPrice));
-        a.addToPaintingArray(p);
-
-        context.commitChanges();
-    }
-
-    /**
-     * An ugly hack - converting row keys to uppercase ... Tracked via CAY-148.
-     */
-    protected DataRow uppercaseConverter(DataRow row) {
-        DataRow converted = new DataRow(row.size());
-
-        for (Entry<String, Object> entry : row.entrySet()) {
-            converted.put(entry.getKey().toString().toUpperCase(), entry.getValue());
-        }
-
-        return converted;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityIT.java
new file mode 100644
index 0000000..3d61ee6
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityIT.java
@@ -0,0 +1,119 @@
+/*****************************************************************
+ *   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 org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.inherit.AbstractPerson;
+import org.apache.cayenne.testdo.inherit.CustomerRepresentative;
+import org.apache.cayenne.testdo.inherit.Employee;
+import org.apache.cayenne.testdo.inherit.Manager;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+import java.sql.Types;
+import java.util.List;
+
+/**
+ */
+@UseServerRuntime(ServerCase.PEOPLE_PROJECT)
+public class DataContextQualifiedEntityIT extends ServerCase {
+
+    @Inject
+    protected ObjectContext context;
+
+    @Inject
+    protected DBHelper dbHelper;
+
+    protected TableHelper tPerson;
+
+    @Override
+    protected void setUpAfterInjection() throws Exception {
+        // manually break circular deps
+        dbHelper.update("PERSON").set("DEPARTMENT_ID", null, Types.INTEGER).execute();
+
+        dbHelper.deleteAll("ADDRESS");
+        dbHelper.deleteAll("DEPARTMENT");
+        dbHelper.deleteAll("PERSON_NOTES");
+        dbHelper.deleteAll("PERSON");
+        dbHelper.deleteAll("CLIENT_COMPANY");
+        
+        tPerson = new TableHelper(dbHelper, "PERSON");
+        tPerson.setColumns(
+                "CLIENT_COMPANY_ID",
+                "CLIENT_CONTACT_TYPE",
+                "DEPARTMENT_ID",
+                "NAME",
+                "PERSON_ID",
+                "PERSON_TYPE",
+                "SALARY").setColumnTypes(
+                Types.INTEGER,
+                Types.VARCHAR,
+                Types.INTEGER,
+                Types.VARCHAR,
+                Types.INTEGER,
+                Types.CHAR,
+                Types.FLOAT);
+    }
+
+    protected void createPersonsDataSet() throws Exception {
+        tPerson.insert(null, null, null, "e1", 1, "EE", 20000);
+        tPerson.insert(null, null, null, "e2", 2, "EE", 25000);
+        tPerson.insert(null, null, null, "e3", 3, "EE", 28000);
+        tPerson.insert(null, null, null, "m1", 4, "EM", 30000);
+        tPerson.insert(null, null, null, "m2", 5, "EM", 40000);
+        tPerson.insert(null, null, null, "c1", 6, "C", null);
+    }
+
+    public void testSelect() throws Exception {
+        createPersonsDataSet();
+
+        // just check that an appropriate qualifier was applied
+        // no inheritance checks in this case...
+
+        // select Abstract Ppl
+        List<?> abstractPpl = context.performQuery(new SelectQuery(AbstractPerson.class));
+        assertEquals(6, abstractPpl.size());
+
+        // select Customer Reps
+        List<?> customerReps = context.performQuery(new SelectQuery(
+                CustomerRepresentative.class));
+        assertEquals(1, customerReps.size());
+
+        // select Employees
+        List<?> employees = context.performQuery(new SelectQuery(Employee.class));
+        assertEquals(5, employees.size());
+
+        // select Managers
+        List<?> managers = context.performQuery(new SelectQuery(Manager.class));
+        assertEquals(2, managers.size());
+    }
+
+    public void testPrefetch() throws Exception {
+        createPersonsDataSet();
+
+        // select Managers.. make sure prefetch query works as expected
+        List<?> managers = context.performQuery(new SelectQuery(Manager.class));
+        assertEquals(2, managers.size());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityTest.java
deleted file mode 100644
index e96209b..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQualifiedEntityTest.java
+++ /dev/null
@@ -1,119 +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.sql.Types;
-import java.util.List;
-
-import org.apache.cayenne.ObjectContext;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.test.jdbc.DBHelper;
-import org.apache.cayenne.test.jdbc.TableHelper;
-import org.apache.cayenne.testdo.inherit.AbstractPerson;
-import org.apache.cayenne.testdo.inherit.CustomerRepresentative;
-import org.apache.cayenne.testdo.inherit.Employee;
-import org.apache.cayenne.testdo.inherit.Manager;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-
-/**
- */
-@UseServerRuntime(ServerCase.PEOPLE_PROJECT)
-public class DataContextQualifiedEntityTest extends ServerCase {
-
-    @Inject
-    protected ObjectContext context;
-
-    @Inject
-    protected DBHelper dbHelper;
-
-    protected TableHelper tPerson;
-
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        // manually break circular deps
-        dbHelper.update("PERSON").set("DEPARTMENT_ID", null, Types.INTEGER).execute();
-
-        dbHelper.deleteAll("ADDRESS");
-        dbHelper.deleteAll("DEPARTMENT");
-        dbHelper.deleteAll("PERSON_NOTES");
-        dbHelper.deleteAll("PERSON");
-        dbHelper.deleteAll("CLIENT_COMPANY");
-        
-        tPerson = new TableHelper(dbHelper, "PERSON");
-        tPerson.setColumns(
-                "CLIENT_COMPANY_ID",
-                "CLIENT_CONTACT_TYPE",
-                "DEPARTMENT_ID",
-                "NAME",
-                "PERSON_ID",
-                "PERSON_TYPE",
-                "SALARY").setColumnTypes(
-                Types.INTEGER,
-                Types.VARCHAR,
-                Types.INTEGER,
-                Types.VARCHAR,
-                Types.INTEGER,
-                Types.CHAR,
-                Types.FLOAT);
-    }
-
-    protected void createPersonsDataSet() throws Exception {
-        tPerson.insert(null, null, null, "e1", 1, "EE", 20000);
-        tPerson.insert(null, null, null, "e2", 2, "EE", 25000);
-        tPerson.insert(null, null, null, "e3", 3, "EE", 28000);
-        tPerson.insert(null, null, null, "m1", 4, "EM", 30000);
-        tPerson.insert(null, null, null, "m2", 5, "EM", 40000);
-        tPerson.insert(null, null, null, "c1", 6, "C", null);
-    }
-
-    public void testSelect() throws Exception {
-        createPersonsDataSet();
-
-        // just check that an appropriate qualifier was applied
-        // no inheritance checks in this case...
-
-        // select Abstract Ppl
-        List<?> abstractPpl = context.performQuery(new SelectQuery(AbstractPerson.class));
-        assertEquals(6, abstractPpl.size());
-
-        // select Customer Reps
-        List<?> customerReps = context.performQuery(new SelectQuery(
-                CustomerRepresentative.class));
-        assertEquals(1, customerReps.size());
-
-        // select Employees
-        List<?> employees = context.performQuery(new SelectQuery(Employee.class));
-        assertEquals(5, employees.size());
-
-        // select Managers
-        List<?> managers = context.performQuery(new SelectQuery(Manager.class));
-        assertEquals(2, managers.size());
-    }
-
-    public void testPrefetch() throws Exception {
-        createPersonsDataSet();
-
-        // select Managers.. make sure prefetch query works as expected
-        List<?> managers = context.performQuery(new SelectQuery(Manager.class));
-        assertEquals(2, managers.size());
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheIT.java
new file mode 100644
index 0000000..d259acf
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheIT.java
@@ -0,0 +1,64 @@
+/*****************************************************************
+ *   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 org.apache.cayenne.cache.EhCacheQueryCache;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataContextQueryCachingEhCacheIT extends DataContextQueryCachingIT {
+    
+    protected EhCacheQueryCache domainCache;
+    protected EhCacheQueryCache contextCache;
+
+    @Override
+    protected void setUpAfterInjection() throws Exception {
+        dbHelper.deleteAll("PAINTING_INFO");
+        dbHelper.deleteAll("PAINTING");
+        dbHelper.deleteAll("ARTIST_EXHIBIT");
+        dbHelper.deleteAll("ARTIST_GROUP");
+        dbHelper.deleteAll("ARTIST");
+
+        tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
+
+        tPainting = new TableHelper(dbHelper, "PAINTING");
+        tPainting.setColumns(
+                "PAINTING_ID",
+                "PAINTING_TITLE",
+                "ARTIST_ID",
+                "ESTIMATED_PRICE");
+
+        domain = context.getParentDataDomain();
+        oldCache = domain.getQueryCache();
+        
+        domainCache = new EhCacheQueryCache();
+        contextCache = new EhCacheQueryCache();
+        domain.setQueryCache(domainCache);
+        context.setQueryCache(contextCache);
+    }
+    
+    @Override
+    protected void tearDownBeforeInjection() throws Exception {
+        domainCache.shutdown();
+        contextCache.shutdown();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheTest.java
deleted file mode 100644
index 77abde1..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingEhCacheTest.java
+++ /dev/null
@@ -1,64 +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 org.apache.cayenne.cache.EhCacheQueryCache;
-import org.apache.cayenne.test.jdbc.TableHelper;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-
-@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
-public class DataContextQueryCachingEhCacheTest extends DataContextQueryCachingTest {
-    
-    protected EhCacheQueryCache domainCache;
-    protected EhCacheQueryCache contextCache;
-
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        dbHelper.deleteAll("PAINTING_INFO");
-        dbHelper.deleteAll("PAINTING");
-        dbHelper.deleteAll("ARTIST_EXHIBIT");
-        dbHelper.deleteAll("ARTIST_GROUP");
-        dbHelper.deleteAll("ARTIST");
-
-        tArtist = new TableHelper(dbHelper, "ARTIST");
-        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
-
-        tPainting = new TableHelper(dbHelper, "PAINTING");
-        tPainting.setColumns(
-                "PAINTING_ID",
-                "PAINTING_TITLE",
-                "ARTIST_ID",
-                "ESTIMATED_PRICE");
-
-        domain = context.getParentDataDomain();
-        oldCache = domain.getQueryCache();
-        
-        domainCache = new EhCacheQueryCache();
-        contextCache = new EhCacheQueryCache();
-        domain.setQueryCache(domainCache);
-        context.setQueryCache(contextCache);
-    }
-    
-    @Override
-    protected void tearDownBeforeInjection() throws Exception {
-        domainCache.shutdown();
-        contextCache.shutdown();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingIT.java
new file mode 100644
index 0000000..14c1954
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingIT.java
@@ -0,0 +1,256 @@
+/*****************************************************************
+ *   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 org.apache.cayenne.DataObject;
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.cache.MapQueryCache;
+import org.apache.cayenne.cache.QueryCache;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.QueryCacheStrategy;
+import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataContextQueryCachingIT extends ServerCase {
+
+    @Inject
+    protected DataContext context;
+
+    @Inject
+    protected DBHelper dbHelper;
+
+    protected TableHelper tArtist;
+    protected TableHelper tPainting;
+
+    protected QueryCache oldCache;
+    protected DataDomain domain;
+
+    protected DataNode getNode() {
+        return this.domain.getDataNodes().iterator().next();
+    }
+
+    @Override
+    protected void setUpAfterInjection() throws Exception {
+        dbHelper.deleteAll("PAINTING_INFO");
+        dbHelper.deleteAll("PAINTING");
+        dbHelper.deleteAll("ARTIST_EXHIBIT");
+        dbHelper.deleteAll("ARTIST_GROUP");
+        dbHelper.deleteAll("ARTIST");
+
+        tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
+
+        tPainting = new TableHelper(dbHelper, "PAINTING");
+        tPainting.setColumns(
+                "PAINTING_ID",
+                "PAINTING_TITLE",
+                "ARTIST_ID",
+                "ESTIMATED_PRICE");
+
+        domain = context.getParentDataDomain();
+        oldCache = domain.getQueryCache();
+        domain.setQueryCache(new MapQueryCache(50));
+        context.setQueryCache(new MapQueryCache(50));
+    }
+
+    @Override
+    protected void tearDownBeforeInjection() throws Exception {
+        domain.setQueryCache(oldCache);
+    }
+
+    protected void createInsertDataSet() throws Exception {
+        tArtist.insert(33001, "aaa");
+        tPainting.insert(33001, "P", 33001, 4000);
+    }
+
+    protected void createUpdateDataSet1() throws Exception {
+        tArtist.update().set("ARTIST_NAME", "bbb").where("ARTIST_ID", 33001).execute();
+    }
+
+    protected void createUpdateDataSet2() throws Exception {
+        tArtist.update().set("ARTIST_NAME", "ccc").where("ARTIST_ID", 33001).execute();
+    }
+
+    public void testLocalCacheDataRowsRefresh() throws Exception {
+        SelectQuery select = new SelectQuery(Artist.class);
+        select.setFetchingDataRows(true);
+        select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
+
+        MockDataNode engine = MockDataNode.interceptNode(domain, getNode());
+
+        try {
+
+            // first run, no cache yet
+            List<?> rows1 = mockupDataRows(2);
+            engine.reset();
+            engine.addExpectedResult(select, rows1);
+            List<?> resultRows = context.performQuery(select);
+            assertEquals(1, engine.getRunCount());
+            assertEquals(rows1, resultRows);
+
+            QueryMetadata cacheKey = select.getMetaData(context.getEntityResolver());
+            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
+
+            assertEquals(rows1, context.getQueryCache().get(cacheKey));
+
+            // second run, must refresh the cache
+            List<?> rows2 = mockupDataRows(4);
+            engine.reset();
+            engine.addExpectedResult(select, rows2);
+            select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);
+            List<?> freshResultRows = context.performQuery(select);
+            assertEquals(1, engine.getRunCount());
+            assertEquals(rows2, freshResultRows);
+            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
+            assertEquals(rows2, context.getQueryCache().get(cacheKey));
+        }
+        finally {
+            engine.stopInterceptNode();
+        }
+    }
+
+    public void testSharedCacheDataRowsRefresh() throws Exception {
+
+        SelectQuery select = new SelectQuery(Artist.class);
+        select.setFetchingDataRows(true);
+        select.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE);
+
+        MockDataNode engine = MockDataNode.interceptNode(domain, getNode());
+
+        try {
+            // first run, no cache yet
+            List<?> rows1 = mockupDataRows(2);
+            engine.reset();
+            engine.addExpectedResult(select, rows1);
+            List<?> resultRows = context.performQuery(select);
+            assertEquals(1, engine.getRunCount());
+            assertEquals(rows1, resultRows);
+
+            QueryMetadata cacheKey = select.getMetaData(context.getEntityResolver());
+
+            assertEquals(rows1, context.getParentDataDomain().getQueryCache().get(
+                    cacheKey));
+
+            assertNull(context.getQueryCache().get(cacheKey));
+
+            // second run, must refresh the cache
+            List<?> rows2 = mockupDataRows(5);
+            engine.reset();
+            engine.addExpectedResult(select, rows2);
+            select.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE_REFRESH);
+            List<?> freshResultRows = context.performQuery(select);
+            assertEquals(1, engine.getRunCount());
+            assertEquals(rows2, freshResultRows);
+            assertEquals(rows2, context.getParentDataDomain().getQueryCache().get(
+                    cacheKey));
+            assertNull(context.getQueryCache().get(cacheKey));
+        }
+        finally {
+            engine.stopInterceptNode();
+        }
+    }
+
+    public void testLocalCacheDataObjectsRefresh() throws Exception {
+
+        SelectQuery select = new SelectQuery(Artist.class);
+        select.setFetchingDataRows(false);
+        select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
+
+        MockDataNode engine = MockDataNode.interceptNode(domain, getNode());
+
+        try {
+            // first run, no cache yet
+            List<?> rows1 = mockupDataRows(2);
+            engine.reset();
+            engine.addExpectedResult(select, rows1);
+            List<?> resultRows = context.performQuery(select);
+            assertEquals(1, engine.getRunCount());
+            assertEquals(2, resultRows.size());
+            assertTrue(resultRows.get(0) instanceof DataObject);
+            QueryMetadata cacheKey = select.getMetaData(context.getEntityResolver());
+            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
+
+            assertEquals(resultRows, context.getQueryCache().get(cacheKey));
+
+            // second run, must refresh the cache
+            List<?> rows2 = mockupDataRows(4);
+            engine.reset();
+            engine.addExpectedResult(select, rows2);
+            select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);
+            List<?> freshResultRows = context.performQuery(select);
+
+            assertEquals(1, engine.getRunCount());
+            assertEquals(4, freshResultRows.size());
+            assertTrue(resultRows.get(0) instanceof DataObject);
+            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
+            assertEquals(freshResultRows, context.getQueryCache().get(cacheKey));
+        }
+        finally {
+            engine.stopInterceptNode();
+        }
+    }
+
+    public void testLocalCacheRefreshObjectsRefresh() throws Exception {
+        createInsertDataSet();
+
+        SelectQuery select = new SelectQuery(Artist.class);
+        select.setName("c");
+        select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);
+
+        // no cache yet...
+
+        List<?> objects1 = context.performQuery(select);
+        assertEquals(1, objects1.size());
+        Artist a1 = (Artist) objects1.get(0);
+        assertEquals("aaa", a1.getArtistName());
+
+        // cache, but force refresh
+
+        createUpdateDataSet1();
+
+        List<?> objects2 = context.performQuery(select);
+        assertEquals(1, objects2.size());
+        Artist a2 = (Artist) objects2.get(0);
+        assertSame(a1, a2);
+        assertEquals("bbb", a2.getArtistName());
+    }
+
+    private List<?> mockupDataRows(int len) {
+        List<Object> rows = new ArrayList<Object>(len);
+
+        for (int i = 0; i < len; i++) {
+            DataRow a = new DataRow(3);
+            a.put("ARTIST_ID", new Integer(i + 1));
+            a.put("ARTIST_NAME", "A-" + (i + 1));
+            rows.add(a);
+        }
+
+        return rows;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheIT.java
new file mode 100644
index 0000000..7428fd5
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheIT.java
@@ -0,0 +1,53 @@
+/*****************************************************************
+ *   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 org.apache.cayenne.cache.OSQueryCache;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataContextQueryCachingOSCacheIT extends DataContextQueryCachingIT {
+
+    // runs super tests with a different setup...
+    @Override
+    protected void setUpAfterInjection() throws Exception {
+        dbHelper.deleteAll("PAINTING_INFO");
+        dbHelper.deleteAll("PAINTING");
+        dbHelper.deleteAll("ARTIST_EXHIBIT");
+        dbHelper.deleteAll("ARTIST_GROUP");
+        dbHelper.deleteAll("ARTIST");
+
+        tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
+
+        tPainting = new TableHelper(dbHelper, "PAINTING");
+        tPainting.setColumns(
+                "PAINTING_ID",
+                "PAINTING_TITLE",
+                "ARTIST_ID",
+                "ESTIMATED_PRICE");
+
+        domain = context.getParentDataDomain();
+        oldCache = domain.getQueryCache();
+        domain.setQueryCache(new OSQueryCache());
+        context.setQueryCache(new OSQueryCache());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheTest.java
deleted file mode 100644
index 07c6f50..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingOSCacheTest.java
+++ /dev/null
@@ -1,53 +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 org.apache.cayenne.cache.OSQueryCache;
-import org.apache.cayenne.test.jdbc.TableHelper;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-
-@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
-public class DataContextQueryCachingOSCacheTest extends DataContextQueryCachingTest {
-
-    // runs super tests with a different setup...
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        dbHelper.deleteAll("PAINTING_INFO");
-        dbHelper.deleteAll("PAINTING");
-        dbHelper.deleteAll("ARTIST_EXHIBIT");
-        dbHelper.deleteAll("ARTIST_GROUP");
-        dbHelper.deleteAll("ARTIST");
-
-        tArtist = new TableHelper(dbHelper, "ARTIST");
-        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
-
-        tPainting = new TableHelper(dbHelper, "PAINTING");
-        tPainting.setColumns(
-                "PAINTING_ID",
-                "PAINTING_TITLE",
-                "ARTIST_ID",
-                "ESTIMATED_PRICE");
-
-        domain = context.getParentDataDomain();
-        oldCache = domain.getQueryCache();
-        domain.setQueryCache(new OSQueryCache());
-        context.setQueryCache(new OSQueryCache());
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingTest.java
deleted file mode 100644
index 7376b2e..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryCachingTest.java
+++ /dev/null
@@ -1,256 +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.List;
-
-import org.apache.cayenne.DataObject;
-import org.apache.cayenne.DataRow;
-import org.apache.cayenne.cache.MapQueryCache;
-import org.apache.cayenne.cache.QueryCache;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.query.QueryCacheStrategy;
-import org.apache.cayenne.query.QueryMetadata;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.test.jdbc.DBHelper;
-import org.apache.cayenne.test.jdbc.TableHelper;
-import org.apache.cayenne.testdo.testmap.Artist;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-
-@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
-public class DataContextQueryCachingTest extends ServerCase {
-
-    @Inject
-    protected DataContext context;
-
-    @Inject
-    protected DBHelper dbHelper;
-
-    protected TableHelper tArtist;
-    protected TableHelper tPainting;
-
-    protected QueryCache oldCache;
-    protected DataDomain domain;
-
-    protected DataNode getNode() {
-        return this.domain.getDataNodes().iterator().next();
-    }
-
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        dbHelper.deleteAll("PAINTING_INFO");
-        dbHelper.deleteAll("PAINTING");
-        dbHelper.deleteAll("ARTIST_EXHIBIT");
-        dbHelper.deleteAll("ARTIST_GROUP");
-        dbHelper.deleteAll("ARTIST");
-
-        tArtist = new TableHelper(dbHelper, "ARTIST");
-        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
-
-        tPainting = new TableHelper(dbHelper, "PAINTING");
-        tPainting.setColumns(
-                "PAINTING_ID",
-                "PAINTING_TITLE",
-                "ARTIST_ID",
-                "ESTIMATED_PRICE");
-
-        domain = context.getParentDataDomain();
-        oldCache = domain.getQueryCache();
-        domain.setQueryCache(new MapQueryCache(50));
-        context.setQueryCache(new MapQueryCache(50));
-    }
-
-    @Override
-    protected void tearDownBeforeInjection() throws Exception {
-        domain.setQueryCache(oldCache);
-    }
-
-    protected void createInsertDataSet() throws Exception {
-        tArtist.insert(33001, "aaa");
-        tPainting.insert(33001, "P", 33001, 4000);
-    }
-
-    protected void createUpdateDataSet1() throws Exception {
-        tArtist.update().set("ARTIST_NAME", "bbb").where("ARTIST_ID", 33001).execute();
-    }
-
-    protected void createUpdateDataSet2() throws Exception {
-        tArtist.update().set("ARTIST_NAME", "ccc").where("ARTIST_ID", 33001).execute();
-    }
-
-    public void testLocalCacheDataRowsRefresh() throws Exception {
-        SelectQuery select = new SelectQuery(Artist.class);
-        select.setFetchingDataRows(true);
-        select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
-
-        MockDataNode engine = MockDataNode.interceptNode(domain, getNode());
-
-        try {
-
-            // first run, no cache yet
-            List<?> rows1 = mockupDataRows(2);
-            engine.reset();
-            engine.addExpectedResult(select, rows1);
-            List<?> resultRows = context.performQuery(select);
-            assertEquals(1, engine.getRunCount());
-            assertEquals(rows1, resultRows);
-
-            QueryMetadata cacheKey = select.getMetaData(context.getEntityResolver());
-            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
-
-            assertEquals(rows1, context.getQueryCache().get(cacheKey));
-
-            // second run, must refresh the cache
-            List<?> rows2 = mockupDataRows(4);
-            engine.reset();
-            engine.addExpectedResult(select, rows2);
-            select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);
-            List<?> freshResultRows = context.performQuery(select);
-            assertEquals(1, engine.getRunCount());
-            assertEquals(rows2, freshResultRows);
-            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
-            assertEquals(rows2, context.getQueryCache().get(cacheKey));
-        }
-        finally {
-            engine.stopInterceptNode();
-        }
-    }
-
-    public void testSharedCacheDataRowsRefresh() throws Exception {
-
-        SelectQuery select = new SelectQuery(Artist.class);
-        select.setFetchingDataRows(true);
-        select.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE);
-
-        MockDataNode engine = MockDataNode.interceptNode(domain, getNode());
-
-        try {
-            // first run, no cache yet
-            List<?> rows1 = mockupDataRows(2);
-            engine.reset();
-            engine.addExpectedResult(select, rows1);
-            List<?> resultRows = context.performQuery(select);
-            assertEquals(1, engine.getRunCount());
-            assertEquals(rows1, resultRows);
-
-            QueryMetadata cacheKey = select.getMetaData(context.getEntityResolver());
-
-            assertEquals(rows1, context.getParentDataDomain().getQueryCache().get(
-                    cacheKey));
-
-            assertNull(context.getQueryCache().get(cacheKey));
-
-            // second run, must refresh the cache
-            List<?> rows2 = mockupDataRows(5);
-            engine.reset();
-            engine.addExpectedResult(select, rows2);
-            select.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE_REFRESH);
-            List<?> freshResultRows = context.performQuery(select);
-            assertEquals(1, engine.getRunCount());
-            assertEquals(rows2, freshResultRows);
-            assertEquals(rows2, context.getParentDataDomain().getQueryCache().get(
-                    cacheKey));
-            assertNull(context.getQueryCache().get(cacheKey));
-        }
-        finally {
-            engine.stopInterceptNode();
-        }
-    }
-
-    public void testLocalCacheDataObjectsRefresh() throws Exception {
-
-        SelectQuery select = new SelectQuery(Artist.class);
-        select.setFetchingDataRows(false);
-        select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
-
-        MockDataNode engine = MockDataNode.interceptNode(domain, getNode());
-
-        try {
-            // first run, no cache yet
-            List<?> rows1 = mockupDataRows(2);
-            engine.reset();
-            engine.addExpectedResult(select, rows1);
-            List<?> resultRows = context.performQuery(select);
-            assertEquals(1, engine.getRunCount());
-            assertEquals(2, resultRows.size());
-            assertTrue(resultRows.get(0) instanceof DataObject);
-            QueryMetadata cacheKey = select.getMetaData(context.getEntityResolver());
-            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
-
-            assertEquals(resultRows, context.getQueryCache().get(cacheKey));
-
-            // second run, must refresh the cache
-            List<?> rows2 = mockupDataRows(4);
-            engine.reset();
-            engine.addExpectedResult(select, rows2);
-            select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);
-            List<?> freshResultRows = context.performQuery(select);
-
-            assertEquals(1, engine.getRunCount());
-            assertEquals(4, freshResultRows.size());
-            assertTrue(resultRows.get(0) instanceof DataObject);
-            assertNull(context.getParentDataDomain().getQueryCache().get(cacheKey));
-            assertEquals(freshResultRows, context.getQueryCache().get(cacheKey));
-        }
-        finally {
-            engine.stopInterceptNode();
-        }
-    }
-
-    public void testLocalCacheRefreshObjectsRefresh() throws Exception {
-        createInsertDataSet();
-
-        SelectQuery select = new SelectQuery(Artist.class);
-        select.setName("c");
-        select.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE_REFRESH);
-
-        // no cache yet...
-
-        List<?> objects1 = context.performQuery(select);
-        assertEquals(1, objects1.size());
-        Artist a1 = (Artist) objects1.get(0);
-        assertEquals("aaa", a1.getArtistName());
-
-        // cache, but force refresh
-
-        createUpdateDataSet1();
-
-        List<?> objects2 = context.performQuery(select);
-        assertEquals(1, objects2.size());
-        Artist a2 = (Artist) objects2.get(0);
-        assertSame(a1, a2);
-        assertEquals("bbb", a2.getArtistName());
-    }
-
-    private List<?> mockupDataRows(int len) {
-        List<Object> rows = new ArrayList<Object>(len);
-
-        for (int i = 0; i < len; i++) {
-            DataRow a = new DataRow(3);
-            a.put("ARTIST_ID", new Integer(i + 1));
-            a.put("ARTIST_NAME", "A-" + (i + 1));
-            rows.add(a);
-        }
-
-        return rows;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainIT.java
new file mode 100644
index 0000000..97dd1b0
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainIT.java
@@ -0,0 +1,74 @@
+/*****************************************************************
+ *   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 org.apache.cayenne.DataRow;
+import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.QueryChain;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+import java.util.List;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataContextQueryChainIT extends ServerCase {
+
+    @Inject
+    private DataContext context;
+
+    @Inject
+    private DBHelper dbHelper;
+
+    @Override
+    protected void setUpAfterInjection() throws Exception {
+        dbHelper.deleteAll("PAINTING_INFO");
+        dbHelper.deleteAll("PAINTING");
+        dbHelper.deleteAll("PAINTING1");
+        dbHelper.deleteAll("ARTIST_EXHIBIT");
+        dbHelper.deleteAll("ARTIST_GROUP");
+        dbHelper.deleteAll("ARTIST");
+    }
+
+    public void testSelectQuery() {
+        Artist a1 = context.newObject(Artist.class);
+        a1.setArtistName("X");
+        context.commitChanges();
+
+        QueryChain chain = new QueryChain();
+        chain.addQuery(new SelectQuery(Artist.class));
+        chain.addQuery(new SelectQuery(Artist.class));
+
+        QueryResponse r = context.performGenericQuery(chain);
+
+        // data comes back as datarows
+        assertEquals(2, r.size());
+        r.reset();
+        r.next();
+        List<?> l1 = r.currentList();
+        r.next();
+        List<?> l2 = r.currentList();
+
+        assertTrue(l1.get(0) instanceof DataRow);
+        assertTrue(l2.get(0) instanceof DataRow);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainTest.java
deleted file mode 100644
index c0505a9..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextQueryChainTest.java
+++ /dev/null
@@ -1,74 +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.List;
-
-import org.apache.cayenne.DataRow;
-import org.apache.cayenne.QueryResponse;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.query.QueryChain;
-import org.apache.cayenne.query.SelectQuery;
-import org.apache.cayenne.test.jdbc.DBHelper;
-import org.apache.cayenne.testdo.testmap.Artist;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-
-@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
-public class DataContextQueryChainTest extends ServerCase {
-
-    @Inject
-    private DataContext context;
-
-    @Inject
-    private DBHelper dbHelper;
-
-    @Override
-    protected void setUpAfterInjection() throws Exception {
-        dbHelper.deleteAll("PAINTING_INFO");
-        dbHelper.deleteAll("PAINTING");
-        dbHelper.deleteAll("PAINTING1");
-        dbHelper.deleteAll("ARTIST_EXHIBIT");
-        dbHelper.deleteAll("ARTIST_GROUP");
-        dbHelper.deleteAll("ARTIST");
-    }
-
-    public void testSelectQuery() {
-        Artist a1 = context.newObject(Artist.class);
-        a1.setArtistName("X");
-        context.commitChanges();
-
-        QueryChain chain = new QueryChain();
-        chain.addQuery(new SelectQuery(Artist.class));
-        chain.addQuery(new SelectQuery(Artist.class));
-
-        QueryResponse r = context.performGenericQuery(chain);
-
-        // data comes back as datarows
-        assertEquals(2, r.size());
-        r.reset();
-        r.next();
-        List<?> l1 = r.currentList();
-        r.next();
-        List<?> l2 = r.currentList();
-
-        assertTrue(l1.get(0) instanceof DataRow);
-        assertTrue(l2.get(0) instanceof DataRow);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/e42c376c/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextRefreshQueryIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextRefreshQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextRefreshQueryIT.java
new file mode 100644
index 0000000..bbbb8ba
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DataContextRefreshQueryIT.java
@@ -0,0 +1,440 @@
+/*****************************************************************
+ *   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.List;
+
+import org.apache.cayenne.Cayenne;
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.ValueHolder;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.query.QueryCacheStrategy;
+import org.apache.cayenne.query.RefreshQuery;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.query.SortOrder;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.Painting;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataContextRefreshQueryIT extends ServerCase {
+
+    @Inject
+    protected DataContext context;
+
+    @Inject
+    protected DBHelper dbHelper;
+
+    protected TableHelper tArtist;
+    protected TableHelper tPainting;
+
+    @Override
+    public void setUpAfterInjection() throws Exception {
+        dbHelper.deleteAll("PAINTING_INFO");
+        dbHelper.deleteAll("PAINTING");
+        dbHelper.deleteAll("ARTIST_EXHIBIT");
+        dbHelper.deleteAll("ARTIST_GROUP");
+        dbHelper.deleteAll("ARTIST");
+
+        tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME");
+
+        tPainting = new TableHelper(dbHelper, "PAINTING");
+        tPainting.setColumns(
+                "PAINTING_ID",
+                "PAINTING_TITLE",
+                "ARTIST_ID",
+                "ESTIMATED_PRICE");
+    }
+
+    protected void createRefreshCollectionDataSet() throws Exception {
+        tArtist.insert(33001, "c");
+        tArtist.insert(33002, "b");
+        tPainting.insert(33001, "P1", 33001, 3000);
+        tPainting.insert(33002, "P2", 33001, 4000);
+    }
+
+    protected void createRefreshCollectionToOneUpdateDataSet() throws Exception {
+        tPainting.update().set("ARTIST_ID", 33002).execute();
+    }
+
+    protected void createRefreshObjectToManyDataSet() throws Exception {
+        tArtist.insert(33001, "c");
+        tPainting.insert(33001, "P1", 33001, 3000);
+        tPainting.insert(33002, "P2", 33001, 4000);
+    }
+
+    protected void createRefreshObjectToManyUpdateDataSet() throws Exception {
+        tPainting.delete().where("PAINTING_ID", 33001).execute();
+    }
+
+    public void testRefreshCollection() throws Exception {
+        createRefreshCollectionDataSet();
+
+        SelectQuery q = new SelectQuery(Artist.class);
+        q.addOrdering("db:ARTIST_ID", SortOrder.ASCENDING);
+        List<?> artists = context.performQuery(q);
+
+        Artist a1 = (Artist) artists.get(0);
+        Artist a2 = (Artist) artists.get(1);
+
+        assertEquals(2, a1.getPaintingArray().size());
+        assertEquals(0, a2.getPaintingArray().size());
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a1.getObjectId()));
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a2.getObjectId()));
+
+        RefreshQuery refresh = new RefreshQuery(artists);
+        context.performQuery(refresh);
+
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a1.getObjectId()));
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a2.getObjectId()));
+
+        assertEquals(PersistenceState.HOLLOW, a1.getPersistenceState());
+        assertEquals(PersistenceState.HOLLOW, a2.getPersistenceState());
+
+        assertTrue(((ValueHolder) a1.readProperty(Artist.PAINTING_ARRAY_PROPERTY))
+                .isFault());
+        assertTrue(((ValueHolder) a2.readProperty(Artist.PAINTING_ARRAY_PROPERTY))
+                .isFault());
+    }
+
+    public void testRefreshCollectionToOne() throws Exception {
+        createRefreshCollectionDataSet();
+
+        SelectQuery q = new SelectQuery(Painting.class);
+        q.addOrdering("db:PAINTING_ID", SortOrder.ASCENDING);
+        List<?> paints = context.performQuery(q);
+
+        Painting p1 = (Painting) paints.get(0);
+        Painting p2 = (Painting) paints.get(1);
+
+        Artist a1 = p1.getToArtist();
+        assertSame(a1, p2.getToArtist());
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        createRefreshCollectionToOneUpdateDataSet();
+
+        RefreshQuery refresh = new RefreshQuery(paints);
+        context.performQuery(refresh);
+
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        assertEquals(PersistenceState.HOLLOW, p1.getPersistenceState());
+        assertEquals(PersistenceState.HOLLOW, p2.getPersistenceState());
+
+        assertNotSame(a1, p1.getToArtist());
+        assertNotSame(a1, p2.getToArtist());
+        assertEquals("b", p1.getToArtist().getArtistName());
+    }
+
+    public void testRefreshSingleObject() throws Exception {
+        createRefreshCollectionDataSet();
+
+        SelectQuery q = new SelectQuery(Artist.class);
+        q.addOrdering("db:ARTIST_ID", SortOrder.ASCENDING);
+        List<?> artists = context.performQuery(q);
+
+        Artist a1 = (Artist) artists.get(0);
+
+        assertEquals(2, a1.getPaintingArray().size());
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a1.getObjectId()));
+
+        RefreshQuery refresh = new RefreshQuery(a1);
+        context.performQuery(refresh);
+
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a1.getObjectId()));
+
+        assertEquals(PersistenceState.HOLLOW, a1.getPersistenceState());
+
+        assertTrue(((ValueHolder) a1.readProperty(Artist.PAINTING_ARRAY_PROPERTY))
+                .isFault());
+    }
+
+    public void testRefreshObjectToMany() throws Exception {
+        createRefreshObjectToManyDataSet();
+
+        Artist a = Cayenne.objectForPK(context, Artist.class, 33001l);
+        assertEquals(2, a.getPaintingArray().size());
+
+        createRefreshObjectToManyUpdateDataSet();
+
+        RefreshQuery refresh = new RefreshQuery(a);
+        context.performQuery(refresh);
+        assertEquals(PersistenceState.HOLLOW, a.getPersistenceState());
+        assertEquals(1, a.getPaintingArray().size());
+    }
+
+    public void testRefreshQueryResultsLocalCache() throws Exception {
+        createRefreshCollectionDataSet();
+
+        Expression qual = ExpressionFactory.matchExp(
+                Painting.PAINTING_TITLE_PROPERTY,
+                "P2");
+        SelectQuery q = new SelectQuery(Painting.class, qual);
+        q.addOrdering("db:PAINTING_ID", SortOrder.ASCENDING);
+        q.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
+        q.setCacheGroups("X");
+        List<?> paints = context.performQuery(q);
+
+        // fetch P1 separately from cached query
+        Painting p1 = Cayenne.objectForPK(context, Painting.class, 33001);
+
+        Painting p2 = (Painting) paints.get(0);
+        Artist a1 = p2.getToArtist();
+        assertSame(a1, p1.getToArtist());
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        createRefreshCollectionToOneUpdateDataSet();
+
+        RefreshQuery refresh = new RefreshQuery(q);
+        context.performQuery(refresh);
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+
+        // probably refreshed eagerly
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        assertEquals(PersistenceState.COMMITTED, p1.getPersistenceState());
+        assertEquals(PersistenceState.COMMITTED, p2.getPersistenceState());
+
+        assertSame(a1, p1.getToArtist());
+        assertNotSame(a1, p2.getToArtist());
+        assertEquals("c", p1.getToArtist().getArtistName());
+        assertEquals("b", p2.getToArtist().getArtistName());
+    }
+
+    public void testRefreshQueryResultsSharedCache() throws Exception {
+        createRefreshCollectionDataSet();
+
+        Expression qual = ExpressionFactory.matchExp(
+                Painting.PAINTING_TITLE_PROPERTY,
+                "P2");
+        SelectQuery q = new SelectQuery(Painting.class, qual);
+        q.addOrdering("db:PAINTING_ID", SortOrder.ASCENDING);
+        q.setCacheStrategy(QueryCacheStrategy.SHARED_CACHE);
+        q.setCacheGroups("X");
+        List<?> paints = context.performQuery(q);
+
+        // fetch P1 separately from cached query
+        Painting p1 = Cayenne.objectForPK(context, Painting.class, 33001);
+
+        Painting p2 = (Painting) paints.get(0);
+        Artist a1 = p2.getToArtist();
+        assertSame(a1, p1.getToArtist());
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        createRefreshCollectionToOneUpdateDataSet();
+
+        RefreshQuery refresh = new RefreshQuery(q);
+        context.performQuery(refresh);
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+
+        // probably refreshed eagerly
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        assertEquals(PersistenceState.COMMITTED, p1.getPersistenceState());
+        assertEquals(PersistenceState.COMMITTED, p2.getPersistenceState());
+
+        assertSame(a1, p1.getToArtist());
+        assertNotSame(a1, p2.getToArtist());
+        assertEquals("c", p1.getToArtist().getArtistName());
+        assertEquals("b", p2.getToArtist().getArtistName());
+    }
+
+    public void testRefreshQueryResultGroupLocal() throws Exception {
+        createRefreshCollectionDataSet();
+
+        Expression qual = ExpressionFactory.matchExp(
+                Painting.PAINTING_TITLE_PROPERTY,
+                "P2");
+        SelectQuery q = new SelectQuery(Painting.class, qual);
+        q.addOrdering("db:PAINTING_ID", SortOrder.ASCENDING);
+        q.setCacheStrategy(QueryCacheStrategy.LOCAL_CACHE);
+        q.setCacheGroups("X");
+        List<?> paints = context.performQuery(q);
+
+        // fetch P1 separately from cached query
+        Painting p1 = Cayenne.objectForPK(context, Painting.class, 33001);
+
+        Painting p2 = (Painting) paints.get(0);
+        Artist a1 = p2.getToArtist();
+        assertSame(a1, p1.getToArtist());
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        createRefreshCollectionToOneUpdateDataSet();
+
+        // results are served from cache and therefore are not refreshed
+        context.performQuery(q);
+        assertSame(a1, p1.getToArtist());
+        assertSame(a1, p2.getToArtist());
+        assertEquals("c", p1.getToArtist().getArtistName());
+        assertEquals("c", p2.getToArtist().getArtistName());
+
+        RefreshQuery refresh = new RefreshQuery("X");
+
+        // this should invalidate results for the next query run
+        context.performQuery(refresh);
+
+        // this should force a refresh
+        context.performQuery(q);
+
+        assertEquals(PersistenceState.COMMITTED, p1.getPersistenceState());
+        assertEquals(PersistenceState.COMMITTED, p2.getPersistenceState());
+
+        assertSame(a1, p1.getToArtist());
+        assertNotSame(a1, p2.getToArtist());
+        assertEquals("c", p1.getToArtist().getArtistName());
+        assertEquals("b", p2.getToArtist().getArtistName());
+    }
+
+    public void testRefreshAll() throws Exception {
+        createRefreshCollectionDataSet();
+
+        SelectQuery q = new SelectQuery(Artist.class);
+        q.addOrdering("db:ARTIST_ID", SortOrder.ASCENDING);
+        List<?> artists = context.performQuery(q);
+
+        Artist a1 = (Artist) artists.get(0);
+        Artist a2 = (Artist) artists.get(1);
+        Painting p1 = a1.getPaintingArray().get(0);
+        Painting p2 = a1.getPaintingArray().get(0);
+
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a1.getObjectId()));
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a2.getObjectId()));
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+        assertNotNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        RefreshQuery refresh = new RefreshQuery();
+        context.performQuery(refresh);
+
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a1.getObjectId()));
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(a2.getObjectId()));
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p1.getObjectId()));
+        assertNull(context
+                .getParentDataDomain()
+                .getSharedSnapshotCache()
+                .getCachedSnapshot(p2.getObjectId()));
+
+        assertEquals(PersistenceState.HOLLOW, a1.getPersistenceState());
+        assertEquals(PersistenceState.HOLLOW, a2.getPersistenceState());
+        assertEquals(PersistenceState.HOLLOW, p1.getPersistenceState());
+        assertEquals(PersistenceState.HOLLOW, p2.getPersistenceState());
+    }
+}