http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java new file mode 100644 index 0000000..c2357b0 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java @@ -0,0 +1,209 @@ +/***************************************************************** + * 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.dbsync.merge; + +import org.apache.cayenne.access.DataNode; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider; +import org.apache.cayenne.dbsync.reverse.DbLoaderConfiguration; +import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig; +import org.apache.cayenne.dbsync.reverse.filters.PatternFilter; +import org.apache.cayenne.dbsync.reverse.filters.TableFilter; +import org.apache.cayenne.dbsync.unit.DbSyncCase; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.test.jdbc.DBHelper; +import org.apache.cayenne.unit.UnitDbAdapter; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Before; + +import java.sql.Connection; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public abstract class MergeCase extends DbSyncCase { + + @Inject + protected EntityResolver resolver; + @Inject + protected DataNode node; + protected DataMap map; + private Log logger = LogFactory.getLog(MergeCase.class); + @Inject + private DBHelper dbHelper; + @Inject + private ServerRuntime runtime; + @Inject + private UnitDbAdapter accessStackAdapter; + @Inject + private ServerCaseDataSourceFactory dataSourceFactory; + + @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 { + + // this map can't be safely modified in this test, as it is reset by DI + // container + // on every test + map = runtime.getDataDomain().getDataMap("testmap"); + + filterDataMap(); + + List<MergerToken> tokens = createMergeTokens(); + execute(tokens); + + assertTokensAndExecute(0, 0); + } + + protected DbMerger createMerger() { + return createMerger(null); + } + + protected DbMerger createMerger(ValueForNullProvider valueForNullProvider) { + return new DbMerger(mergerFactory(), valueForNullProvider); + } + + protected List<MergerToken> createMergeTokens() { + DbLoaderConfiguration loaderConfiguration = new DbLoaderConfiguration(); + loaderConfiguration.setFiltersConfig(FiltersConfig.create(null, null, + TableFilter.include("ARTIST|GALLERY|PAINTING|NEW_TABLE2?"), PatternFilter.INCLUDE_NOTHING)); + + return createMerger().createMergeTokens(node.getDataSource(), node.getAdapter(), map, loaderConfiguration); + } + + /** + * Remote binary pk {@link DbEntity} for {@link DbAdapter} not supporting + * that and so on. + */ + private void filterDataMap() { + // copied from AbstractAccessStack.dbEntitiesInInsertOrder + boolean excludeBinPK = accessStackAdapter.supportsBinaryPK(); + + if (!excludeBinPK) { + return; + } + + List<DbEntity> entitiesToRemove = new ArrayList<DbEntity>(); + + for (DbEntity ent : map.getDbEntities()) { + for (DbAttribute attr : ent.getAttributes()) { + // check for BIN PK or FK to BIN Pk + if (attr.getType() == Types.BINARY || attr.getType() == Types.VARBINARY + || attr.getType() == Types.LONGVARBINARY) { + + if (attr.isPrimaryKey() || attr.isForeignKey()) { + entitiesToRemove.add(ent); + break; + } + } + } + } + + for (DbEntity e : entitiesToRemove) { + map.removeDbEntity(e.getName(), true); + } + } + + protected void execute(List<MergerToken> tokens) { + MergerContext mergerContext = MergerContext.builder(map).dataNode(node).build(); + for (MergerToken tok : tokens) { + tok.execute(mergerContext); + } + } + + protected void execute(MergerToken token) throws Exception { + MergerContext mergerContext = MergerContext.builder(map).dataNode(node).build(); + token.execute(mergerContext); + } + + private void executeSql(String sql) throws Exception { + + try (Connection conn = dataSourceFactory.getSharedDataSource().getConnection();) { + + try (Statement st = conn.createStatement();) { + st.execute(sql); + } + } + } + + protected void assertTokens(List<MergerToken> tokens, int expectedToDb, int expectedToModel) { + int actualToDb = 0; + int actualToModel = 0; + for (MergerToken token : tokens) { + if (token.getDirection().isToDb()) { + actualToDb++; + } else if (token.getDirection().isToModel()) { + actualToModel++; + } + } + + assertEquals("tokens to db", expectedToDb, actualToDb); + assertEquals("tokens to model", expectedToModel, actualToModel); + } + + protected void assertTokensAndExecute(int expectedToDb, int expectedToModel) { + List<MergerToken> tokens = createMergeTokens(); + assertTokens(tokens, expectedToDb, expectedToModel); + execute(tokens); + } + + protected MergerTokenFactory mergerFactory() { + return runtime.getInjector().getInstance(MergerTokenFactoryProvider.class).get(node.getAdapter()); + } + + protected void dropTableIfPresent(String tableName) throws Exception { + + // must have a dummy datamap for the dummy table for the downstream code + // to work + DataMap map = new DataMap("dummy"); + map.setQuotingSQLIdentifiers(map.isQuotingSQLIdentifiers()); + DbEntity entity = new DbEntity(tableName); + map.addDbEntity(entity); + + AbstractToDbToken t = (AbstractToDbToken) mergerFactory().createDropTableToDb(entity); + + for (String sql : t.createSql(node.getAdapter())) { + + try { + executeSql(sql); + } catch (Exception e) { + logger.info("Exception dropping table " + tableName + ", probably abscent.."); + } + } + } +}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergerFactoryIT.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergerFactoryIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergerFactoryIT.java new file mode 100644 index 0000000..1fe7da7 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergerFactoryIT.java @@ -0,0 +1,310 @@ +/***************************************************************** + * 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.dbsync.merge; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.sql.Types; + +import org.apache.cayenne.CayenneDataObject; +import org.apache.cayenne.access.DataContext; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbJoin; +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.ObjAttribute; +import org.apache.cayenne.map.ObjEntity; +import org.junit.Test; + +public class MergerFactoryIT extends MergeCase { + + @Inject + private DataContext context; + + @Test + public void testAddAndDropColumnToDb() throws Exception { + DbEntity dbEntity = map.getDbEntity("PAINTING"); + assertNotNull(dbEntity); + + // create and add new column to model and db + DbAttribute column = new DbAttribute("NEWCOL1", Types.VARCHAR, dbEntity); + + column.setMandatory(false); + column.setMaxLength(10); + dbEntity.addAttribute(column); + assertTokensAndExecute(1, 0); + + // try merge once more to check that is was merged + assertTokensAndExecute(0, 0); + + // remove it from model and db + dbEntity.removeAttribute(column.getName()); + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + + @Test + public void testChangeVarcharSizeToDb() throws Exception { + DbEntity dbEntity = map.getDbEntity("PAINTING"); + assertNotNull(dbEntity); + + // create and add new column to model and db + DbAttribute column = new DbAttribute("NEWCOL2", Types.VARCHAR, dbEntity); + + column.setMandatory(false); + column.setMaxLength(10); + dbEntity.addAttribute(column); + assertTokensAndExecute(1, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // change size + column.setMaxLength(20); + + // merge to db + assertTokensAndExecute(1, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // clean up + dbEntity.removeAttribute(column.getName()); + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + + @Test + public void testMultipleTokensToDb() throws Exception { + DbEntity dbEntity = map.getDbEntity("PAINTING"); + assertNotNull(dbEntity); + + DbAttribute column1 = new DbAttribute("NEWCOL3", Types.VARCHAR, dbEntity); + column1.setMandatory(false); + column1.setMaxLength(10); + dbEntity.addAttribute(column1); + DbAttribute column2 = new DbAttribute("NEWCOL4", Types.VARCHAR, dbEntity); + column2.setMandatory(false); + column2.setMaxLength(10); + dbEntity.addAttribute(column2); + + assertTokensAndExecute(2, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // change size + column1.setMaxLength(20); + column2.setMaxLength(30); + + // merge to db + assertTokensAndExecute(2, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // clean up + dbEntity.removeAttribute(column1.getName()); + dbEntity.removeAttribute(column2.getName()); + assertTokensAndExecute(2, 0); + assertTokensAndExecute(0, 0); + } + + @Test + public void testAddTableToDb() throws Exception { + dropTableIfPresent("NEW_TABLE"); + + assertTokensAndExecute(0, 0); + + DbEntity dbEntity = new DbEntity("NEW_TABLE"); + + DbAttribute column1 = new DbAttribute("ID", Types.INTEGER, dbEntity); + column1.setMandatory(true); + column1.setPrimaryKey(true); + dbEntity.addAttribute(column1); + + DbAttribute column2 = new DbAttribute("NAME", Types.VARCHAR, dbEntity); + column2.setMaxLength(10); + column2.setMandatory(false); + dbEntity.addAttribute(column2); + + map.addDbEntity(dbEntity); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + + ObjEntity objEntity = new ObjEntity("NewTable"); + objEntity.setDbEntity(dbEntity); + ObjAttribute oatr1 = new ObjAttribute("name"); + oatr1.setDbAttributePath(column2.getName()); + oatr1.setType("java.lang.String"); + objEntity.addAttribute(oatr1); + map.addObjEntity(objEntity); + + for (int i = 0; i < 5; i++) { + CayenneDataObject dao = (CayenneDataObject) context.newObject(objEntity + .getName()); + dao.writeProperty(oatr1.getName(), "test " + i); + } + context.commitChanges(); + + // clear up + map.removeObjEntity(objEntity.getName(), true); + map.removeDbEntity(dbEntity.getName(), true); + resolver.refreshMappingCache(); + assertNull(map.getObjEntity(objEntity.getName())); + assertNull(map.getDbEntity(dbEntity.getName())); + assertFalse(map.getDbEntities().contains(dbEntity)); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + + @Test + public void testAddForeignKeyWithTable() throws Exception { + dropTableIfPresent("NEW_TABLE"); + + assertTokensAndExecute(0, 0); + + DbEntity dbEntity = new DbEntity("NEW_TABLE"); + + attr(dbEntity, "ID", Types.INTEGER, true, true); + attr(dbEntity, "NAME", Types.VARCHAR, false, false).setMaxLength(10); + attr(dbEntity, "ARTIST_ID", Types.BIGINT, false, false); + + map.addDbEntity(dbEntity); + + DbEntity artistDbEntity = map.getDbEntity("ARTIST"); + assertNotNull(artistDbEntity); + + // relation from new_table to artist + DbRelationship r1 = new DbRelationship("toArtistR1"); + r1.setSourceEntity(dbEntity); + r1.setTargetEntityName(artistDbEntity); + r1.setToMany(false); + r1.addJoin(new DbJoin(r1, "ARTIST_ID", "ARTIST_ID")); + dbEntity.addRelationship(r1); + + // relation from artist to new_table + DbRelationship r2 = new DbRelationship("toNewTableR2"); + r2.setSourceEntity(artistDbEntity); + r2.setTargetEntityName(dbEntity); + r2.setToMany(true); + r2.addJoin(new DbJoin(r2, "ARTIST_ID", "ARTIST_ID")); + artistDbEntity.addRelationship(r2); + + assertTokensAndExecute(2, 0); + assertTokensAndExecute(0, 0); + + // remove relationships + dbEntity.removeRelationship(r1.getName()); + artistDbEntity.removeRelationship(r2.getName()); + resolver.refreshMappingCache(); + /* + * Db -Rel 'toArtistR1' - NEW_TABLE 1 -> 1 ARTIST" +r2 = * Db -Rel 'toNewTableR2' - ARTIST 1 -> * NEW_TABLE" + * */ + assertTokensAndExecute(1, 1); + assertTokensAndExecute(0, 0); + + // clear up + // map.removeObjEntity(objEntity.getName(), true); + map.removeDbEntity(dbEntity.getName(), true); + resolver.refreshMappingCache(); + // assertNull(map.getObjEntity(objEntity.getName())); + assertNull(map.getDbEntity(dbEntity.getName())); + assertFalse(map.getDbEntities().contains(dbEntity)); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + + @Test + public void testAddForeignKeyAfterTable() throws Exception { + dropTableIfPresent("NEW_TABLE"); + + assertTokensAndExecute(0, 0); + + DbEntity dbEntity = new DbEntity("NEW_TABLE"); + attr(dbEntity, "ID", Types.INTEGER, true, true); + attr(dbEntity, "NAME", Types.VARCHAR, false, false).setMaxLength(10); + attr(dbEntity, "ARTIST_ID", Types.BIGINT, false, false); + + map.addDbEntity(dbEntity); + + DbEntity artistDbEntity = map.getDbEntity("ARTIST"); + assertNotNull(artistDbEntity); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + + // relation from new_table to artist + DbRelationship r1 = new DbRelationship("toArtistR1"); + r1.setSourceEntity(dbEntity); + r1.setTargetEntityName(artistDbEntity); + r1.setToMany(false); + r1.addJoin(new DbJoin(r1, "ARTIST_ID", "ARTIST_ID")); + dbEntity.addRelationship(r1); + + // relation from artist to new_table + DbRelationship r2 = new DbRelationship("toNewTableR2"); + r2.setSourceEntity(artistDbEntity); + r2.setTargetEntityName(dbEntity); + r2.setToMany(true); + r2.addJoin(new DbJoin(r2, "ARTIST_ID", "ARTIST_ID")); + artistDbEntity.addRelationship(r2); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + + // remove relationships + dbEntity.removeRelationship(r1.getName()); + artistDbEntity.removeRelationship(r2.getName()); + resolver.refreshMappingCache(); + /* + * Add Relationship ARTIST->NEW_TABLE To Model + * Drop Relationship NEW_TABLE->ARTIST To DB + * */ + assertTokensAndExecute(1, 1); + assertTokensAndExecute(0, 0); + + // clear up + // map.removeObjEntity(objEntity.getName(), true); + map.removeDbEntity(dbEntity.getName(), true); + resolver.refreshMappingCache(); + // assertNull(map.getObjEntity(objEntity.getName())); + assertNull(map.getDbEntity(dbEntity.getName())); + assertFalse(map.getDbEntities().contains(dbEntity)); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + + private static DbAttribute attr(DbEntity dbEntity, String name, int type, boolean mandatory, boolean primaryKey) { + DbAttribute column1 = new DbAttribute(name, type, dbEntity); + column1.setMandatory(mandatory); + column1.setPrimaryKey(primaryKey); + + dbEntity.addAttribute(column1); + return column1; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDbIT.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDbIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDbIT.java new file mode 100644 index 0000000..6391ad0 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDbIT.java @@ -0,0 +1,66 @@ +/***************************************************************** + * 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.dbsync.merge; + +import static org.junit.Assert.assertNotNull; + +import java.sql.Types; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.junit.Test; + +public class SetAllowNullToDbIT extends MergeCase { + + @Test + public void test() throws Exception { + DbEntity dbEntity = map.getDbEntity("PAINTING"); + assertNotNull(dbEntity); + + // create and add new column to model and db + DbAttribute column = new DbAttribute("NEWCOL2", Types.VARCHAR, dbEntity); + + try { + + column.setMandatory(true); + column.setMaxLength(10); + dbEntity.addAttribute(column); + assertTokensAndExecute(2, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // set null + column.setMandatory(false); + + // merge to db + assertTokensAndExecute(1, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // clean up + } finally { + dbEntity.removeAttribute(column.getName()); + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetNotNullToDbIT.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetNotNullToDbIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetNotNullToDbIT.java new file mode 100644 index 0000000..508d0f8 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetNotNullToDbIT.java @@ -0,0 +1,62 @@ +/***************************************************************** + * 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.dbsync.merge; + +import static org.junit.Assert.assertNotNull; + +import java.sql.Types; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.junit.Test; + +public class SetNotNullToDbIT extends MergeCase { + + @Test + public void test() throws Exception { + DbEntity dbEntity = map.getDbEntity("PAINTING"); + assertNotNull(dbEntity); + + // create and add new column to model and db + DbAttribute column = new DbAttribute("NEWCOL2", Types.VARCHAR, dbEntity); + + column.setMandatory(false); + column.setMaxLength(10); + dbEntity.addAttribute(column); + assertTokensAndExecute(1, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // set not null + column.setMandatory(true); + + // merge to db + assertTokensAndExecute(1, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // clean up + dbEntity.removeAttribute(column.getName()); + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDbIT.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDbIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDbIT.java new file mode 100644 index 0000000..3b513e7 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDbIT.java @@ -0,0 +1,58 @@ +/***************************************************************** + * 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.dbsync.merge; + +import java.sql.Types; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.junit.Test; + +public class SetPrimaryKeyToDbIT extends MergeCase { + + @Test + public void test() throws Exception { + dropTableIfPresent("NEW_TABLE"); + assertTokensAndExecute(0, 0); + + DbEntity dbEntity1 = new DbEntity("NEW_TABLE"); + + DbAttribute e1col1 = new DbAttribute("ID1", Types.INTEGER, dbEntity1); + e1col1.setMandatory(true); + e1col1.setPrimaryKey(true); + dbEntity1.addAttribute(e1col1); + map.addDbEntity(dbEntity1); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + + DbAttribute e1col2 = new DbAttribute("ID2", Types.INTEGER, dbEntity1); + e1col2.setMandatory(true); + dbEntity1.addAttribute(e1col2); + + assertTokensAndExecute(2, 0); + assertTokensAndExecute(0, 0); + + e1col1.setPrimaryKey(false); + e1col2.setPrimaryKey(true); + + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReversTest.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReversTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReversTest.java new file mode 100644 index 0000000..b23128c --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReversTest.java @@ -0,0 +1,89 @@ +/***************************************************************** + * 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.dbsync.merge; + +import org.apache.cayenne.dbsync.merge.factory.HSQLMergerTokenFactory; +import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbJoin; +import org.apache.cayenne.map.DbRelationship; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; + +import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbAttr; +import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbEntity; + +/** + * @since 4.0. + */ +public class TokensReversTest { + + @Test + public void testReverses() { + DbAttribute attr = dbAttr().build(); + DbEntity entity = dbEntity().attributes(attr).build(); + DbRelationship rel = new DbRelationship("rel"); + rel.setSourceEntity(entity); + rel.addJoin(new DbJoin(rel, attr.getName(), "dontKnow")); + + test(factory().createAddColumnToDb(entity, attr)); + test(factory().createAddColumnToModel(entity, attr)); + test(factory().createDropColumnToDb(entity, attr)); + test(factory().createDropColumnToModel(entity, attr)); + + test(factory().createAddRelationshipToDb(entity, rel)); + test(factory().createAddRelationshipToModel(entity, rel)); + test(factory().createDropRelationshipToDb(entity, rel)); + test(factory().createDropRelationshipToModel(entity, rel)); + + test(factory().createCreateTableToDb(entity)); + test(factory().createCreateTableToModel(entity)); + test(factory().createDropTableToDb(entity)); + test(factory().createDropTableToModel(entity)); + + test(factory().createSetAllowNullToDb(entity, attr)); + test(factory().createSetAllowNullToModel(entity, attr)); + test(factory().createSetNotNullToDb(entity, attr)); + test(factory().createSetNotNullToModel(entity, attr)); + + DbAttribute attr2 = dbAttr().build(); + test(factory().createSetColumnTypeToDb(entity, attr, attr2)); + test(factory().createSetColumnTypeToModel(entity, attr, attr2)); + + test(factory().createSetPrimaryKeyToDb(entity, Collections.singleton(attr), Collections.singleton(attr2), "PK")); + test(factory().createSetPrimaryKeyToModel(entity, Collections.singleton(attr), Collections.singleton(attr2), "PK")); + + test(factory().createSetValueForNullToDb(entity, attr, new DefaultValueForNullProvider())); + } + + private void test(MergerToken token1) { + MergerToken token2 = token1.createReverse(factory()).createReverse(factory()); + + Assert.assertEquals(token1.getTokenName(), token2.getTokenName()); + Assert.assertEquals(token1.getTokenValue(), token2.getTokenValue()); + Assert.assertEquals(token1.getDirection(), token2.getDirection()); + } + + private MergerTokenFactory factory() { + return new HSQLMergerTokenFactory(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensToModelExecutionTest.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensToModelExecutionTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensToModelExecutionTest.java new file mode 100644 index 0000000..b9abea1 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensToModelExecutionTest.java @@ -0,0 +1,80 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ +package org.apache.cayenne.dbsync.merge; + +import org.apache.cayenne.access.DataNode; +import org.apache.cayenne.dbsync.merge.factory.DefaultMergerTokenFactory; +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.junit.Test; + +import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dataMap; +import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbAttr; +import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbEntity; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @since 4.0. + */ +public class TokensToModelExecutionTest { + + @Test + public void testCreateAndDropTable() throws Exception { + DbEntity entity = dbEntity().build(); + + DataMap dataMap = dataMap().build(); + assertTrue(dataMap.getDbEntityMap().isEmpty()); + assertTrue(dataMap.getObjEntityMap().isEmpty()); + + MergerContext context = MergerContext.builder(dataMap).dataNode(new DataNode()).build(); + new DefaultMergerTokenFactory().createCreateTableToModel(entity).execute(context); + + assertEquals(1, dataMap.getDbEntityMap().size()); + assertEquals(1, dataMap.getObjEntities().size()); + assertEquals(entity, dataMap.getDbEntity(entity.getName())); + + new DefaultMergerTokenFactory().createDropTableToModel(entity).execute(context); + assertTrue(dataMap.getDbEntityMap().isEmpty()); + assertTrue(dataMap.getObjEntityMap().isEmpty()); + } + + @Test + public void testCreateAndDropColumn() throws Exception { + DbAttribute attr = dbAttr("attr").build(); + DbEntity entity = dbEntity().build(); + + DataMap dataMap = dataMap().with(entity).build(); + assertEquals(1, dataMap.getDbEntityMap().size()); + assertTrue(dataMap.getObjEntityMap().isEmpty()); + + MergerContext context = MergerContext.builder(dataMap).dataNode(new DataNode()).build(); + new DefaultMergerTokenFactory().createAddColumnToModel(entity, attr).execute(context); + + assertEquals(1, dataMap.getDbEntityMap().size()); + assertEquals(1, entity.getAttributes().size()); + assertEquals(attr, entity.getAttribute(attr.getName())); + + new DefaultMergerTokenFactory().createDropColumnToModel(entity, attr).execute(context); + assertEquals(1, dataMap.getDbEntityMap().size()); + assertTrue(entity.getAttributes().isEmpty()); + assertTrue(dataMap.getObjEntityMap().isEmpty()); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/ValueForNullIT.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/ValueForNullIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/ValueForNullIT.java new file mode 100644 index 0000000..e9910fd --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/ValueForNullIT.java @@ -0,0 +1,127 @@ +/***************************************************************** + * 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.dbsync.merge; + +import junit.framework.AssertionFailedError; +import org.apache.cayenne.DataObject; +import org.apache.cayenne.Persistent; +import org.apache.cayenne.access.DataContext; +import org.apache.cayenne.access.jdbc.SQLParameterBinding; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.ExpressionFactory; +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.ObjAttribute; +import org.apache.cayenne.map.ObjEntity; +import org.apache.cayenne.query.SelectQuery; +import org.junit.Test; + +import java.sql.Types; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class ValueForNullIT extends MergeCase { + + private static final String DEFAULT_VALUE_STRING = "DEFSTRING"; + + @Inject + private DataContext context; + + @Test + public void test() throws Exception { + DbEntity dbEntity = map.getDbEntity("PAINTING"); + assertNotNull(dbEntity); + ObjEntity objEntity = map.getObjEntity("Painting"); + assertNotNull(objEntity); + + // insert some rows before adding "not null" column + final int nrows = 10; + for (int i = 0; i < nrows; i++) { + DataObject o = (DataObject) context.newObject("Painting"); + o.writeProperty("paintingTitle", "ptitle" + i); + } + context.commitChanges(); + + // create and add new column to model and db + DbAttribute column = new DbAttribute("NEWCOL2", Types.VARCHAR, dbEntity); + + column.setMandatory(false); + column.setMaxLength(10); + dbEntity.addAttribute(column); + assertTrue(dbEntity.getAttributes().contains(column)); + assertEquals(column, dbEntity.getAttribute(column.getName())); + assertTokensAndExecute(1, 0); + + // need obj attr to be able to query + ObjAttribute objAttr = new ObjAttribute("newcol2"); + objAttr.setDbAttributePath(column.getName()); + objEntity.addAttribute(objAttr); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // set not null + column.setMandatory(true); + + // merge to db + assertTokensAndExecute(2, 0); + + // check that is was merged + assertTokensAndExecute(0, 0); + + // check values for null + Expression qual = ExpressionFactory.matchExp(objAttr.getName(), DEFAULT_VALUE_STRING); + SelectQuery query = new SelectQuery("Painting", qual); + List<Persistent> rows = context.performQuery(query); + assertEquals(nrows, rows.size()); + + // clean up + dbEntity.removeAttribute(column.getName()); + assertTokensAndExecute(1, 0); + assertTokensAndExecute(0, 0); + } + + @Override + protected DbMerger createMerger(final ValueForNullProvider valueForNullProvider) { + return super.createMerger(new DefaultValueForNullProvider() { + + @Override + protected SQLParameterBinding get(DbEntity entity, DbAttribute column) { + int type = column.getType(); + switch (type) { + case Types.VARCHAR: + return new SQLParameterBinding(DEFAULT_VALUE_STRING, type, -1); + default: + throw new AssertionFailedError("should not get here"); + } + } + + @Override + public boolean hasValueFor(DbEntity entity, DbAttribute column) { + return true; + } + + }); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/Builder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/Builder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/Builder.java new file mode 100644 index 0000000..baf8240 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/Builder.java @@ -0,0 +1,38 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +/** + * Base interface for all domain builders + * + * @since 4.0. + */ +public interface Builder<T> { + + /** + * Build valid object. If some required data omitted it will be filled with random data. + * */ + T build(); + + /** + * Build valid object and add some optional fields randomly. + * */ + T random(); + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DataMapBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DataMapBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DataMapBuilder.java new file mode 100644 index 0000000..a7aa11c --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DataMapBuilder.java @@ -0,0 +1,128 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.map.DataMap; +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.EntityResolver; +import org.apache.cayenne.map.ObjEntity; + +import java.util.Collections; + +/** + * @since 4.0. + */ +public class DataMapBuilder extends DefaultBuilder<DataMap> { + + public DataMapBuilder() { + this(new DataMap()); + } + + public DataMapBuilder(DataMap dataMap) { + super(dataMap); + } + + public DataMapBuilder with(DbEntity ... entities) { + for (DbEntity entity : entities) { + obj.addDbEntity(entity); + } + + return this; + } + + public DataMapBuilder with(DbEntityBuilder ... entities) { + for (DbEntityBuilder entity : entities) { + obj.addDbEntity(entity.build()); + } + + return this; + } + + public DataMapBuilder withDbEntities(int count) { + for (int i = 0; i < count; i++) { + obj.addDbEntity(ObjectMother.dbEntity().random()); + } + + return this; + } + + public DataMapBuilder with(ObjEntity... entities) { + for (ObjEntity entity : entities) { + obj.addObjEntity(entity); + } + + return this; + } + + public DataMapBuilder with(ObjEntityBuilder ... entities) { + for (ObjEntityBuilder entity : entities) { + obj.addObjEntity(entity.build()); + } + + return this; + } + + public DataMapBuilder withObjEntities(int count) { + for (int i = 0; i < count; i++) { + obj.addObjEntity(ObjectMother.objEntity().random()); + } + + return this; + } + + public DataMapBuilder join(String from, String to) { + return join(null, from, to); + } + + public DataMapBuilder join(String name, String from, String to) { + String[] fromSplit = from.split("\\."); + DbEntity fromEntity = obj.getDbEntity(fromSplit[0]); + if (fromEntity == null) { + throw new IllegalArgumentException("Entity '" + fromSplit[0] + "' is undefined"); + } + + String[] toSplit = to.split("\\."); + + fromEntity.addRelationship(new DbRelationshipBuilder(name) + .from(fromEntity, fromSplit[1]) + .to(toSplit[0], toSplit[1]) + + .build()); + + return this; + } + + public DataMap build() { + if (obj.getNamespace() == null) { + obj.setNamespace(new EntityResolver(Collections.singleton(obj))); + } + + return obj; + } + + @Override + public DataMap random() { + if (dataFactory.chance(90)) { + withDbEntities(dataFactory.getNumberUpTo(10)); + } + + + return build(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbAttributeBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbAttributeBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbAttributeBuilder.java new file mode 100644 index 0000000..2d600f3 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbAttributeBuilder.java @@ -0,0 +1,115 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.datafactory.DictionaryValueProvider; +import org.apache.cayenne.datafactory.ValueProvider; +import org.apache.cayenne.dba.TypesMapping; +import org.apache.cayenne.map.DbAttribute; + +import static org.apache.commons.lang.StringUtils.isEmpty; + +/** + * @since 4.0. + */ +public class DbAttributeBuilder extends DefaultBuilder<DbAttribute> { + + private static final ValueProvider<String> TYPES_RANDOM = new DictionaryValueProvider<String>(ValueProvider.RANDOM) { + @Override + protected String[] values() { + return TypesMapping.getDatabaseTypes(); + } + }; + + public DbAttributeBuilder() { + super(new DbAttribute()); + } + + public DbAttributeBuilder name() { + return name(getRandomJavaName()); + } + + public DbAttributeBuilder name(String name) { + obj.setName(name); + + return this; + } + + public DbAttributeBuilder type() { + return type(TYPES_RANDOM.randomValue()); + } + + public DbAttributeBuilder type(String item) { + obj.setType(TypesMapping.getSqlTypeByName(item)); + + return this; + } + + public DbAttributeBuilder typeInt() { + return type(TypesMapping.SQL_INTEGER); + } + + public DbAttributeBuilder typeBigInt() { + return type(TypesMapping.SQL_BIGINT); + } + + public DbAttributeBuilder typeVarchar(int length) { + type(TypesMapping.SQL_VARCHAR); + length(length); + + return this; + } + + private DbAttributeBuilder length(int length) { + obj.setMaxLength(length); + + return this; + } + + public DbAttributeBuilder primaryKey() { + obj.setPrimaryKey(true); + + return this; + } + + public DbAttributeBuilder mandatory() { + obj.setMandatory(true); + + return this; + } + + @Override + public DbAttribute build() { + if (isEmpty(obj.getName())) { + name(); + } + + if (obj.getType() == TypesMapping.NOT_DEFINED) { + type(); + } + + return obj; + } + + @Override + public DbAttribute random() { + return build(); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java new file mode 100644 index 0000000..03f0738 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbEntityBuilder.java @@ -0,0 +1,90 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.map.DbAttribute; +import org.apache.cayenne.map.DbEntity; +import org.apache.commons.lang.StringUtils; + +/** + * @since 4.0. + */ +public class DbEntityBuilder extends DefaultBuilder<DbEntity> { + + public DbEntityBuilder() { + super(new DbEntity()); + } + + public DbEntityBuilder name() { + return name(getRandomJavaName()); + } + + public DbEntityBuilder name(String name) { + obj.setName(name); + + return this; + } + + public DbEntityBuilder attributes(DbAttribute ... attributes) { + for (DbAttribute attribute : attributes) { + obj.addAttribute(attribute); + } + + return this; + } + + public DbEntityBuilder attributes(DbAttributeBuilder ... attributes) { + for (DbAttributeBuilder attribute : attributes) { + obj.addAttribute(attribute.build()); + } + + return this; + } + + public DbEntityBuilder attributes(int numberUpTo) { + for (int i = 0; i < numberUpTo; i++) { + try { + obj.addAttribute(new DbAttributeBuilder().random()); + } catch (IllegalArgumentException e) { + i--; // try again + } + } + + return this; + } + + + @Override + public DbEntity build() { + if (obj.getName() == null) { + obj.setName(StringUtils.capitalize(getRandomJavaName())); + } + + return obj; + } + + @Override + public DbEntity random() { + if (dataFactory.chance(99)) { + attributes(dataFactory.getNumberUpTo(20)); + } + + return build(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbRelationshipBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbRelationshipBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbRelationshipBuilder.java new file mode 100644 index 0000000..ae87549 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DbRelationshipBuilder.java @@ -0,0 +1,85 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.map.DbEntity; +import org.apache.cayenne.map.DbJoin; +import org.apache.cayenne.map.DbRelationship; + +/** + * @since 4.0. + */ +public class DbRelationshipBuilder extends DefaultBuilder<DbRelationship> { + + private String[] from; + private String[] to; + + public DbRelationshipBuilder() { + super(new DbRelationship()); + } + + public DbRelationshipBuilder(String name) { + super(new DbRelationship(name)); + } + + public DbRelationshipBuilder(DbRelationship obj) { + super(obj); + } + + public DbRelationshipBuilder name() { + return name(getRandomJavaName()); + } + + public DbRelationshipBuilder name(String name) { + obj.setName(name); + + return this; + } + + public DbRelationshipBuilder from(DbEntity entity, String ... columns) { + obj.setSourceEntity(entity); + this.from = columns; + + return this; + } + + public DbRelationshipBuilder to(String entityName, String ... columns) { + obj.setTargetEntityName(entityName); + this.to = columns; + + return this; + } + + @Override + public DbRelationship build() { + if (obj.getName() == null) { + name(); + } + + if (from.length != to.length) { + throw new IllegalStateException("from and to columns name size mismatch"); + } + + for (int i = 0; i < from.length; i++) { + obj.addJoin(new DbJoin(obj, from[i], to[i])); + } + + return obj; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java new file mode 100644 index 0000000..559347b --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/DefaultBuilder.java @@ -0,0 +1,57 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.datafactory.DataFactory; +import org.apache.commons.lang.StringUtils; + +/** + * @since 4.0. + */ +public abstract class DefaultBuilder<T> implements Builder<T> { + + protected final DataFactory dataFactory; + protected final T obj; + + + protected DefaultBuilder(T obj) { + this.dataFactory = new DataFactory(); + this.obj = obj; + } + + public String getRandomJavaName() { + int count = dataFactory.getNumberBetween(1, 5); + StringBuilder res = new StringBuilder(); + for (int i = 0; i < count; i++) { + res.append(StringUtils.capitalize(dataFactory.getRandomWord())); + } + + return StringUtils.uncapitalize(res.toString()); + } + + @Override + public T build() { + return obj; + } + + @Override + public T random() { + return build(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjAttributeBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjAttributeBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjAttributeBuilder.java new file mode 100644 index 0000000..a183c34 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjAttributeBuilder.java @@ -0,0 +1,67 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.map.ObjAttribute; + +/** + * @since 4.0. + */ +public class ObjAttributeBuilder extends DefaultBuilder<ObjAttribute> { + + public ObjAttributeBuilder() { + super(new ObjAttribute()); + } + + public ObjAttributeBuilder name() { + return name(getRandomJavaName()); + } + + public ObjAttributeBuilder name(String name) { + obj.setName(name); + + return this; + } + + public ObjAttributeBuilder type(Class type) { + obj.setType(type.getCanonicalName()); + + return this; + } + + public ObjAttributeBuilder dbPath(String path) { + obj.setDbAttributePath(path); + + return this; + } + + @Override + public ObjAttribute build() { + if (obj.getName() == null) { + name(); + } + + return obj; + } + + @Override + public ObjAttribute random() { + return build(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java new file mode 100644 index 0000000..f2f701c --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjEntityBuilder.java @@ -0,0 +1,98 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.map.ObjAttribute; +import org.apache.cayenne.map.ObjEntity; +import org.apache.commons.lang.StringUtils; + +/** + * @since 4.0. + */ +public class ObjEntityBuilder extends DefaultBuilder<ObjEntity> { + + public ObjEntityBuilder() { + super(new ObjEntity()); + } + + public ObjEntityBuilder name() { + return name(getRandomJavaName()); + } + + public ObjEntityBuilder name(String name) { + obj.setName(name); + + return this; + } + + public ObjEntityBuilder attributes(ObjAttribute... attributes) { + for (ObjAttribute attribute : attributes) { + obj.addAttribute(attribute); + } + + return this; + } + + public ObjEntityBuilder attributes(ObjAttributeBuilder ... attributes) { + for (ObjAttributeBuilder attribute : attributes) { + obj.addAttribute(attribute.build()); + } + + return this; + } + + public ObjEntityBuilder attributes(int numberUpTo) { + for (int i = 0; i < numberUpTo; i++) { + obj.addAttribute(new ObjAttributeBuilder().random()); + } + + return this; + } + + + @Override + public ObjEntity build() { + if (obj.getName() == null) { + obj.setName(StringUtils.capitalize(getRandomJavaName())); + } + + return obj; + } + + @Override + public ObjEntity random() { + if (dataFactory.chance(99)) { + attributes(dataFactory.getNumberUpTo(20)); + } + + return build(); + } + + public ObjEntityBuilder clazz(String s) { + obj.setClassName(s); + + return this; + } + + public ObjEntityBuilder dbEntity(String table) { + obj.setDbEntityName(table); + + return this; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjectMother.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjectMother.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjectMother.java new file mode 100644 index 0000000..0097fe1 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/builders/ObjectMother.java @@ -0,0 +1,70 @@ +/***************************************************************** + * 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.dbsync.merge.builders; + +import org.apache.cayenne.map.DataMap; + +/** + * Factory for test data see pattern definition: + * http://martinfowler.com/bliki/ObjectMother.html + * + * @since 4.0. + */ +public class ObjectMother { + + public static DataMapBuilder dataMap() { + return new DataMapBuilder(); + } + + public static DataMapBuilder dataMap(DataMap dataMap) { + return new DataMapBuilder(dataMap); + } + + public static DbEntityBuilder dbEntity() { + return new DbEntityBuilder(); + } + + public static DbEntityBuilder dbEntity(String name) { + return new DbEntityBuilder().name(name); + } + + public static ObjEntityBuilder objEntity() { + return new ObjEntityBuilder(); + } + + public static ObjEntityBuilder objEntity(String packageName, String className, String table) { + return new ObjEntityBuilder() + .name(className) + .clazz(packageName + "." + className) + .dbEntity(table); + } + + public static ObjAttributeBuilder objAttr(String name) { + return new ObjAttributeBuilder().name(name); + } + + public static DbAttributeBuilder dbAttr(String name) { + return dbAttr().name(name); + } + + public static DbAttributeBuilder dbAttr() { + return new DbAttributeBuilder(); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/2f7b1d53/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java new file mode 100644 index 0000000..a902754 --- /dev/null +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java @@ -0,0 +1,430 @@ +/***************************************************************** + * 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.dbsync.reverse; + +import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig; +import org.apache.cayenne.dbsync.reverse.filters.PatternFilter; +import org.apache.cayenne.dbsync.reverse.filters.TableFilter; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dba.TypesMapping; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.map.*; +import org.apache.cayenne.unit.UnitDbAdapter; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.sql.Types; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.*; + +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public class DbLoaderIT extends ServerCase { + + public static final DbLoaderConfiguration CONFIG = new DbLoaderConfiguration(); + @Inject + private ServerRuntime runtime; + + @Inject + private DbAdapter adapter; + + @Inject + private ServerCaseDataSourceFactory dataSourceFactory; + + @Inject + private UnitDbAdapter accessStackAdapter; + + private DbLoader loader; + + @Before + public void setUp() throws Exception { + loader = new DbLoader(dataSourceFactory.getSharedDataSource().getConnection(), adapter, null); + } + + @After + public void tearDown() throws Exception { + loader.getConnection().close(); + } + + @Test + public void testGetTableTypes() throws Exception { + + List<?> tableTypes = loader.getTableTypes(); + + assertNotNull(tableTypes); + + String tableLabel = adapter.tableTypeForTable(); + if (tableLabel != null) { + assertTrue("Missing type for table '" + tableLabel + "' - " + tableTypes, tableTypes.contains(tableLabel)); + } + + String viewLabel = adapter.tableTypeForView(); + if (viewLabel != null) { + assertTrue("Missing type for view '" + viewLabel + "' - " + tableTypes, tableTypes.contains(viewLabel)); + } + } + + @Test + public void testGetTables() throws Exception { + + String tableLabel = adapter.tableTypeForTable(); + + List<DetectedDbEntity> tables = loader.createTableLoader(null, null, TableFilter.everything()) + .getDbEntities(TableFilter.everything(), new String[]{tableLabel}); + + assertNotNull(tables); + + boolean foundArtist = false; + + for (DetectedDbEntity table : tables) { + if ("ARTIST".equalsIgnoreCase(table.getName())) { + foundArtist = true; + break; + } + } + + assertTrue("'ARTIST' is missing from the table list: " + tables, foundArtist); + } + + @Test + public void testGetTablesWithWrongCatalog() throws Exception { + + DbLoaderConfiguration config = new DbLoaderConfiguration(); + config.setFiltersConfig( + FiltersConfig.create("WRONG", null, TableFilter.everything(), PatternFilter.INCLUDE_NOTHING)); + List<DetectedDbEntity> tables = loader + .createTableLoader("WRONG", null, TableFilter.everything()) + .getDbEntities(TableFilter.everything(), new String[]{adapter.tableTypeForTable()}); + + assertNotNull(tables); + assertTrue(tables.isEmpty()); + } + + @Test + public void testGetTablesWithWrongSchema() throws Exception { + + DbLoaderConfiguration config = new DbLoaderConfiguration(); + config.setFiltersConfig( + FiltersConfig.create(null, "WRONG", TableFilter.everything(), PatternFilter.INCLUDE_NOTHING)); + List<DetectedDbEntity> tables = loader + .createTableLoader(null, "WRONG", TableFilter.everything()) + .getDbEntities(TableFilter.everything(), new String[]{adapter.tableTypeForTable()}); + + assertNotNull(tables); + assertTrue(tables.isEmpty()); + } + + @Test + public void testLoadWithMeaningfulPK() throws Exception { + + DataMap map = new DataMap(); + String[] tableLabel = { adapter.tableTypeForTable() }; + + loader.setCreatingMeaningfulPK(true); + + List<DbEntity> entities = loader + .createTableLoader(null, null, TableFilter.everything()) + .loadDbEntities(map, CONFIG, tableLabel); + + loader.loadObjEntities(map, CONFIG, entities); + + ObjEntity artist = map.getObjEntity("Artist"); + assertNotNull(artist); + + ObjAttribute id = artist.getAttribute("artistId"); + assertNotNull(id); + } + + /** + * DataMap loading is in one big test method, since breaking it in + * individual tests would require multiple reads of metatdata which is + * extremely slow on some RDBMS (Sybase). + */ + @Test + public void testLoad() throws Exception { + + boolean supportsUnique = runtime.getDataDomain().getDataNodes().iterator().next().getAdapter() + .supportsUniqueConstraints(); + boolean supportsLobs = accessStackAdapter.supportsLobs(); + boolean supportsFK = accessStackAdapter.supportsFKConstraints(); + + DataMap map = new DataMap(); + map.setDefaultPackage("foo.x"); + + String tableLabel = adapter.tableTypeForTable(); + + // *** TESTING THIS *** + List<DbEntity> entities = loader + .createTableLoader(null, null, TableFilter.everything()) + .loadDbEntities(map, CONFIG, new String[]{adapter.tableTypeForTable()}); + + + assertDbEntities(map); + + if (supportsLobs) { + assertLobDbEntities(map); + } + + // *** TESTING THIS *** + loader.loadDbRelationships(CONFIG, null, null, entities); + + if (supportsFK) { + Collection<DbRelationship> rels = getDbEntity(map, "ARTIST").getRelationships(); + assertNotNull(rels); + assertTrue(!rels.isEmpty()); + + // test one-to-one + rels = getDbEntity(map, "PAINTING").getRelationships(); + assertNotNull(rels); + + // find relationship to PAINTING_INFO + DbRelationship oneToOne = null; + for (DbRelationship rel : rels) { + if ("PAINTING_INFO".equalsIgnoreCase(rel.getTargetEntityName())) { + oneToOne = rel; + break; + } + } + + assertNotNull("No relationship to PAINTING_INFO", oneToOne); + assertFalse("Relationship to PAINTING_INFO must be to-one", oneToOne.isToMany()); + assertTrue("Relationship to PAINTING_INFO must be to-one", oneToOne.isToDependentPK()); + + // test UNIQUE only if FK is supported... + if (supportsUnique) { + assertUniqueConstraintsInRelationships(map); + } + } + + // *** TESTING THIS *** + loader.setCreatingMeaningfulPK(false); + loader.loadObjEntities(map, CONFIG, entities); + + assertObjEntities(map); + + // now when the map is loaded, test + // various things + // selectively check how different types were processed + if (accessStackAdapter.supportsColumnTypeReengineering()) { + checkTypes(map); + } + } + + private void assertUniqueConstraintsInRelationships(DataMap map) { + // unfortunately JDBC metadata doesn't provide info for UNIQUE + // constraints.... + // cant reengineer them... + + // find rel to TO_ONEFK1 + /* + * Iterator it = getDbEntity(map, + * "TO_ONEFK2").getRelationships().iterator(); DbRelationship rel = + * (DbRelationship) it.next(); assertEquals("TO_ONEFK1", + * rel.getTargetEntityName()); + * assertFalse("UNIQUE constraint was ignored...", rel.isToMany()); + */ + } + + private void assertDbEntities(DataMap map) { + DbEntity dae = getDbEntity(map, "ARTIST"); + assertNotNull("Null 'ARTIST' entity, other DbEntities: " + map.getDbEntityMap(), dae); + assertEquals("ARTIST", dae.getName().toUpperCase()); + + DbAttribute a = getDbAttribute(dae, "ARTIST_ID"); + assertNotNull(a); + assertTrue(a.isPrimaryKey()); + assertFalse(a.isGenerated()); + + if (adapter.supportsGeneratedKeys()) { + DbEntity bag = getDbEntity(map, "GENERATED_COLUMN_TEST"); + DbAttribute id = getDbAttribute(bag, "GENERATED_COLUMN"); + assertTrue(id.isPrimaryKey()); + assertTrue(id.isGenerated()); + } + } + + private void assertObjEntities(DataMap map) { + + boolean supportsLobs = accessStackAdapter.supportsLobs(); + boolean supportsFK = accessStackAdapter.supportsFKConstraints(); + + ObjEntity ae = map.getObjEntity("Artist"); + assertNotNull(ae); + assertEquals("Artist", ae.getName()); + + // assert primary key is not an attribute + assertNull(ae.getAttribute("artistId")); + + if (supportsLobs) { + assertLobObjEntities(map); + } + + if (supportsFK) { + Collection<?> rels1 = ae.getRelationships(); + assertNotNull(rels1); + assertTrue(rels1.size() > 0); + } + + assertEquals("foo.x.Artist", ae.getClassName()); + } + + private void assertLobDbEntities(DataMap map) { + DbEntity blobEnt = getDbEntity(map, "BLOB_TEST"); + assertNotNull(blobEnt); + DbAttribute blobAttr = getDbAttribute(blobEnt, "BLOB_COL"); + assertNotNull(blobAttr); + assertTrue(msgForTypeMismatch(Types.BLOB, blobAttr), Types.BLOB == blobAttr.getType() + || Types.LONGVARBINARY == blobAttr.getType()); + + DbEntity clobEnt = getDbEntity(map, "CLOB_TEST"); + assertNotNull(clobEnt); + DbAttribute clobAttr = getDbAttribute(clobEnt, "CLOB_COL"); + assertNotNull(clobAttr); + assertTrue(msgForTypeMismatch(Types.CLOB, clobAttr), Types.CLOB == clobAttr.getType() + || Types.LONGVARCHAR == clobAttr.getType()); + +/* + DbEntity nclobEnt = getDbEntity(map, "NCLOB_TEST"); + assertNotNull(nclobEnt); + DbAttribute nclobAttr = getDbAttribute(nclobEnt, "NCLOB_COL"); + assertNotNull(nclobAttr); + assertTrue(msgForTypeMismatch(Types.NCLOB, nclobAttr), Types.NCLOB == nclobAttr.getType() + || Types.LONGVARCHAR == nclobAttr.getType()); +*/ + } + + private void assertLobObjEntities(DataMap map) { + ObjEntity blobEnt = map.getObjEntity("BlobTest"); + assertNotNull(blobEnt); + // BLOBs should be mapped as byte[] + ObjAttribute blobAttr = blobEnt.getAttribute("blobCol"); + assertNotNull("BlobTest.blobCol failed to doLoad", blobAttr); + assertEquals("byte[]", blobAttr.getType()); + + + ObjEntity clobEnt = map.getObjEntity("ClobTest"); + assertNotNull(clobEnt); + // CLOBs should be mapped as Strings by default + ObjAttribute clobAttr = clobEnt.getAttribute("clobCol"); + assertNotNull(clobAttr); + assertEquals(String.class.getName(), clobAttr.getType()); + + + ObjEntity nclobEnt = map.getObjEntity("NclobTest"); + assertNotNull(nclobEnt); + // CLOBs should be mapped as Strings by default + ObjAttribute nclobAttr = nclobEnt.getAttribute("nclobCol"); + assertNotNull(nclobAttr); + assertEquals(String.class.getName(), nclobAttr.getType()); + } + + private DbEntity getDbEntity(DataMap map, String name) { + DbEntity de = map.getDbEntity(name); + // sometimes table names get converted to lowercase + if (de == null) { + de = map.getDbEntity(name.toLowerCase()); + } + + return de; + } + + private DbAttribute getDbAttribute(DbEntity ent, String name) { + DbAttribute da = ent.getAttribute(name); + // sometimes table names get converted to lowercase + if (da == null) { + da = ent.getAttribute(name.toLowerCase()); + } + + return da; + } + + private DataMap originalMap() { + return runtime.getDataDomain().getDataNodes().iterator().next().getDataMaps().iterator().next(); + } + + /** + * Selectively check how different types were processed. + */ + public void checkTypes(DataMap map) { + DbEntity dbe = getDbEntity(map, "PAINTING"); + DbEntity floatTest = getDbEntity(map, "FLOAT_TEST"); + DbEntity smallintTest = getDbEntity(map, "SMALLINT_TEST"); + DbAttribute integerAttr = getDbAttribute(dbe, "PAINTING_ID"); + DbAttribute decimalAttr = getDbAttribute(dbe, "ESTIMATED_PRICE"); + DbAttribute varcharAttr = getDbAttribute(dbe, "PAINTING_TITLE"); + DbAttribute floatAttr = getDbAttribute(floatTest, "FLOAT_COL"); + DbAttribute smallintAttr = getDbAttribute(smallintTest, "SMALLINT_COL"); + + // check decimal + assertTrue(msgForTypeMismatch(Types.DECIMAL, decimalAttr), Types.DECIMAL == decimalAttr.getType() + || Types.NUMERIC == decimalAttr.getType()); + assertEquals(2, decimalAttr.getScale()); + + // check varchar + assertEquals(msgForTypeMismatch(Types.VARCHAR, varcharAttr), Types.VARCHAR, varcharAttr.getType()); + assertEquals(255, varcharAttr.getMaxLength()); + // check integer + assertEquals(msgForTypeMismatch(Types.INTEGER, integerAttr), Types.INTEGER, integerAttr.getType()); + // check float + assertTrue(msgForTypeMismatch(Types.FLOAT, floatAttr), Types.FLOAT == floatAttr.getType() + || Types.DOUBLE == floatAttr.getType() || Types.REAL == floatAttr.getType()); + + // check smallint + assertTrue(msgForTypeMismatch(Types.SMALLINT, smallintAttr), Types.SMALLINT == smallintAttr.getType() + || Types.INTEGER == smallintAttr.getType()); + } + + public void checkAllDBEntities(DataMap map) { + + for (DbEntity origEnt : originalMap().getDbEntities()) { + DbEntity newEnt = map.getDbEntity(origEnt.getName()); + for (DbAttribute origAttr : origEnt.getAttributes()) { + DbAttribute newAttr = newEnt.getAttribute(origAttr.getName()); + assertNotNull("No matching DbAttribute for '" + origAttr.getName(), newAttr); + assertEquals(msgForTypeMismatch(origAttr, newAttr), origAttr.getType(), newAttr.getType()); + // length and precision doesn't have to be the same + // it must be greater or equal + assertTrue(origAttr.getMaxLength() <= newAttr.getMaxLength()); + assertTrue(origAttr.getScale() <= newAttr.getScale()); + } + } + } + + private static String msgForTypeMismatch(DbAttribute origAttr, DbAttribute newAttr) { + return msgForTypeMismatch(origAttr.getType(), newAttr); + } + + private static String msgForTypeMismatch(int origType, DbAttribute newAttr) { + String nt = TypesMapping.getSqlNameByType(newAttr.getType()); + String ot = TypesMapping.getSqlNameByType(origType); + return attrMismatch(newAttr.getName(), "expected type: <" + ot + ">, but was <" + nt + ">"); + } + + private static String attrMismatch(String attrName, String msg) { + return "[Error loading attribute '" + attrName + "': " + msg + "]"; + } +}