Repository: cayenne Updated Branches: refs/heads/master 047a29477 -> 5331b8c5c
CAY-1958 SelectById - a new full-featured select query to get objects by id * support for prefetches Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/5331b8c5 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/5331b8c5 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/5331b8c5 Branch: refs/heads/master Commit: 5331b8c5cad581ecfe71bed41f7d671b548bee7d Parents: 047a294 Author: aadamchik <[email protected]> Authored: Fri Nov 28 17:07:51 2014 +0300 Committer: aadamchik <[email protected]> Committed: Fri Nov 28 17:19:21 2014 +0300 ---------------------------------------------------------------------- .../apache/cayenne/query/SelectByIdTest.java | 8 +- .../org/apache/cayenne/query/SelectById.java | 66 ++++++ .../org/apache/cayenne/query/SelectByIdIT.java | 201 ---------------- .../apache/cayenne/query/SelectByIdTest.java | 78 +++++++ .../apache/cayenne/query/SelectById_RunIT.java | 227 +++++++++++++++++++ 5 files changed, 375 insertions(+), 205 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/5331b8c5/cayenne-client/src/test/java/org/apache/cayenne/query/SelectByIdTest.java ---------------------------------------------------------------------- diff --git a/cayenne-client/src/test/java/org/apache/cayenne/query/SelectByIdTest.java b/cayenne-client/src/test/java/org/apache/cayenne/query/SelectByIdTest.java index 087c84c..84b2e06 100644 --- a/cayenne-client/src/test/java/org/apache/cayenne/query/SelectByIdTest.java +++ b/cayenne-client/src/test/java/org/apache/cayenne/query/SelectByIdTest.java @@ -18,15 +18,15 @@ ****************************************************************/ package org.apache.cayenne.query; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + import org.apache.cayenne.map.EntityResolver; import org.apache.cayenne.remote.hessian.service.HessianUtil; import org.apache.cayenne.testdo.testmap.Artist; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertTrue; - public class SelectByIdTest { @Test http://git-wip-us.apache.org/repos/asf/cayenne/blob/5331b8c5/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java index 00ee205..92060ad 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectById.java @@ -53,6 +53,7 @@ public class SelectById<T> extends IndirectQuery implements Select<T> { boolean fetchingDataRows; QueryCacheStrategy cacheStrategy; String[] cacheGroups; + PrefetchTreeNode prefetches; public static <T> SelectById<T> query(Class<T> entityType, Object id) { SelectById<T> q = new SelectById<T>(); @@ -185,6 +186,70 @@ public class SelectById<T> extends IndirectQuery implements Select<T> { return fetchingDataRows; } + /** + * Resets internal prefetches to the new value, which is a single prefetch + * with specified semantics. + * + * @return this object + */ + public SelectById<T> prefetch(String path, int semantics) { + this.prefetches = PrefetchTreeNode.withPath(path, semantics); + return this; + } + + /** + * Resets internal prefetches to the new value. + * + * @return this object + */ + public SelectById<T> prefetch(PrefetchTreeNode prefetch) { + this.prefetches = prefetch; + return this; + } + + /** + * Merges prefetch into the query prefetch tree. + * + * @return this object + */ + public SelectById<T> addPrefetch(PrefetchTreeNode prefetch) { + + if (prefetch == null) { + return this; + } + + if (prefetches == null) { + prefetches = new PrefetchTreeNode(); + } + + prefetches.merge(prefetch); + return this; + } + + /** + * Merges a prefetch path with specified semantics into the query prefetch + * tree. + * + * @return this object + */ + public SelectById<T> addPrefetch(String path, int semantics) { + + if (path == null) { + return this; + } + + if (prefetches == null) { + prefetches = new PrefetchTreeNode(); + } + + prefetches.addPath(path).setSemantics(semantics); + return this; + } + + public PrefetchTreeNode getPrefetches() { + return prefetches; + } + @Override protected Query createReplacementQuery(EntityResolver resolver) { @@ -201,6 +266,7 @@ public class SelectById<T> extends IndirectQuery implements Select<T> { // optimally - object cache may have an object, but query cache will not query.setCacheGroups(cacheGroups); query.setCacheStrategy(cacheStrategy); + query.setPrefetchTree(prefetches); return query; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/5331b8c5/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java deleted file mode 100644 index bc18f5d..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdIT.java +++ /dev/null @@ -1,201 +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.query; - -import org.apache.cayenne.DataRow; -import org.apache.cayenne.ObjectContext; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.di.Inject; -import org.apache.cayenne.map.EntityResolver; -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.DataChannelInterceptor; -import org.apache.cayenne.unit.di.UnitTestClosure; -import org.apache.cayenne.unit.di.server.CayenneProjects; -import org.apache.cayenne.unit.di.server.ServerCase; -import org.apache.cayenne.unit.di.server.UseServerRuntime; -import org.junit.Before; -import org.junit.Test; - -import static java.util.Collections.singletonMap; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; - -@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) -public class SelectByIdIT extends ServerCase { - - @Inject - private DataChannelInterceptor interceptor; - - @Inject - private DBHelper dbHelper; - - private TableHelper tArtist; - - @Inject - private ObjectContext context; - - @Inject - private EntityResolver resolver; - - @Before - public void setUp() throws Exception { - tArtist = new TableHelper(dbHelper, "ARTIST"); - tArtist.setColumns("ARTIST_ID", "ARTIST_NAME"); - } - - private void createTwoArtists() throws Exception { - tArtist.insert(2, "artist2"); - tArtist.insert(3, "artist3"); - } - - @Test - public void testIntPk() throws Exception { - createTwoArtists(); - - Artist a3 = SelectById.query(Artist.class, 3).selectOne(context); - assertNotNull(a3); - assertEquals("artist3", a3.getArtistName()); - - Artist a2 = SelectById.query(Artist.class, 2).selectOne(context); - assertNotNull(a2); - assertEquals("artist2", a2.getArtistName()); - } - - @Test - public void testMapPk() throws Exception { - createTwoArtists(); - - Artist a3 = SelectById.query(Artist.class, singletonMap(Artist.ARTIST_ID_PK_COLUMN, 3)).selectOne(context); - assertNotNull(a3); - assertEquals("artist3", a3.getArtistName()); - - Artist a2 = SelectById.query(Artist.class, singletonMap(Artist.ARTIST_ID_PK_COLUMN, 2)).selectOne(context); - assertNotNull(a2); - assertEquals("artist2", a2.getArtistName()); - } - - @Test - public void testObjectIdPk() throws Exception { - createTwoArtists(); - - ObjectId oid3 = new ObjectId("Artist", Artist.ARTIST_ID_PK_COLUMN, 3); - Artist a3 = SelectById.query(Artist.class, oid3).selectOne(context); - assertNotNull(a3); - assertEquals("artist3", a3.getArtistName()); - - ObjectId oid2 = new ObjectId("Artist", Artist.ARTIST_ID_PK_COLUMN, 2); - Artist a2 = SelectById.query(Artist.class, oid2).selectOne(context); - assertNotNull(a2); - assertEquals("artist2", a2.getArtistName()); - } - - @Test - public void testDataRowIntPk() throws Exception { - createTwoArtists(); - - DataRow a3 = SelectById.dataRowQuery(Artist.class, 3).selectOne(context); - assertNotNull(a3); - assertEquals("artist3", a3.get("ARTIST_NAME")); - - DataRow a2 = SelectById.dataRowQuery(Artist.class, 2).selectOne(context); - assertNotNull(a2); - assertEquals("artist2", a2.get("ARTIST_NAME")); - } - - @Test - public void testMetadataCacheKey() throws Exception { - SelectById<Painting> q1 = SelectById.query(Painting.class, 4).useLocalCache(); - QueryMetadata md1 = q1.getMetaData(resolver); - assertNotNull(md1); - assertNotNull(md1.getCacheKey()); - - SelectById<Painting> q2 = SelectById.query(Painting.class, singletonMap(Painting.PAINTING_ID_PK_COLUMN, 4)) - .useLocalCache(); - QueryMetadata md2 = q2.getMetaData(resolver); - assertNotNull(md2); - assertNotNull(md2.getCacheKey()); - - // this query is just a different form of q1, so should hit the same - // cache entry - assertEquals(md1.getCacheKey(), md2.getCacheKey()); - - SelectById<Painting> q3 = SelectById.query(Painting.class, 5).useLocalCache(); - QueryMetadata md3 = q3.getMetaData(resolver); - assertNotNull(md3); - assertNotNull(md3.getCacheKey()); - assertNotEquals(md1.getCacheKey(), md3.getCacheKey()); - - SelectById<Artist> q4 = SelectById.query(Artist.class, 4).useLocalCache(); - QueryMetadata md4 = q4.getMetaData(resolver); - assertNotNull(md4); - assertNotNull(md4.getCacheKey()); - assertNotEquals(md1.getCacheKey(), md4.getCacheKey()); - - SelectById<Painting> q5 = SelectById.query(Painting.class, - new ObjectId("Painting", Painting.PAINTING_ID_PK_COLUMN, 4)).useLocalCache(); - QueryMetadata md5 = q5.getMetaData(resolver); - assertNotNull(md5); - assertNotNull(md5.getCacheKey()); - - // this query is just a different form of q1, so should hit the same - // cache entry - assertEquals(md1.getCacheKey(), md5.getCacheKey()); - } - - @Test - public void testLocalCache() throws Exception { - createTwoArtists(); - - final Artist[] a3 = new Artist[1]; - - assertEquals(1, interceptor.runWithQueryCounter(new UnitTestClosure() { - - @Override - public void execute() { - a3[0] = SelectById.query(Artist.class, 3).useLocalCache("g1").selectOne(context); - assertNotNull(a3[0]); - assertEquals("artist3", a3[0].getArtistName()); - } - })); - - interceptor.runWithQueriesBlocked(new UnitTestClosure() { - - @Override - public void execute() { - Artist a3cached = SelectById.query(Artist.class, 3).useLocalCache("g1").selectOne(context); - assertSame(a3[0], a3cached); - } - }); - - context.performGenericQuery(new RefreshQuery("g1")); - - assertEquals(1, interceptor.runWithQueryCounter(new UnitTestClosure() { - - @Override - public void execute() { - SelectById.query(Artist.class, 3).useLocalCache("g1").selectOne(context); - } - })); - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/5331b8c5/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdTest.java new file mode 100644 index 0000000..4dca1c8 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectByIdTest.java @@ -0,0 +1,78 @@ +/***************************************************************** + * 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.query; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import org.apache.cayenne.testdo.testmap.Artist; +import org.junit.Test; + +public class SelectByIdTest { + + @Test + public void testPrefetch() { + + PrefetchTreeNode root = PrefetchTreeNode.withPath("a.b", PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + + SelectById<Artist> q = SelectById.query(Artist.class, 6); + q.prefetch(root); + + assertSame(root, q.getPrefetches()); + } + + @Test + public void testPrefetch_Path() { + + SelectById<Artist> q = SelectById.query(Artist.class, 7); + q.prefetch("a.b", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + PrefetchTreeNode root1 = q.getPrefetches(); + + assertNotNull(root1); + assertNotNull(root1.getNode("a.b")); + + q.prefetch("a.c", PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); + PrefetchTreeNode root2 = q.getPrefetches(); + + assertNotNull(root2); + assertNotNull(root2.getNode("a.c")); + assertNull(root2.getNode("a.b")); + assertNotSame(root1, root2); + } + + @Test + public void testAddPrefetch() { + + PrefetchTreeNode root = PrefetchTreeNode.withPath("a.b", PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + + SelectById<Artist> q = SelectById.query(Artist.class, 8); + q.prefetch(root); + + assertSame(root, q.getPrefetches()); + + PrefetchTreeNode subRoot = PrefetchTreeNode.withPath("a.b.c", PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); + q.addPrefetch(subRoot); + + assertSame(root, q.getPrefetches()); + + assertNotNull(root.getNode("a.b.c")); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/5331b8c5/cayenne-server/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java new file mode 100644 index 0000000..ab67596 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectById_RunIT.java @@ -0,0 +1,227 @@ +/***************************************************************** + * 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.query; + +import static java.util.Collections.singletonMap; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +import java.sql.Types; + +import org.apache.cayenne.DataRow; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.ObjectId; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.map.EntityResolver; +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.DataChannelInterceptor; +import org.apache.cayenne.unit.di.UnitTestClosure; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.junit.Before; +import org.junit.Test; + +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public class SelectById_RunIT extends ServerCase { + + @Inject + private DataChannelInterceptor interceptor; + + @Inject + private DBHelper dbHelper; + + private TableHelper tArtist; + private TableHelper tPainting; + + @Inject + private ObjectContext context; + + @Inject + private EntityResolver resolver; + + @Before + public void setUp() throws Exception { + tArtist = new TableHelper(dbHelper, "ARTIST").setColumns("ARTIST_ID", "ARTIST_NAME"); + tPainting = new TableHelper(dbHelper, "PAINTING").setColumns("PAINTING_ID", "ARTIST_ID", "PAINTING_TITLE") + .setColumnTypes(Types.INTEGER, Types.BIGINT, Types.VARCHAR); + } + + private void createTwoArtists() throws Exception { + tArtist.insert(2, "artist2"); + tArtist.insert(3, "artist3"); + } + + @Test + public void testIntPk() throws Exception { + createTwoArtists(); + + Artist a3 = SelectById.query(Artist.class, 3).selectOne(context); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + + Artist a2 = SelectById.query(Artist.class, 2).selectOne(context); + assertNotNull(a2); + assertEquals("artist2", a2.getArtistName()); + } + + @Test + public void testMapPk() throws Exception { + createTwoArtists(); + + Artist a3 = SelectById.query(Artist.class, singletonMap(Artist.ARTIST_ID_PK_COLUMN, 3)).selectOne(context); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + + Artist a2 = SelectById.query(Artist.class, singletonMap(Artist.ARTIST_ID_PK_COLUMN, 2)).selectOne(context); + assertNotNull(a2); + assertEquals("artist2", a2.getArtistName()); + } + + @Test + public void testObjectIdPk() throws Exception { + createTwoArtists(); + + ObjectId oid3 = new ObjectId("Artist", Artist.ARTIST_ID_PK_COLUMN, 3); + Artist a3 = SelectById.query(Artist.class, oid3).selectOne(context); + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + + ObjectId oid2 = new ObjectId("Artist", Artist.ARTIST_ID_PK_COLUMN, 2); + Artist a2 = SelectById.query(Artist.class, oid2).selectOne(context); + assertNotNull(a2); + assertEquals("artist2", a2.getArtistName()); + } + + @Test + public void testDataRowIntPk() throws Exception { + createTwoArtists(); + + DataRow a3 = SelectById.dataRowQuery(Artist.class, 3).selectOne(context); + assertNotNull(a3); + assertEquals("artist3", a3.get("ARTIST_NAME")); + + DataRow a2 = SelectById.dataRowQuery(Artist.class, 2).selectOne(context); + assertNotNull(a2); + assertEquals("artist2", a2.get("ARTIST_NAME")); + } + + @Test + public void testMetadataCacheKey() throws Exception { + SelectById<Painting> q1 = SelectById.query(Painting.class, 4).useLocalCache(); + QueryMetadata md1 = q1.getMetaData(resolver); + assertNotNull(md1); + assertNotNull(md1.getCacheKey()); + + SelectById<Painting> q2 = SelectById.query(Painting.class, singletonMap(Painting.PAINTING_ID_PK_COLUMN, 4)) + .useLocalCache(); + QueryMetadata md2 = q2.getMetaData(resolver); + assertNotNull(md2); + assertNotNull(md2.getCacheKey()); + + // this query is just a different form of q1, so should hit the same + // cache entry + assertEquals(md1.getCacheKey(), md2.getCacheKey()); + + SelectById<Painting> q3 = SelectById.query(Painting.class, 5).useLocalCache(); + QueryMetadata md3 = q3.getMetaData(resolver); + assertNotNull(md3); + assertNotNull(md3.getCacheKey()); + assertNotEquals(md1.getCacheKey(), md3.getCacheKey()); + + SelectById<Artist> q4 = SelectById.query(Artist.class, 4).useLocalCache(); + QueryMetadata md4 = q4.getMetaData(resolver); + assertNotNull(md4); + assertNotNull(md4.getCacheKey()); + assertNotEquals(md1.getCacheKey(), md4.getCacheKey()); + + SelectById<Painting> q5 = SelectById.query(Painting.class, + new ObjectId("Painting", Painting.PAINTING_ID_PK_COLUMN, 4)).useLocalCache(); + QueryMetadata md5 = q5.getMetaData(resolver); + assertNotNull(md5); + assertNotNull(md5.getCacheKey()); + + // this query is just a different form of q1, so should hit the same + // cache entry + assertEquals(md1.getCacheKey(), md5.getCacheKey()); + } + + @Test + public void testLocalCache() throws Exception { + createTwoArtists(); + + final Artist[] a3 = new Artist[1]; + + assertEquals(1, interceptor.runWithQueryCounter(new UnitTestClosure() { + + @Override + public void execute() { + a3[0] = SelectById.query(Artist.class, 3).useLocalCache("g1").selectOne(context); + assertNotNull(a3[0]); + assertEquals("artist3", a3[0].getArtistName()); + } + })); + + interceptor.runWithQueriesBlocked(new UnitTestClosure() { + + @Override + public void execute() { + Artist a3cached = SelectById.query(Artist.class, 3).useLocalCache("g1").selectOne(context); + assertSame(a3[0], a3cached); + } + }); + + context.performGenericQuery(new RefreshQuery("g1")); + + assertEquals(1, interceptor.runWithQueryCounter(new UnitTestClosure() { + + @Override + public void execute() { + SelectById.query(Artist.class, 3).useLocalCache("g1").selectOne(context); + } + })); + } + + @Test + public void testPrefetch() throws Exception { + createTwoArtists(); + tPainting.insert(45, 3, "One"); + tPainting.insert(48, 3, "Two"); + + final Artist a3 = SelectById.query(Artist.class, 3).prefetch(Artist.PAINTING_ARRAY.joint()).selectOne(context); + + interceptor.runWithQueriesBlocked(new UnitTestClosure() { + + @Override + public void execute() { + assertNotNull(a3); + assertEquals("artist3", a3.getArtistName()); + assertEquals(2, a3.getPaintingArray().size()); + + a3.getPaintingArray().get(0).getPaintingTitle(); + a3.getPaintingArray().get(1).getPaintingTitle(); + } + }); + } +}
