You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2014/11/14 18:47:50 UTC

[49/50] [abbrv] cayenne git commit: Merge remote-tracking branch 'apache/master' into CAY-1946

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
index a994ce2,c8589e0..e538e48
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java
@@@ -671,16 -734,14 +671,15 @@@ public class DbLoader 
      }
  
      /**
 +     * By default we want to load Tables and Views for mo types
 +     *
 +     * @see DbLoader#getTableTypes()
-      *
-      * @since 3.2
+      * @since 4.0
       */
      public String[] getDefaultTableTypes() {
 -        String viewType = adapter.tableTypeForView();
 -        String tableType = adapter.tableTypeForTable();
 -
 -        // use types that are not null
          List<String> list = new ArrayList<String>(2);
 +
 +        String viewType = adapter.tableTypeForView();
          if (viewType != null) {
              list.add(viewType);
          }
@@@ -699,8 -760,8 +698,8 @@@
       * tables and views.
       * 
       * @since 1.0.7
-      * @deprecated since 3.2 use
+      * @deprecated since 4.0 use
 -     *             {@link #load(DataMap, String, String, String, String...)}
 +     *             {@link #load(org.apache.cayenne.map.DataMap, DbLoaderConfiguration, String...)}
       *             method that supports catalogs.
       */
      @Deprecated
@@@ -723,8 -781,8 +722,8 @@@
       * contains default mapping of the tables and views. Allows to limit types
       * of tables to read.
       * 
-      * @deprecated since 3.2 use
+      * @deprecated since 4.0 use
 -     *             {@link #load(DataMap, String, String, String, String...)}
 +     *             {@link #load(org.apache.cayenne.map.DataMap, DbLoaderConfiguration, String...)}
       *             method that supports catalogs.
       */
      @Deprecated
@@@ -755,16 -799,21 +754,16 @@@
       * schema, table name and table type patterns and fills the specified
       * DataMap object with DB and object mapping info.
       * 
-      * @since 3.2
+      * @since 4.0
       */
 -    public void load(DataMap dataMap, String catalogPattern, String schemaPattern, String tablePattern,
 -            String... tableTypes) throws SQLException {
 -
 -        if (tablePattern == null) {
 -            tablePattern = WILDCARD;
 -        }
 +    public void load(DataMap dataMap, DbLoaderConfiguration config, String... tableTypes) throws SQLException {
  
 -        List<DbEntity> tables = getTables(catalogPattern, schemaPattern, tablePattern, tableTypes);
 +        List<DbEntity> entities = loadDbEntities(dataMap, config, getTables(config, tableTypes));
  
 -        if (loadDbEntities(dataMap, tables)) {
 -            loadDbRelationships(dataMap);
 +        if (entities != null) {
 +            loadDbRelationships(dataMap, config, entities);
 +            loadObjEntities(dataMap, config, entities);
  
 -            loadObjEntities(dataMap);
              flattenManyToManyRelationships(dataMap);
              fireObjEntitiesAddedEvents(dataMap);
          }
@@@ -798,17 -829,12 +797,17 @@@
       * </p>
       * 
       * @since 1.1
-      * @deprecated since 3.2 use
-      *             {@link #loadProcedures(org.apache.cayenne.map.DataMap, org.apache.cayenne.access.loader.DbLoaderConfiguration)} that
+      * @deprecated since 4.0 use
+      *             {@link #loadProcedures(DataMap, String, String, String)} that
       *             supports "catalog" pattern.
       */
 +    @Deprecated
      public void loadProceduresFromDB(String schemaPattern, String namePattern, DataMap dataMap) throws SQLException {
 -        loadProcedures(dataMap, null, schemaPattern, namePattern);
 +        DbLoaderConfiguration configuration = new DbLoaderConfiguration();
 +        configuration.setFiltersConfig(new FiltersConfig(new EntityFilters(
 +                new DbPath(null, schemaPattern), NULL, NULL, include(namePattern))));
 +
 +        loadProcedures(dataMap, configuration);
      }
  
      /**
@@@ -820,27 -846,61 +819,27 @@@
       * be invoked explicitly by the user. </i>
       * </p>
       * 
-      * @since 3.2
+      * @since 4.0
       */
 -    public void loadProcedures(DataMap dataMap, String catalogPattern, String schemaPattern, String namePattern)
 +    public Map<String, Procedure> loadProcedures(DataMap dataMap, DbLoaderConfiguration config)
              throws SQLException {
  
 -        Map<String, Procedure> procedures = null;
 -
 -        // get procedures
 -        ResultSet rs = getMetaData().getProcedures(catalogPattern, schemaPattern, namePattern);
 -        try {
 -            while (rs.next()) {
 -                String name = rs.getString("PROCEDURE_NAME");
 -
 -                // TODO: this will be moved to Delegate...
 -                if (EXCLUDED_PROCEDURES.contains(name)) {
 -                    logger.info("skipping Cayenne PK procedure: " + name);
 -                    continue;
 -                }
 -
 -                String catalog = rs.getString("PROCEDURE_CAT");
 -                String schema = rs.getString("PROCEDURE_SCHEM");
 -
 -                short type = rs.getShort("PROCEDURE_TYPE");
 -
 -                Procedure procedure = new Procedure(name);
 -                procedure.setCatalog(catalog);
 -                procedure.setSchema(schema);
 -
 -                switch (type) {
 -                case DatabaseMetaData.procedureNoResult:
 -                case DatabaseMetaData.procedureResultUnknown:
 -                    procedure.setReturningValue(false);
 -                    break;
 -                case DatabaseMetaData.procedureReturnsResult:
 -                    procedure.setReturningValue(true);
 -                    break;
 -                }
 +        Map<String, Procedure> procedures = loadProcedures(config);
 +        if (procedures.isEmpty()) {
 +            return procedures;
 +        }
  
 -                if (procedures == null) {
 -                    procedures = new HashMap<String, Procedure>();
 -                }
 +        loadProceduresColumns(procedures);
  
 -                procedures.put(procedure.getFullyQualifiedName(), procedure);
 -            }
 -        } finally {
 -            rs.close();
 +        for (Procedure procedure : procedures.values()) {
 +            dataMap.addProcedure(procedure);
          }
  
 -        // if nothing found, return
 -        if (procedures == null) {
 -            return;
 -        }
 +        return procedures;
 +    }
  
 -        // get columns
 -        ResultSet columnsRS = getMetaData().getProcedureColumns(null, schemaPattern, namePattern, null);
 +    private void loadProceduresColumns(Map<String, Procedure> procedures) throws SQLException {
 +        ResultSet columnsRS = getMetaData().getProcedureColumns(null, null, null, null); // TODO catalog, schema
          try {
              while (columnsRS.next()) {
  

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DefaultDbLoaderDelegate.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/main/java/org/apache/cayenne/access/loader/DefaultDbLoaderDelegate.java
index 2ca2d97,0000000..bc91162
mode 100644,000000..100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DefaultDbLoaderDelegate.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DefaultDbLoaderDelegate.java
@@@ -1,55 -1,0 +1,55 @@@
 +/*****************************************************************
 + *   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.loader;
 +
 +import org.apache.cayenne.CayenneException;
 +import org.apache.cayenne.access.DbLoaderDelegate;
 +import org.apache.cayenne.map.DbEntity;
 +import org.apache.cayenne.map.ObjEntity;
 +
 +/**
-  * @since 3.2.
++ * @since 4.0.
 + */
 +public class DefaultDbLoaderDelegate implements DbLoaderDelegate {
 +
 +    @Override
 +    public boolean overwriteDbEntity(DbEntity entity) throws CayenneException {
 +        return false;
 +    }
 +
 +    @Override
 +    public void dbEntityAdded(DbEntity entity) {
 +
 +    }
 +
 +    @Override
 +    public void dbEntityRemoved(DbEntity entity) {
 +
 +    }
 +
 +    @Override
 +    public void objEntityAdded(ObjEntity entity) {
 +
 +    }
 +
 +    @Override
 +    public void objEntityRemoved(ObjEntity entity) {
 +
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
index 5bc794a,0000000..932531f
mode 100644,000000..100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/filters/ExcludeFilter.java
@@@ -1,42 -1,0 +1,42 @@@
 +/*****************************************************************
 + *   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.loader.filters;
 +
 +import java.util.regex.Pattern;
 +
 +/**
-  * @since 3.2.
++ * @since 4.0
 + * @Immutable
 + */
 +public class ExcludeFilter extends IncludeFilter {
 +
 +    ExcludeFilter(Pattern pattern) {
 +        super(pattern);
 +    }
 +
 +    @Override
 +    public boolean isInclude(String obj) {
 +        return !super.isInclude(obj);
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return "-(" + super.getPattern() + ')';
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/map/DbRelationship.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/map/naming/DefaultUniqueNameGenerator.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/main/java/org/apache/cayenne/util/EntityMergeSupport.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderIT.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderIT.java
index 0000000,8bad0d2..04efb54
mode 000000,100644..100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderIT.java
@@@ -1,0 -1,388 +1,576 @@@
+ /*****************************************************************
+  *   Licensed to the Apache Software Foundation (ASF) under one
+  *  or more contributor license agreements.  See the NOTICE file
+  *  distributed with this work for additional information
+  *  regarding copyright ownership.  The ASF licenses this file
+  *  to you under the Apache License, Version 2.0 (the
+  *  "License"); you may not use this file except in compliance
+  *  with the License.  You may obtain a copy of the License at
+  *
+  *    http://www.apache.org/licenses/LICENSE-2.0
+  *
+  *  Unless required by applicable law or agreed to in writing,
+  *  software distributed under the License is distributed on an
+  *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  *  KIND, either express or implied.  See the License for the
+  *  specific language governing permissions and limitations
+  *  under the License.
+  ****************************************************************/
+ 
+ package org.apache.cayenne.access;
+ 
++import java.sql.Types;
++import java.util.Collection;
++import java.util.List;
++
++import org.apache.cayenne.access.loader.DbLoaderConfiguration;
+ 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.DataMap;
+ import org.apache.cayenne.map.DbAttribute;
+ import org.apache.cayenne.map.DbEntity;
+ import org.apache.cayenne.map.DbRelationship;
+ import org.apache.cayenne.map.ObjAttribute;
+ import org.apache.cayenne.map.ObjEntity;
+ import org.apache.cayenne.unit.UnitDbAdapter;
+ 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.Test;
+ 
+ import java.sql.Types;
+ import java.util.Collection;
+ 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.assertNull;
+ import static org.junit.Assert.assertTrue;
+ 
+ @UseServerRuntime(ServerCase.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;
+ 
+     @Override
+     protected void setUpAfterInjection() throws Exception {
+         loader = new DbLoader(dataSourceFactory.getSharedDataSource().getConnection(), adapter, null);
+     }
+ 
+     @Override
+     protected void tearDownBeforeInjection() 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<DbEntity> tables = loader.getTables(null, null, "%", new String[] { tableLabel });
++        List<DbEntity> tables = loader.getTables(new DbLoaderConfiguration(), new String[] { tableLabel });
+ 
+         assertNotNull(tables);
+ 
+         boolean foundArtist = false;
+ 
+         for (DbEntity table : tables) {
+             if ("ARTIST".equalsIgnoreCase(table.getName())) {
+                 foundArtist = true;
+                 break;
+             }
+         }
+ 
+         assertTrue("'ARTIST' is missing from the table list: " + tables, foundArtist);
+     }
+ 
+     @Test
+     public void testLoadWithMeaningfulPK() throws Exception {
+ 
+         DataMap map = new DataMap();
 -        String tableLabel = adapter.tableTypeForTable();
++        String[] tableLabel = { adapter.tableTypeForTable() };
+ 
+         loader.setCreatingMeaningfulPK(true);
+ 
 -        List<DbEntity> testLoader = loader.getTables(null, null, "artist", new String[] { tableLabel });
 -        if (testLoader.size() == 0) {
 -            testLoader = loader.getTables(null, null, "ARTIST", new String[] { tableLabel });
++        List<DbEntity> testLoader = loader.getTables(CONFIG, tableLabel);
++        if (testLoader.isEmpty()) {
++            testLoader = loader.getTables(CONFIG, tableLabel);
+         }
+ 
 -        loader.loadDbEntities(map, testLoader);
++        List<DbEntity> entities = loader.loadDbEntities(map, CONFIG, testLoader);
++        loader.loadObjEntities(map, CONFIG, entities);
+ 
 -        loader.loadObjEntities(map);
+         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 ***
 -        loader.loadDbEntities(map, loader.getTables(null, null, "%", new String[] { tableLabel }));
++        List<DbEntity> entities = loader.loadDbEntities(map, CONFIG, loader.getTables(CONFIG, new String[]{tableLabel}));
+ 
+         assertDbEntities(map);
+ 
+         if (supportsLobs) {
+             assertLobDbEntities(map);
+         }
+ 
+         // *** TESTING THIS ***
 -        loader.loadDbRelationships(map);
++        loader.loadDbRelationships(map, CONFIG, entities);
+ 
+         if (supportsFK) {
+             Collection<DbRelationship> rels = getDbEntity(map, "ARTIST").getRelationships();
+             assertNotNull(rels);
 -            assertTrue(rels.size() > 0);
++            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);
++        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());
+ 
+         if (accessStackAdapter.supportsCatalogs()) {
+             assertNotNull(dae.getCatalog());
+             assertEquals("CAYENNE", dae.getCatalog().toUpperCase());
+         }
+ 
+         DbAttribute a = getDbAttribute(dae, "ARTIST_ID");
+         assertNotNull(a);
+         assertTrue(a.isPrimaryKey());
+         assertFalse(a.isGenerated());
+ 
+         if (adapter.supportsGeneratedKeys()) {
+             DbEntity bag = getDbEntity(map, "BAG");
+             DbAttribute id = bag.getAttribute("ID");
+             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());
+     }
+ 
+     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 load", blobAttr);
++        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());
+     }
+ 
+     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 String msgForTypeMismatch(DbAttribute origAttr, DbAttribute newAttr) {
++    private static String msgForTypeMismatch(DbAttribute origAttr, DbAttribute newAttr) {
+         return msgForTypeMismatch(origAttr.getType(), newAttr);
+     }
+ 
 -    private String msgForTypeMismatch(int origType, DbAttribute 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 String attrMismatch(String attrName, String msg) {
 -        StringBuffer buf = new StringBuffer();
 -        buf.append("[Error loading attribute '").append(attrName).append("': ").append(msg).append("]");
 -        return buf.toString();
++    private static String attrMismatch(String attrName, String msg) {
++        return "[Error loading attribute '" + attrName + "': " + msg + "]";
++    }
++
++/*
++    TODO
++
++    @Test
++    public void testCreateLoader() throws Exception {
++
++        DbLoader loader = parameters.createLoader(mock(DbAdapter.class), connection,
++                mock(DbLoaderDelegate.class));
++        assertNotNull(loader);
++        assertSame(connection, loader.getConnection());
++
++        assertTrue(loader.includeTableName("dummy"));
++    }
++
++    @Test
++    public void testCreateLoader_IncludeExclude() throws Exception {
++        DbImportConfiguration parameters = new DbImportConfiguration();
++        parameters.setIncludeTables("a,b,c*");
++
++        DbLoader loader1 = parameters.createLoader(mock(DbAdapter.class), mock(Connection.class),
++                mock(DbLoaderDelegate.class));
++
++        assertFalse(loader1.includeTableName("dummy"));
++        assertFalse(loader1.includeTableName("ab"));
++        assertTrue(loader1.includeTableName("a"));
++        assertTrue(loader1.includeTableName("b"));
++        assertTrue(loader1.includeTableName("cd"));
++
++        parameters.setExcludeTables("cd");
++
++        DbLoader loader2 = parameters.createLoader(mock(DbAdapter.class), mock(Connection.class),
++                mock(DbLoaderDelegate.class));
++
++        assertFalse(loader2.includeTableName("dummy"));
++        assertFalse(loader2.includeTableName("ab"));
++        assertTrue(loader2.includeTableName("a"));
++        assertTrue(loader2.includeTableName("b"));
++        assertFalse(loader2.includeTableName("cd"));
++        assertTrue(loader2.includeTableName("cx"));
++    }
++
++
++    @Test
++    public void testCreateLoader_MeaningfulPk_Default() throws Exception {
++        DbImportConfiguration parameters = new DbImportConfiguration();
++        assertNull(parameters.getMeaningfulPkTables());
++
++        DbLoader loader1 = parameters.createLoader(mock(DbAdapter.class), mock(Connection.class),
++                mock(DbLoaderDelegate.class));
++
++        DataMap map = new DataMap();
++
++        DbEntity e1 = new DbEntity("e1");
++        DbAttribute pk = new DbAttribute("pk", Types.INTEGER, e1);
++        pk.setPrimaryKey(true);
++        e1.addAttribute(pk);
++        DbAttribute nonPk = new DbAttribute("nonPk", Types.INTEGER, e1);
++        e1.addAttribute(nonPk);
++
++        map.addDbEntity(e1);
++
++        // DbLoader is so ugly and hard to test..
++        Field dbEntityList = DbLoader.class.getDeclaredField("dbEntityList");
++        dbEntityList.setAccessible(true);
++        List<DbEntity> entities = (List<DbEntity>) dbEntityList.get(loader1);
++        entities.add(e1);
++
++        loader1.loadObjEntities(map, entities);
++
++        ObjEntity oe1 = map.getObjEntity("E1");
++        assertEquals(1, oe1.getAttributes().size());
++        assertNotNull(oe1.getAttribute("nonPk"));
++    }
++
++    @Test
++    public void testCreateLoader_MeaningfulPk_Specified() throws Exception {
++        DbImportConfiguration parameters = new DbImportConfiguration();
++        parameters.setMeaningfulPkTables("a*");
++
++        DbLoader loader1 = parameters.createLoader(mock(DbAdapter.class), mock(Connection.class),
++                mock(DbLoaderDelegate.class));
++
++        // DbLoader is so ugly and hard to test..
++        Field dbEntityList = DbLoader.class.getDeclaredField("dbEntityList");
++        dbEntityList.setAccessible(true);
++        Collection<DbEntity> entities = (List<DbEntity>) dbEntityList.get(loader1);
++
++        DataMap map = new DataMap();
++
++        DbEntity e1 = new DbEntity("e1");
++        DbAttribute pk = new DbAttribute("pk", Types.INTEGER, e1);
++        pk.setPrimaryKey(true);
++        e1.addAttribute(pk);
++        DbAttribute nonPk = new DbAttribute("nonPk", Types.INTEGER, e1);
++        e1.addAttribute(nonPk);
++
++        map.addDbEntity(e1);
++        entities.add(e1);
++
++        DbEntity a1 = new DbEntity("a1");
++        DbAttribute apk = new DbAttribute("pk", Types.INTEGER, a1);
++        apk.setPrimaryKey(true);
++        a1.addAttribute(apk);
++        DbAttribute anonPk = new DbAttribute("nonPk", Types.INTEGER, a1);
++        a1.addAttribute(anonPk);
++
++        map.addDbEntity(a1);
++        entities.add(a1);
++
++        loader1.loadObjEntities(map, entities);
++
++        ObjEntity oe1 = map.getObjEntity("E1");
++        assertEquals(1, oe1.getAttributes().size());
++        assertNotNull(oe1.getAttribute("nonPk"));
++
++        ObjEntity oe2 = map.getObjEntity("A1");
++        assertEquals(2, oe2.getAttributes().size());
++        assertNotNull(oe2.getAttribute("nonPk"));
++        assertNotNull(oe2.getAttribute("pk"));
++    }
++
++    @Test
++    public void testCreateLoader_UsePrimitives_False() throws Exception {
++        DbImportConfiguration parameters = new DbImportConfiguration();
++        parameters.setUsePrimitives(false);
++
++        DbLoader loader1 = parameters.createLoader(mock(DbAdapter.class), mock(Connection.class),
++                mock(DbLoaderDelegate.class));
++
++        DataMap map = new DataMap();
++
++        DbEntity e1 = new DbEntity("e1");
++        DbAttribute nonPk = new DbAttribute("nonPk", Types.INTEGER, e1);
++        e1.addAttribute(nonPk);
++
++        map.addDbEntity(e1);
++
++        // DbLoader is so ugly and hard to test..
++        Field dbEntityList = DbLoader.class.getDeclaredField("dbEntityList");
++        dbEntityList.setAccessible(true);
++        List<DbEntity> entities = (List<DbEntity>) dbEntityList.get(loader1);
++        entities.add(e1);
++
++        loader1.loadObjEntities(map, entities);
++
++        ObjEntity oe1 = map.getObjEntity("E1");
++
++        ObjAttribute oa1 = oe1.getAttribute("nonPk");
++        assertEquals("java.lang.Integer", oa1.getType());
++    }
++
++    @Test
++    public void testCreateLoader_UsePrimitives_True() throws Exception {
++        DbImportConfiguration parameters = new DbImportConfiguration();
++        parameters.setUsePrimitives(true);
++
++        DbLoader loader1 = parameters.createLoader(mock(DbAdapter.class), mock(Connection.class),
++                mock(DbLoaderDelegate.class));
++
++        DataMap map = new DataMap();
++
++        DbEntity e1 = new DbEntity("e1");
++        DbAttribute nonPk = new DbAttribute("nonPk", Types.INTEGER, e1);
++        e1.addAttribute(nonPk);
++
++        map.addDbEntity(e1);
++
++        // DbLoader is so ugly and hard to test..
++        Field dbEntityList = DbLoader.class.getDeclaredField("dbEntityList");
++        dbEntityList.setAccessible(true);
++        List<DbEntity> entities = (List<DbEntity>) dbEntityList.get(loader1);
++        entities.add(e1);
++
++        loader1.loadObjEntities(map, entities);
++
++        ObjEntity oe1 = map.getObjEntity("E1");
++
++        ObjAttribute oa1 = oe1.getAttribute("nonPk");
++        assertEquals("int", oa1.getType());
+     }
++*/
++
+ }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderPartialIT.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderPartialIT.java
index 0000000,4b0a9cd..4cac2b2
mode 000000,100644..100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderPartialIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DbLoaderPartialIT.java
@@@ -1,0 -1,122 +1,123 @@@
+ /*****************************************************************
+  *   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.CayenneException;
+ import org.apache.cayenne.dba.DbAdapter;
+ import org.apache.cayenne.di.Inject;
+ import org.apache.cayenne.map.DataMap;
+ import org.apache.cayenne.map.DbEntity;
++import org.apache.cayenne.access.loader.DefaultDbLoaderDelegate;
+ 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.Test;
+ 
+ import java.util.Collection;
+ 
+ import static org.junit.Assert.assertEquals;
+ import static org.junit.Assert.assertNotNull;
+ 
+ @UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+ public class DbLoaderPartialIT extends ServerCase {
+ 
+     @Inject
+     private DbAdapter adapter;
+ 
+     @Inject
+     private ServerCaseDataSourceFactory dataSourceFactory;
+ 
+     private DbLoader loader;
+ 
+     @Override
+     protected void setUpAfterInjection() throws Exception {
+         loader = new DbLoader(
+                 dataSourceFactory.getSharedDataSource().getConnection(),
+                 adapter,
+                 new DefaultDbLoaderDelegate() {
+ 
+                     public boolean overwriteDbEntity(DbEntity ent) throws CayenneException {
+                         return !(ent.getName().equalsIgnoreCase("ARTIST")
+                                 || ent.getName().equalsIgnoreCase("PAINTING"));
+                     }
+                 });
+     }
+ 
+     @Override
+     protected void tearDownBeforeInjection() throws Exception {
+         loader.getConnection().close();
+     }
+ 
+     /**
+      * Tests that FKs are properly loaded when the relationship source is not loaded. See
+      * CAY-479. This test will perform two reverse engineers. The second reverse engineer
+      * will skip two tables that share relationships with PAINTING. Relationships in
+      * ARTIST and GALLERY should remain unmodified, and all PAINTING relationships should
+      * be loaded.
+      */
+     @Test
+     public void testPartialLoad() throws Exception {
+ 
+         DataMap map = new DataMap();
+         String tableLabel = adapter.tableTypeForTable();
+ 
+         loader.loadDataMapFromDB(null, "%", new String[] {
+             tableLabel
+         }, map);
+ 
+         Collection<?> rels = getDbEntity(map, "ARTIST").getRelationships();
+         assertNotNull(rels);
+         int artistRels = rels.size();
+ 
+         rels = getDbEntity(map, "GALLERY").getRelationships();
+         assertNotNull(rels);
+         int galleryRels = rels.size();
+ 
+         rels = getDbEntity(map, "PAINTING").getRelationships();
+         assertNotNull(rels);
+         int paintingRels = rels.size();
+ 
+         loader.loadDataMapFromDB(null, "%", new String[] {
+             tableLabel
+         }, map);
+ 
+         rels = getDbEntity(map, "ARTIST").getRelationships();
+         assertNotNull(rels);
+         assertEquals(artistRels, rels.size());
+ 
+         rels = getDbEntity(map, "GALLERY").getRelationships();
+         assertNotNull(rels);
+         assertEquals(galleryRels, rels.size());
+ 
+         rels = getDbEntity(map, "PAINTING").getRelationships();
+         assertNotNull(rels);
+         assertEquals(paintingRels, rels.size());
+     }
+ 
+     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;
+     }
+ }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/test/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntityTest.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/test/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntityTest.java
index 4348edf,0000000..ad4238f
mode 100644,000000..100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntityTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntityTest.java
@@@ -1,106 -1,0 +1,113 @@@
 +/*****************************************************************
 + *   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.loader;
 +
- import junit.framework.TestCase;
- 
 +import org.apache.cayenne.configuration.ConfigurationNameMapper;
 +import org.apache.cayenne.configuration.ConfigurationTree;
 +import org.apache.cayenne.configuration.DataChannelDescriptor;
 +import org.apache.cayenne.configuration.DataMapLoader;
 +import org.apache.cayenne.configuration.DefaultConfigurationNameMapper;
 +import org.apache.cayenne.configuration.XMLDataChannelDescriptorLoader;
 +import org.apache.cayenne.configuration.XMLDataMapLoader;
 +import org.apache.cayenne.di.AdhocObjectFactory;
 +import org.apache.cayenne.di.Binder;
 +import org.apache.cayenne.di.ClassLoaderManager;
 +import org.apache.cayenne.di.DIBootstrap;
 +import org.apache.cayenne.di.Injector;
 +import org.apache.cayenne.di.Module;
 +import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
 +import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
 +import org.apache.cayenne.map.DataMap;
 +import org.apache.cayenne.map.ObjEntity;
 +import org.apache.cayenne.map.Relationship;
 +import org.apache.cayenne.map.naming.LegacyNameGenerator;
 +import org.apache.cayenne.resource.URLResource;
++import org.junit.Before;
++import org.junit.Test;
 +
 +import java.net.URL;
 +import java.util.ArrayList;
 +
- public class ManyToManyCandidateEntityTest extends TestCase {
++import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertNull;
++
++public class ManyToManyCandidateEntityTest {
 +
 +    private DataMap map;
 +
-     @Override
++    @Before
 +    public void setUp() throws Exception {
 +        Module testModule = new Module() {
 +
 +            public void configure(Binder binder) {
 +                binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
 +                binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
 +                binder.bind(DataMapLoader.class).to(XMLDataMapLoader.class);
 +                binder.bind(ConfigurationNameMapper.class).to(DefaultConfigurationNameMapper.class);
 +            }
 +        };
 +
 +        Injector injector = DIBootstrap.createInjector(testModule);
 +
 +        // create and initialize loader instance to test
 +        XMLDataChannelDescriptorLoader loader = new XMLDataChannelDescriptorLoader();
 +        injector.injectMembers(loader);
 +
 +        String testConfigName = "relationship-optimisation";
 +        URL url = getClass().getResource("cayenne-" + testConfigName + ".xml");
 +
 +        ConfigurationTree<DataChannelDescriptor> tree = loader.load(new URLResource(url));
 +
 +        map = tree.getRootNode().getDataMap(testConfigName);
 +    }
 +
++    @Test
 +    public void testMatchingForManyToManyEntity() throws Exception {
 +        ObjEntity manyToManyEntity = map.getObjEntity("Table1Table2");
 +
 +        assertNotNull(ManyToManyCandidateEntity.build(manyToManyEntity));
 +    }
 +
++    @Test
 +    public void testMatchingForNotManyToManyEntity() throws Exception {
 +        ObjEntity entity = map.getObjEntity("Table1");
 +
 +        assertNull(ManyToManyCandidateEntity.build(entity));
 +    }
 +
++    @Test
 +    public void testOptimisationForManyToManyEntity() {
 +        ObjEntity manyToManyEntity = map.getObjEntity("Table1Table2");
 +
 +        ManyToManyCandidateEntity.build(manyToManyEntity).optimizeRelationships(new LegacyNameGenerator());
 +
 +        ObjEntity table1Entity = map.getObjEntity("Table1");
 +        ObjEntity table2Entity = map.getObjEntity("Table2");
 +
 +        assertEquals(1, table1Entity.getRelationships().size());
 +        assertEquals(table2Entity, new ArrayList<Relationship>(table1Entity.getRelationships()).get(0)
 +                .getTargetEntity());
 +
 +        assertEquals(1, table2Entity.getRelationships().size());
 +        assertEquals(table1Entity, new ArrayList<Relationship>(table2Entity.getRelationships()).get(0)
 +                .getTargetEntity());
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
----------------------------------------------------------------------
diff --cc cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
index 1305229,d8fac76..2e75d6b
--- a/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/merge/MergeCase.java
@@@ -18,15 -18,7 +18,8 @@@
   ****************************************************************/
  package org.apache.cayenne.merge;
  
- import java.sql.Connection;
- import java.sql.Statement;
- import java.sql.Types;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
- 
  import org.apache.cayenne.access.DataNode;
 +import org.apache.cayenne.access.loader.DbLoaderConfiguration;
  import org.apache.cayenne.configuration.server.ServerRuntime;
  import org.apache.cayenne.dba.DbAdapter;
  import org.apache.cayenne.di.Inject;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-tools/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-tools/src/main/java/org/apache/cayenne/tools/AntDataPortDelegate.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java
----------------------------------------------------------------------
diff --cc cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java
index 0407649,b641759..23fe2bd
--- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbImporterTask.java
@@@ -121,32 -114,31 +121,32 @@@ public class DbImporterTask extends Tas
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setOverwrite(boolean overwrite) {
 -        parameters.setOverwrite(overwrite);
 +        config.setOverwrite(overwrite);
      }
  
      /**
-      * @deprecated since 3.2 use {@link #setSchema(String)}
+      * @deprecated since 4.0 use {@link #setSchema(String)}
       */
 +    @Deprecated
      public void setSchemaName(String schemaName) {
 -        this.schemaName = schemaName;
 +        this.setSchema(schemaName);
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setSchema(String schema) {
 -        parameters.setSchema(schema);
 +        filterBuilder.schema(schema);
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setDefaultPackage(String defaultPackage) {
 -        parameters.setDefaultPackage(defaultPackage);
 +        config.setDefaultPackage(defaultPackage);
      }
  
      public void setTablePattern(String tablePattern) {
@@@ -162,21 -154,17 +162,21 @@@
      }
  
      /**
-      * @deprecated since 3.2 use {@link #setMeaningfulPkTables(String)}
+      * @deprecated since 4.0 use {@link #setMeaningfulPkTables(String)}
       */
      public void setMeaningfulPk(boolean meaningfulPk) {
 -        this.meaningfulPk = meaningfulPk;
 +        log("'meaningfulPk' property is deprecated. Use 'meaningfulPkTables' pattern instead", Project.MSG_WARN);
 +
 +        if (meaningfulPk) {
 +            setMeaningfulPkTables("*");
 +        }
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setMeaningfulPkTables(String meaningfulPkTables) {
 -        parameters.setMeaningfulPkTables(meaningfulPkTables);
 +        config.setMeaningfulPkTables(meaningfulPkTables);
      }
  
      public void setNamingStrategy(String namingStrategy) {
@@@ -208,67 -196,43 +208,67 @@@
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setIncludeTables(String includeTables) {
 -        parameters.setIncludeTables(includeTables);
 +        filterBuilder.includeTables(includeTables);
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setExcludeTables(String excludeTables) {
 -        parameters.setExcludeTables(excludeTables);
 +        filterBuilder.excludeTables(excludeTables);
      }
  
      /**
-      * @since 3.2
+      * @since 4.0
       */
      public void setUsePrimitives(boolean usePrimitives) {
 -        parameters.setUsePrimitives(usePrimitives);
 +        config.setUsePrimitives(usePrimitives);
      }
  
 -    private void initSchema() {
 -        if (schemaName != null) {
 -            log("'schemaName' property is deprecated. Use 'schema' instead", Project.MSG_WARN);
 -        }
 +    public void addConfiguredIncludeColumn(IncludeColumn includeColumn) {
 +        reverseEngineering.addIncludeColumn(includeColumn);
 +    }
  
 -        if (parameters.getSchema() == null) {
 -            parameters.setSchema(schemaName);
 -        }
 +    public void addConfiguredExcludeColumn(ExcludeColumn excludeColumn) {
 +        reverseEngineering.addExcludeColumn(excludeColumn);
      }
  
 -    private void initMeaningfulPkTables() {
 -        if (meaningfulPk) {
 -            log("'meaningfulPk' property is deprecated. Use 'meaningfulPkTables' pattern instead", Project.MSG_WARN);
 -        }
 +    public void addConfiguredIncludeTable(IncludeTable includeTable) {
 +        reverseEngineering.addIncludeTable(includeTable);
 +    }
  
 -        if (parameters.getMeaningfulPkTables() == null && meaningfulPk) {
 -            parameters.setMeaningfulPkTables("*");
 -        }
 +    public void addConfiguredExcludeTable(ExcludeTable excludeTable) {
 +        reverseEngineering.addExcludeTable(excludeTable);
 +    }
 +
 +    public void addConfiguredIncludeProcedure(IncludeProcedure includeProcedure) {
 +        reverseEngineering.addIncludeProcedure(includeProcedure);
 +    }
 +
 +    public void addConfiguredExcludeProcedure(ExcludeProcedure excludeProcedure) {
 +        reverseEngineering.addExcludeProcedure(excludeProcedure);
 +    }
 +
 +    public void addConfiguredSchema(Schema schema) {
 +        reverseEngineering.addSchema(schema);
 +    }
 +
 +    public void addConfiguredCatalog(Catalog catalog) {
 +        reverseEngineering.addCatalog(catalog);
 +    }
 +
 +    public ReverseEngineering getReverseEngineering() {
 +        return reverseEngineering;
 +    }
 +
 +    public File getMap() {
 +        return config.getDataMapFile();
 +    }
 +
 +    public DbImportConfiguration toParameters() {
 +        return config;
      }
  }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportAction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportConfiguration.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/cayenne-tools/src/test/java/org/apache/cayenne/tools/NamePatternMatcherTest.java
----------------------------------------------------------------------
diff --cc cayenne-tools/src/test/java/org/apache/cayenne/tools/NamePatternMatcherTest.java
index a67e85b,67fe11c..14183dc
--- a/cayenne-tools/src/test/java/org/apache/cayenne/tools/NamePatternMatcherTest.java
+++ b/cayenne-tools/src/test/java/org/apache/cayenne/tools/NamePatternMatcherTest.java
@@@ -19,27 -19,44 +19,29 @@@
  
  package org.apache.cayenne.tools;
  
- import junit.framework.TestCase;
- 
 +import org.apache.cayenne.access.loader.NamePatternMatcher;
  import org.apache.tools.ant.Task;
+ import org.junit.Test;
+ 
+ import static org.junit.Assert.assertEquals;
  
 +import static org.apache.cayenne.access.loader.NamePatternMatcher.replaceWildcardInStringWithString;
 +
- public class NamePatternMatcherTest extends TestCase {
+ public class NamePatternMatcherTest {
  
      /**
       * Test pattern expansion.
       */
+     @Test
      public void testReplaceWildcardInStringWithString() throws Exception {
 -        assertEquals(null, NamePatternMatcher.replaceWildcardInStringWithString(
 -                "*",
 -                null,
 -                "Entity"));
 -        assertEquals("*.java", NamePatternMatcher.replaceWildcardInStringWithString(
 -                null,
 -                "*.java",
 -                "Entity"));
 -        assertEquals("Entity.java", NamePatternMatcher.replaceWildcardInStringWithString(
 -                "*",
 -                "*.java",
 -                "Entity"));
 -        assertEquals("java.Entity", NamePatternMatcher.replaceWildcardInStringWithString(
 -                "*",
 -                "java.*",
 -                "Entity"));
 -        assertEquals("Entity.Entity", NamePatternMatcher
 -                .replaceWildcardInStringWithString("*", "*.*", "Entity"));
 -        assertEquals("EntityEntity", NamePatternMatcher
 -                .replaceWildcardInStringWithString("*", "**", "Entity"));
 -        assertEquals("EditEntityReport.vm", NamePatternMatcher
 -                .replaceWildcardInStringWithString("*", "Edit*Report.vm", "Entity"));
 -        assertEquals("Entity", NamePatternMatcher.replaceWildcardInStringWithString(
 -                "*",
 -                "*",
 -                "Entity"));
 +        assertEquals(null, replaceWildcardInStringWithString("*", null, "Entity"));
 +        assertEquals("*.java", replaceWildcardInStringWithString(null, "*.java", "Entity"));
 +        assertEquals("Entity.java", replaceWildcardInStringWithString("*", "*.java", "Entity"));
 +        assertEquals("java.Entity", replaceWildcardInStringWithString("*", "java.*", "Entity"));
 +        assertEquals("Entity.Entity", replaceWildcardInStringWithString("*", "*.*", "Entity"));
 +        assertEquals("EntityEntity", replaceWildcardInStringWithString("*", "**", "Entity"));
 +        assertEquals("EditEntityReport.vm", replaceWildcardInStringWithString("*", "Edit*Report.vm", "Entity"));
 +        assertEquals("Entity", replaceWildcardInStringWithString("*", "*", "Entity"));
      }
  
      /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/plugins/maven-cayenne-plugin/pom.xml
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/CayenneGeneratorMojo.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java
----------------------------------------------------------------------
diff --cc plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java
index 1918106,2a11e23..1e07ca1
--- a/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java
+++ b/plugins/maven-cayenne-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java
@@@ -82,8 -71,88 +82,8 @@@ public class DbImporterMojo extends Abs
      private boolean overwrite;
  
      /**
 -     * DB schema to use for DB importing.
 -     * 
 -     * @parameter expression="${cdbimport.schemaName}"
 -     * @deprecated since 4.0 renamed to "schema"
 -     */
 -    private String schemaName;
 -
 -    /**
 -     * DB schema to use for DB importing.
 -     * 
 -     * @parameter expression="${cdbimport.catalog}"
 -     * @since 4.0
 -     */
 -    private String catalog;
 -
 -    /**
 -     * DB schema to use for DB importing.
 -     * 
 -     * @parameter expression="${cdbimport.schema}"
 -     * @since 4.0
 -     */
 -    private String schema;
 -
 -    /**
 -     * Pattern for tables to import from DB.
 -     * 
 -     * The default is to match against all tables.
 -     * 
 -     * @parameter expression="${cdbimport.tablePattern}"
 -     */
 -    private String tablePattern;
 -
 -    /**
 -     * A comma-separated list of Perl5 regex that defines tables that should be
 -     * included in import.
 -     * 
 -     * @parameter expression="${cdbimport.includeTables}"
 -     */
 -    private String includeTables;
 -
 -    /**
 -     * A comma-separated list of Perl5 regex that defines tables that should be
 -     * skipped from import.
 -     * 
 -     * @parameter expression="${cdbimport.excludeTables}"
 -     */
 -    private String excludeTables;
 -
 -    /**
 -     * Indicates whether stored procedures should be imported.
 -     * 
 -     * Default is <code>false</code>.
 -     * 
 -     * @parameter expression="${cdbimport.importProcedures}"
 -     *            default-value="false"
 -     */
 -    private boolean importProcedures;
 -
 -    /**
 -     * Pattern for stored procedures to import from DB. This is only meaningful
 -     * if <code>importProcedures</code> is set to <code>true</code>.
 -     * 
 -     * The default is to match against all stored procedures.
 -     * 
 -     * @parameter expression="${cdbimport.procedurePattern}"
 -     */
 -    private String procedurePattern;
 -
 -    /**
 -     * Indicates whether primary keys should be mapped as meaningful attributes
 -     * in the object entities.
 -     * 
 -     * Default is <code>false</code>.
 -     * 
 -     * @parameter expression="${cdbimport.meaningfulPk}" default-value="false"
 -     * @deprecated since 4.0 use meaningfulPkTables
 -     */
 -    private boolean meaningfulPk;
 -
 -    /**
       * @parameter expression="${cdbimport.meaningfulPkTables}"
-      * @since 3.2
+      * @since 4.0
       */
      private String meaningfulPkTables;
  
@@@ -146,92 -215,6 +146,92 @@@
       */
      private boolean usePrimitives;
  
 +    private final ReverseEngineering reverseEngineering = new ReverseEngineering();
 +
 +    private final EntityFilters.Builder filterBuilder = new EntityFilters.Builder();
 +
 +    /**
 +     * DB schema to use for DB importing.
 +     *
 +     * @parameter expression="${cdbimport.schemaName}"
-      * @deprecated since 3.2 renamed to "schema"
++     * @deprecated since 4.0 renamed to "schema"
 +     */
 +    private void setSchemaName(String schemaName) {
 +        getLog().warn("'schemaName' property is deprecated. Use 'schema' instead");
 +
 +        filterBuilder.schema(schemaName);
 +    }
 +
 +    /**
 +     * DB schema to use for DB importing.
 +     *
 +     * @parameter expression="${cdbimport.catalog}"
-      * @since 3.2
++     * @since 4.0
 +     */
 +    private void setCatalog(String catalog) {
 +        filterBuilder.catalog(catalog);
 +    }
 +
 +    /**
 +     * DB schema to use for DB importing.
 +     *
 +     * @parameter expression="${cdbimport.schema}"
-      * @since 3.2
++     * @since 4.0
 +     */
 +    private void setSchema(String schema) {
 +        filterBuilder.schema(schema);
 +    }
 +
 +    /**
 +     * Pattern for tables to import from DB.
 +     *
 +     * The default is to match against all tables.
 +     *
 +     * @parameter expression="${cdbimport.tablePattern}"
 +     */
 +    private void setTablePattern(String tablePattern) {
 +        filterBuilder.includeTables(tablePattern);
 +    }
 +
 +    /**
 +     * Indicates whether stored procedures should be imported.
 +     *
 +     * Default is <code>false</code>.
 +     *
 +     * @parameter expression="${cdbimport.importProcedures}"
 +     *            default-value="false"
 +     */
 +    private void setImportProcedures(boolean importProcedures) {
 +        filterBuilder.setProceduresFilters(importProcedures ? FilterFactory.TRUE : FilterFactory.NULL);
 +    }
 +
 +    /**
 +     * Pattern for stored procedures to import from DB. This is only meaningful
 +     * if <code>importProcedures</code> is set to <code>true</code>.
 +     *
 +     * The default is to match against all stored procedures.
 +     *
 +     * @parameter expression="${cdbimport.procedurePattern}"
 +     */
 +    private void setProcedurePattern(String procedurePattern) {
 +        filterBuilder.includeProcedures(procedurePattern);
 +    }
 +
 +    /**
 +     * Indicates whether primary keys should be mapped as meaningful attributes
 +     * in the object entities.
 +     *
 +     * Default is <code>false</code>.
 +     *
 +     * @parameter expression="${cdbimport.meaningfulPk}" default-value="false"
-      * @deprecated since 3.2 use meaningfulPkTables
++     * @deprecated since 4.0 use meaningfulPkTables
 +     */
 +    public void setMeaningfulPk(boolean meaningfulPk) {
 +        getLog().warn("'meaningfulPk' property is deprecated. Use 'meaningfulPkTables' pattern instead");
 +
 +        this.meaningfulPkTables = meaningfulPk ? "*" : null;
 +    }
 +
      public void execute() throws MojoExecutionException, MojoFailureException {
  
          Log logger = new MavenLogger(this);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/16a8cec6/plugins/pom.xml
----------------------------------------------------------------------