/***************************************************************** * 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.Cayenne; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.di.Inject; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; 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.generated.GeneratedColumnCompKey; import org.apache.cayenne.testdo.generated.GeneratedColumnCompMaster; import org.apache.cayenne.testdo.generated.GeneratedColumnDep; import org.apache.cayenne.testdo.generated.GeneratedColumnTest2; import org.apache.cayenne.testdo.generated.GeneratedColumnTestEntity; import org.apache.cayenne.testdo.generated.GeneratedF1; import org.apache.cayenne.testdo.generated.GeneratedF2; 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.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; @UseServerRuntime(CayenneProjects.GENERATED_PROJECT) public class IdentityColumnsIT extends ServerCase { @Inject protected ObjectContext context; @Inject protected DBHelper dbHelper; @Inject protected DbAdapter adapter; @Inject protected DataNode node; protected TableHelper joinTable; @Before public void setUp() throws Exception { joinTable = new TableHelper(dbHelper, "GENERATED_JOIN"); } /** * Tests a bug casued by the ID Java type mismatch vs the default JDBC type * of the ID column. */ @Test public void testCAY823() throws Exception { GeneratedColumnTestEntity idObject = context.newObject(GeneratedColumnTestEntity.class); String name = "n_" + System.currentTimeMillis(); idObject.setName(name); idObject.getObjectContext().commitChanges(); ObjectId id = idObject.getObjectId(); context.invalidateObjects(idObject); SelectQuery q = new SelectQuery(GeneratedColumnTestEntity.class); q.setPageSize(10); List<?> results = context.performQuery(q); assertEquals(1, results.size()); // per CAY-823 an attempt to resolve an object results in an exception assertEquals(id, ((Persistent) results.get(0)).getObjectId()); } @Test public void testNewObject() throws Exception { GeneratedColumnTestEntity idObject = context.newObject(GeneratedColumnTestEntity.class); String name = "n_" + System.currentTimeMillis(); idObject.setName(name); idObject.getObjectContext().commitChanges(); // this will throw an exception if id wasn't generated one way or // another int id = Cayenne.intPKForObject(idObject); assertTrue(id >= 0); // make sure that id is the same as id in the DB context.invalidateObjects(idObject); GeneratedColumnTestEntity object = Cayenne.objectForPK(context, GeneratedColumnTestEntity.class, id); assertNotNull(object); assertEquals(name, object.getName()); } @Test public void testGeneratedJoinInFlattenedRelationship() throws Exception { // before saving objects, let's manually access PKGenerator to get a // base PK value // for comparison DbEntity joinTableEntity = context.getEntityResolver().getDbEntity(joinTable.getTableName()); DbAttribute pkAttribute = joinTableEntity.getAttribute("ID"); Number pk = (Number) adapter.getPkGenerator().generatePk(node, pkAttribute); GeneratedF1 f1 = context.newObject(GeneratedF1.class); GeneratedF2 f2 = context.newObject(GeneratedF2.class); f1.addToF2(f2); context.commitChanges(); int id = joinTable.getInt("ID"); assertTrue(id > 0); // this is a leap of faith that autoincrement-based IDs will not match // PkGenertor provided ids... This sorta works though if pk generator // has a 200 // base value if (adapter.supportsGeneratedKeys()) { assertFalse("Looks like auto-increment wasn't used for the join table. ID: " + id, id == pk.intValue() + 1); } else { assertEquals(id, pk.intValue() + 1); } } /** * Tests CAY-422 bug. */ @Test public void testUnrelatedUpdate() throws Exception { GeneratedColumnTestEntity m = context.newObject(GeneratedColumnTestEntity.class); m.setName("m"); GeneratedColumnDep d = context.newObject(GeneratedColumnDep.class); d.setName("d"); d.setToMaster(m); context.commitChanges(); context.invalidateObjects(m, d); context.prepareForAccess(d, null, false); // this line caused CAY-422 error d.getToMaster(); d.setName("new name"); context.commitChanges(); } /** * Tests that insert in two tables with identity pk does not generate a * conflict. See CAY-341 for the original bug. */ @Test public void testMultipleNewObjectsSeparateTables() throws Exception { GeneratedColumnTestEntity idObject1 = context.newObject(GeneratedColumnTestEntity.class); idObject1.setName("o1"); GeneratedColumnTest2 idObject2 = context.newObject(GeneratedColumnTest2.class); idObject2.setName("o2"); context.commitChanges(); } @Test public void testMultipleNewObjects() throws Exception { String[] names = new String[] { "n1_" + System.currentTimeMillis(), "n2_" + System.currentTimeMillis(), "n3_" + System.currentTimeMillis() }; GeneratedColumnTestEntity[] idObjects = new GeneratedColumnTestEntity[] { context.newObject(GeneratedColumnTestEntity.class), context.newObject(GeneratedColumnTestEntity.class), context.newObject(GeneratedColumnTestEntity.class) }; for (int i = 0; i < idObjects.length; i++) { idObjects[i].setName(names[i]); } context.commitChanges(); int[] ids = new int[idObjects.length]; for (int i = 0; i < idObjects.length; i++) { ids[i] = Cayenne.intPKForObject(idObjects[i]); assertTrue(ids[i] > 0); } context.invalidateObjects(idObjects); for (int i = 0; i < ids.length; i++) { GeneratedColumnTestEntity object = Cayenne.objectForPK(context, GeneratedColumnTestEntity.class, ids[i]); assertNotNull(object); assertEquals(names[i], object.getName()); } } @Test public void testCompoundPKWithGeneratedColumn() throws Exception { if (adapter.supportsGeneratedKeys()) { // only works for generated keys, as the entity tested has one // Cayenne // auto-pk and one generated key String masterName = "m_" + System.currentTimeMillis(); String depName1 = "dep1_" + System.currentTimeMillis(); String depName2 = "dep2_" + System.currentTimeMillis(); GeneratedColumnCompMaster master = context.newObject(GeneratedColumnCompMaster.class); master.setName(masterName); GeneratedColumnCompKey dep1 = context.newObject(GeneratedColumnCompKey.class); dep1.setName(depName1); dep1.setToMaster(master); GeneratedColumnCompKey dep2 = context.newObject(GeneratedColumnCompKey.class); dep2.setName(depName2); dep2.setToMaster(master); context.commitChanges(); int masterId = Cayenne.intPKForObject(master); ObjectId id2 = dep2.getObjectId(); // check propagated id Number propagatedID2 = (Number) id2.getIdSnapshot().get(GeneratedColumnCompKey.PROPAGATED_PK_PK_COLUMN); assertNotNull(propagatedID2); assertEquals(masterId, propagatedID2.intValue()); // check Cayenne-generated ID Number cayenneGeneratedID2 = (Number) id2.getIdSnapshot().get(GeneratedColumnCompKey.AUTO_PK_PK_COLUMN); assertNotNull(cayenneGeneratedID2); // check DB-generated ID Number dbGeneratedID2 = (Number) id2.getIdSnapshot().get(GeneratedColumnCompKey.GENERATED_COLUMN_PK_COLUMN); assertNotNull(dbGeneratedID2); context.invalidateObjects(master, dep1, dep2); Object fetchedDep2 = Cayenne.objectForPK(context, id2); assertNotNull(fetchedDep2); } } @Test public void testUpdateDependentWithNewMaster() throws Exception { GeneratedColumnTestEntity master1 = context.newObject(GeneratedColumnTestEntity.class); master1.setName("aaa"); GeneratedColumnDep dependent = context.newObject(GeneratedColumnDep.class); dependent.setName("aaa"); dependent.setToMaster(master1); context.commitChanges(); // change master GeneratedColumnTestEntity master2 = context.newObject(GeneratedColumnTestEntity.class); master2.setName("bbb"); // TESTING THIS dependent.setToMaster(master2); context.commitChanges(); int id1 = Cayenne.intPKForObject(master2); assertTrue(id1 >= 0); int id2 = Cayenne.intPKForObject(dependent); assertTrue(id2 >= 0); assertEquals(id1, id2); context.invalidateObjects(master2, dependent); assertNotNull(Cayenne.objectForPK(context, GeneratedColumnTestEntity.class, id1)); assertNotNull(Cayenne.objectForPK(context, GeneratedColumnDep.class, id2)); } @Test public void testGeneratedDefaultValue() throws Exception { // fail("TODO: test insert with DEFAULT generated column...need custom // SQL to // build such table"); } @Test public void testPropagateToDependent() throws Exception { GeneratedColumnTestEntity idObject = context.newObject(GeneratedColumnTestEntity.class); idObject.setName("aaa"); GeneratedColumnDep dependent = idObject.getObjectContext().newObject(GeneratedColumnDep.class); dependent.setName("aaa"); dependent.setToMaster(idObject); context.commitChanges(); // this will throw an exception if id wasn't generated int id1 = Cayenne.intPKForObject(idObject); assertTrue(id1 >= 0); int id2 = Cayenne.intPKForObject(dependent); assertTrue(id2 >= 0); assertEquals(id1, id2); // refetch from DB context.invalidateObjects(idObject, dependent); assertNotNull(Cayenne.objectForPK(context, GeneratedColumnTestEntity.class, id1)); assertNotNull(Cayenne.objectForPK(context, GeneratedColumnDep.class, id2)); } }