/***************************************************************** * 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; import org.apache.cayenne.access.MockDataNode; import org.apache.cayenne.configuration.server.ServerRuntime; import org.apache.cayenne.di.Inject; import org.apache.cayenne.exp.ExpressionFactory; 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.ArtGroup; import org.apache.cayenne.testdo.testmap.Artist; 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 java.sql.Types; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) public class CayenneDataObjectFlattenedRelIT extends ServerCase { @Inject private ServerRuntime runtime; @Inject private ObjectContext context; @Inject private DBHelper dbHelper; @Inject private DataChannelInterceptor queryInterceptor; private TableHelper tArtist; private TableHelper tArtGroup; private TableHelper tArtistGroup; @Override public void cleanUpDB() throws Exception { dbHelper.update("ARTGROUP").set("PARENT_GROUP_ID", null, Types.INTEGER).execute(); super.cleanUpDB(); } @Before public void setUp() throws Exception { tArtist = new TableHelper(dbHelper, "ARTIST"); tArtist.setColumns("ARTIST_ID", "ARTIST_NAME"); tArtGroup = new TableHelper(dbHelper, "ARTGROUP"); tArtGroup.setColumns("GROUP_ID", "NAME"); tArtistGroup = new TableHelper(dbHelper, "ARTIST_GROUP"); tArtistGroup.setColumns("ARTIST_ID", "GROUP_ID"); } private void create1Artist1ArtGroupDataSet() throws Exception { tArtist.insert(33001, "artist1"); tArtGroup.insert(1, "g1"); } private void create1Artist2ArtGroupDataSet() throws Exception { create1Artist1ArtGroupDataSet(); tArtGroup.insert(2, "g2"); } private void create1Artist1ArtGroup1ArtistGroupDataSet() throws Exception { create1Artist1ArtGroupDataSet(); tArtistGroup.insert(33001, 1); } @Test public void testReadFlattenedRelationship() throws Exception { create1Artist1ArtGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); List<ArtGroup> groupList = a1.getGroupArray(); assertNotNull(groupList); assertEquals(0, groupList.size()); } @Test public void testReadFlattenedRelationship2() throws Exception { create1Artist1ArtGroup1ArtistGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); List<ArtGroup> groupList = a1.getGroupArray(); assertNotNull(groupList); assertEquals(1, groupList.size()); assertEquals(PersistenceState.COMMITTED, groupList.get(0).getPersistenceState()); assertEquals("g1", groupList.get(0).getName()); } @Test public void testAddToFlattenedRelationship() throws Exception { create1Artist1ArtGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); assertEquals(0, a1.getGroupArray().size()); SelectQuery q = new SelectQuery(ArtGroup.class, ExpressionFactory.matchExp( "name", "g1")); List<?> results = context.performQuery(q); assertEquals(1, results.size()); assertFalse(context.hasChanges()); ArtGroup group = (ArtGroup) results.get(0); a1.addToGroupArray(group); assertTrue(context.hasChanges()); List<?> groupList = a1.getGroupArray(); assertEquals(1, groupList.size()); assertEquals("g1", ((ArtGroup) groupList.get(0)).getName()); // Ensure that the commit doesn't fail a1.getObjectContext().commitChanges(); // and check again assertFalse(context.hasChanges()); // refetch artist with a different context ObjectContext context2 = runtime.newContext(); a1 = Cayenne.objectForPK(context2, Artist.class, 33001); groupList = a1.getGroupArray(); assertEquals(1, groupList.size()); assertEquals("g1", ((ArtGroup) groupList.get(0)).getName()); } // Test case to show up a bug in committing more than once @Test public void testDoubleCommitAddToFlattenedRelationship() throws Exception { create1Artist1ArtGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); SelectQuery q = new SelectQuery(ArtGroup.class, ExpressionFactory.matchExp( "name", "g1")); List<?> results = context.performQuery(q); assertEquals(1, results.size()); ArtGroup group = (ArtGroup) results.get(0); a1.addToGroupArray(group); List<?> groupList = a1.getGroupArray(); assertEquals(1, groupList.size()); assertEquals("g1", ((ArtGroup) groupList.get(0)).getName()); // Ensure that the commit doesn't fail a1.getObjectContext().commitChanges(); try { // The bug caused the second commit to fail (the link record // was inserted again) a1.getObjectContext().commitChanges(); } catch (Exception e) { e.printStackTrace(); fail("Should not have thrown an exception"); } } @Test public void testRemoveFromFlattenedRelationship() throws Exception { create1Artist1ArtGroup1ArtistGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); ArtGroup group = a1.getGroupArray().get(0); a1.removeFromGroupArray(group); List<ArtGroup> groupList = a1.getGroupArray(); assertEquals(0, groupList.size()); // Ensure that the commit doesn't fail a1.getObjectContext().commitChanges(); // and check again groupList = a1.getGroupArray(); assertEquals(0, groupList.size()); } // Demonstrates a possible bug in ordering of deletes, when a flattened relationships // link record is deleted at the same time (same transaction) as one of the record to // which it links. @Test public void testRemoveFlattenedRelationshipAndRootRecord() throws Exception { create1Artist1ArtGroup1ArtistGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); ArtGroup group = a1.getGroupArray().get(0); a1.removeFromGroupArray(group); // Cause the delete of the link record context.deleteObjects(a1); // Cause the deletion of the artist try { context.commitChanges(); } catch (Exception e) { e.printStackTrace(); fail("Should not have thrown the exception :" + e.getMessage()); } } @Test public void testAddRemoveFlattenedRelationship1() throws Exception { create1Artist1ArtGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); SelectQuery q = new SelectQuery(ArtGroup.class, ExpressionFactory.matchExp( "name", "g1")); List<?> results = context.performQuery(q); assertEquals(1, results.size()); ArtGroup group = (ArtGroup) results.get(0); a1.addToGroupArray(group); group.removeFromArtistArray(a1); queryInterceptor.runWithQueriesBlocked(new UnitTestClosure() { public void execute() { context.commitChanges(); } }); } @Test public void testAddRemoveFlattenedRelationship2() throws Exception { create1Artist2ArtGroupDataSet(); Artist a1 = Cayenne.objectForPK(context, Artist.class, 33001); SelectQuery q = new SelectQuery(ArtGroup.class); List<?> results = context.performQuery(q); assertEquals(2, results.size()); ArtGroup g1 = (ArtGroup) results.get(0); ArtGroup g2 = (ArtGroup) results.get(1); a1.addToGroupArray(g1); a1.addToGroupArray(g2); // test that there is no delete query issued when a flattened join is first // added and then deleted AND there are some other changes (CAY-548) a1.removeFromGroupArray(g1); MockDataNode nodeWrapper = MockDataNode.interceptNode( runtime.getDataDomain(), runtime.getDataDomain().getDataNodes().iterator().next()); try { context.commitChanges(); } finally { nodeWrapper.stopInterceptNode(); } assertEquals(1, nodeWrapper.getRunCount()); } }