CAY-1946 CDbimport improvements a squashed commit of the branch CAY-1946_1 which is a result of a series of pull requests by Alex Kolonitsky
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/e2474202 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/e2474202 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/e2474202 Branch: refs/heads/master Commit: e2474202c009f33d1fa29dc0176ddecb0af78b42 Parents: acf54ba Author: aadamchik <aadamc...@apache.org> Authored: Thu Nov 27 10:12:07 2014 +0300 Committer: aadamchik <aadamc...@apache.org> Committed: Thu Nov 27 10:12:07 2014 +0300 ---------------------------------------------------------------------- .../org/apache/cayenne/access/DbLoader.java | 801 +++++++++---------- .../cayenne/access/DbLoaderConfiguration.java | 48 -- .../cayenne/access/DefaultDbLoaderDelegate.java | 54 -- .../access/ManyToManyCandidateEntity.java | 126 --- .../access/loader/BooleanNameFilter.java | 35 + .../access/loader/DbLoaderConfiguration.java | 115 +++ .../access/loader/DefaultDbLoaderDelegate.java | 55 ++ .../loader/ManyToManyCandidateEntity.java | 141 ++++ .../cayenne/access/loader/NameFilter.java | 27 + .../access/loader/NamePatternMatcher.java | 225 ++++++ .../cayenne/access/loader/filters/DbPath.java | 154 ++++ .../access/loader/filters/EntityFilters.java | 426 ++++++++++ .../access/loader/filters/ExcludeFilter.java | 42 + .../cayenne/access/loader/filters/Filter.java | 31 + .../access/loader/filters/FilterFactory.java | 94 +++ .../access/loader/filters/FiltersConfig.java | 180 +++++ .../access/loader/filters/IncludeFilter.java | 76 ++ .../access/loader/filters/ListFilter.java | 123 +++ .../cayenne/access/loader/mapper/DbType.java | 195 +++++ .../mapper/DefaultJdbc2JavaTypeMapper.java | 282 +++++++ .../loader/mapper/Jdbc2JavaTypeMapper.java | 33 + .../org/apache/cayenne/dba/JdbcAdapter.java | 93 ++- .../java/org/apache/cayenne/map/DataMap.java | 35 + .../org/apache/cayenne/map/DbRelationship.java | 21 +- .../java/org/apache/cayenne/map/ObjEntity.java | 13 +- .../map/naming/DefaultUniqueNameGenerator.java | 7 +- .../apache/cayenne/map/naming/NameCheckers.java | 6 +- .../org/apache/cayenne/merge/AddColumnToDb.java | 72 +- .../cayenne/merge/CreateTableToModel.java | 20 +- .../java/org/apache/cayenne/merge/DbMerger.java | 91 +-- .../apache/cayenne/merge/SetColumnTypeToDb.java | 68 +- .../apache/cayenne/util/EntityMergeSupport.java | 170 ++-- .../org/apache/cayenne/access/DbLoaderIT.java | 224 +++++- .../cayenne/access/DbLoaderPartialIT.java | 11 +- .../access/ManyToManyCandidateEntityTest.java | 113 --- .../loader/ManyToManyCandidateEntityTest.java | 113 +++ .../access/loader/filters/DbPathTest.java | 75 ++ .../loader/filters/EntityFiltersTest.java | 62 ++ .../loader/filters/FiltersConfigTest.java | 198 +++++ .../access/loader/filters/FiltersFactory.java | 44 + .../loader/filters/IncludeFilterTest.java | 37 + .../access/loader/mapper/DbTypeTest.java | 87 ++ .../org/apache/cayenne/merge/MergeCase.java | 5 +- .../cayenne-relationship-optimisation.xml | 4 - .../cayenne-relationship-optimisation.xml | 4 + .../loader/relationship-optimisation.map.xml | 43 + .../access/relationship-optimisation.map.xml | 43 - cayenne-tools/pom.xml | 26 + .../cayenne/gen/ClassGenerationAction.java | 2 +- .../cayenne/gen/ClientDataMapArtifact.java | 17 +- .../org/apache/cayenne/gen/DataMapArtifact.java | 13 +- .../map/naming/DefaultNameGenerator.java | 12 +- .../cayenne/tools/AntDataPortDelegate.java | 7 +- .../CayenneGeneratorEntityFilterAction.java | 20 +- .../cayenne/tools/CayenneGeneratorTask.java | 6 +- .../apache/cayenne/tools/DbImporterTask.java | 180 +++-- .../org/apache/cayenne/tools/ExcludeTable.java | 33 + .../cayenne/tools/NamePatternMatcher.java | 294 ------- .../cayenne/tools/dbimport/DbImportAction.java | 65 +- .../tools/dbimport/DbImportConfiguration.java | 322 +++----- .../tools/dbimport/config/AntNestedElement.java | 38 + .../cayenne/tools/dbimport/config/Catalog.java | 98 +++ .../config/DefaultReverseEngineeringLoader.java | 212 +++++ .../config/DefaultTypeMapperBuilder.java | 82 ++ .../tools/dbimport/config/ExcludeColumn.java | 31 + .../tools/dbimport/config/ExcludeProcedure.java | 31 + .../tools/dbimport/config/FilterContainer.java | 116 +++ .../dbimport/config/FiltersConfigBuilder.java | 171 ++++ .../tools/dbimport/config/IncludeColumn.java | 31 + .../tools/dbimport/config/IncludeProcedure.java | 31 + .../tools/dbimport/config/IncludeTable.java | 77 ++ .../tools/dbimport/config/PatternParam.java | 74 ++ .../dbimport/config/ReverseEngineering.java | 60 ++ .../config/ReverseEngineeringLoader.java | 31 + .../cayenne/tools/dbimport/config/Schema.java | 58 ++ .../cayenne/tools/dbimport/config/Type.java | 136 ++++ .../tools/dbimport/config/TypeMapper.java | 95 +++ .../cayenne/tools/DbImporterTaskTest.java | 168 ++++ .../cayenne/tools/NamePatternMatcherTest.java | 49 +- .../tools/dbimport/DbImportActionTest.java | 65 +- .../dbimport/DbImportConfigurationTest.java | 193 ----- .../DefaultReverseEngineeringLoaderTest.java | 208 +++++ .../config/FiltersConfigBuilderTest.java | 154 ++++ .../cayenne/tools/build-catalog-and-schema.xml | 78 ++ .../org/apache/cayenne/tools/build-catalog.xml | 83 ++ .../org/apache/cayenne/tools/build-flat.xml | 75 ++ .../org/apache/cayenne/tools/build-mapping.xml | 44 + ...ild-reverse-engineering-in-external-file.xml | 33 + .../org/apache/cayenne/tools/build-schema.xml | 83 ++ .../dbimport/build-include-table.map.xml-result | 37 + .../tools/dbimport/build-include-table.xml | 35 + .../tools/dbimport/build-include-table.xml.sql | 20 + .../config/reverseEngineering-ant-mapping.xml | 31 + .../reverseEngineering-catalog-and-schema.xml | 65 ++ .../config/reverseEngineering-catalog.xml | 68 ++ .../dbimport/config/reverseEngineering-flat.xml | 61 ++ .../config/reverseEngineering-maven-mapping.xml | 55 ++ .../config/reverseEngineering-mixed-mapping.xml | 43 + .../config/reverseEngineering-schema.xml | 69 ++ docs/doc/src/main/resources/UPGRADE.txt | 4 + .../modeler/action/CreateObjEntityAction.java | 209 +++-- .../cayenne/modeler/action/MigrateAction.java | 2 - .../dialog/datamap/PackageUpdateController.java | 20 +- .../modeler/dialog/db/DbLoaderHelper.java | 132 +-- .../dialog/db/DbLoaderOptionsDialog.java | 4 +- .../modeler/dialog/db/MergerOptions.java | 61 +- .../dialog/objentity/ClassNameUpdater.java | 31 +- .../dialog/objentity/EntitySyncController.java | 32 +- .../modeler/util/NameGeneratorPreferences.java | 14 +- plugins/maven-cayenne-plugin/pom.xml | 28 +- .../cayenne/tools/CayenneGeneratorMojo.java | 6 +- .../apache/cayenne/tools/DbImporterMojo.java | 309 ++++--- .../tools/DbImporterMojoConfigurationTest.java | 62 ++ .../cayenne/tools/DbImporterMojoTest.java | 109 ++- .../org/apache/cayenne/tools/config/pom-01.xml | 99 +++ .../tools/config/pom-catalog-and-schema.xml | 82 ++ .../apache/cayenne/tools/config/pom-catalog.xml | 85 ++ .../apache/cayenne/tools/config/pom-flat.xml | 78 ++ .../apache/cayenne/tools/config/pom-mapping.xml | 67 ++ .../apache/cayenne/tools/config/pom-schema.xml | 85 ++ .../dbimport/testFilteringWithSchema-pom.xml | 41 + .../testFilteringWithSchema.map.xml-result | 51 ++ .../tools/dbimport/testFilteringWithSchema.sql | 48 ++ .../testOldParamsSchemasAndTableExclude-pom.xml | 43 + ...dParamsSchemasAndTableExclude.map.xml-result | 37 + .../testOldParamsSchemasAndTableExclude.sql | 48 ++ .../dbimport/testSchemasAndTableExclude-pom.xml | 45 ++ .../testSchemasAndTableExclude.map.xml-result | 38 + .../dbimport/testSchemasAndTableExclude.sql | 48 ++ .../tools/dbimport/testSimpleFiltering-pom.xml | 41 + .../dbimport/testSimpleFiltering.map.xml-result | 37 + .../tools/dbimport/testSimpleFiltering.sql | 20 + .../tools/dbimport/testViewsExclude-pom.xml | 41 + .../dbimport/testViewsExclude.map.xml-result | 43 + .../cayenne/tools/dbimport/testViewsExclude.sql | 14 + plugins/pom.xml | 16 + 136 files changed, 8676 insertions(+), 2462 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java index c8589e0..8e81483 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoader.java @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. ****************************************************************/ - package org.apache.cayenne.access; import java.sql.Connection; @@ -28,12 +27,18 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.cayenne.CayenneException; +import org.apache.cayenne.access.loader.DbLoaderConfiguration; +import org.apache.cayenne.access.loader.ManyToManyCandidateEntity; +import org.apache.cayenne.access.loader.filters.EntityFilters; +import org.apache.cayenne.access.loader.filters.Filter; +import org.apache.cayenne.access.loader.filters.FiltersConfig; +import org.apache.cayenne.access.loader.filters.DbPath; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.map.DataMap; @@ -47,66 +52,47 @@ import org.apache.cayenne.map.Entity; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.map.Procedure; import org.apache.cayenne.map.ProcedureParameter; -import org.apache.cayenne.map.naming.LegacyNameGenerator; +import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator; import org.apache.cayenne.map.naming.ExportedKey; +import org.apache.cayenne.map.naming.LegacyNameGenerator; +import org.apache.cayenne.map.naming.NameCheckers; import org.apache.cayenne.map.naming.ObjectNameGenerator; import org.apache.cayenne.util.EntityMergeSupport; -import org.apache.cayenne.util.Util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import static org.apache.cayenne.access.loader.filters.FilterFactory.*; + /** * Utility class that does reverse engineering of the database. It can create * DataMaps using database meta data obtained via JDBC driver. */ public class DbLoader { - private static final Log logger = LogFactory.getLog(DbLoader.class); - - // TODO: remove this hardcoded stuff once delegate starts to support - // procedure - // loading... - private static final Collection<String> EXCLUDED_PROCEDURES = Arrays.asList("auto_pk_for_table", - "auto_pk_for_table;1" /* - * the last name is some Mac OS X Sybase - * artifact - */ - ); + private static final Log LOGGER = LogFactory.getLog(DbLoader.class); public static final String WILDCARD = "%"; - - /** List of db entities to process. */ - private List<DbEntity> dbEntityList = new ArrayList<DbEntity>(); + public static final String WILDCARD_PATTERN = ".*"; /** - * CAY-479 - need to track which entities are skipped in the loader so that - * relationships to non-skipped entities can be loaded + * CAY-479 - need to track which entities which are skipped during loading from db since it it already present in + * dataMap and haven't marked for overriding so that relationships to non-skipped entities can be loaded */ private Set<DbEntity> skippedEntities = new HashSet<DbEntity>(); - /** Creates a unique name for loaded relationship on the given entity. */ - private static String uniqueRelName(Entity entity, String preferredName) { - int currentSuffix = 1; - String relName = preferredName; + private final Connection connection; + private final DbAdapter adapter; + private final DbLoaderDelegate delegate; - while (entity.getRelationship(relName) != null || entity.getAttribute(relName) != null) { - relName = preferredName + currentSuffix; - currentSuffix++; - } - return relName; - } + private boolean creatingMeaningfulPK; + + private DatabaseMetaData metaData; - protected Connection connection; - protected DbAdapter adapter; - protected DatabaseMetaData metaData; - protected DbLoaderDelegate delegate; - protected String genericClassName; - protected boolean creatingMeaningfulPK; /** * Strategy for choosing names for entities, attributes and relationships */ - protected ObjectNameGenerator nameGenerator; + private ObjectNameGenerator nameGenerator; /** * Creates new DbLoader. @@ -131,7 +117,7 @@ public class DbLoader { /** * Returns DatabaseMetaData object associated with this DbLoader. */ - public DatabaseMetaData getMetaData() throws SQLException { + private DatabaseMetaData getMetaData() throws SQLException { if (metaData == null) { metaData = connection.getMetaData(); } @@ -148,7 +134,7 @@ public class DbLoader { /** * Returns true if the generator should map all primary key columns as * ObjAttributes. - * + * * @since 3.0 */ public boolean isCreatingMeaningfulPK() { @@ -165,32 +151,6 @@ public class DbLoader { } /** - * Returns a name of a generic class that should be used for all - * ObjEntities. The most common generic class is - * {@link org.apache.cayenne.CayenneDataObject}. If generic class name is - * null (which is the default), DbLoader will assign each entity a unique - * class name derived from the table name. - * - * @since 1.2 - */ - public String getGenericClassName() { - return genericClassName; - } - - /** - * Sets a name of a generic class that should be used for all ObjEntities. - * The most common generic class is - * {@link org.apache.cayenne.CayenneDataObject}. If generic class name is - * set to null (which is the default), DbLoader will assign each entity a - * unique class name derived from the table name. - * - * @since 1.2 - */ - public void setGenericClassName(String genericClassName) { - this.genericClassName = genericClassName; - } - - /** * Returns DbAdapter associated with this DbLoader. * * @since 1.1 @@ -200,31 +160,12 @@ public class DbLoader { } /** - * A method that return true if the given table name should be included. The - * default implementation include all tables. - */ - public boolean includeTableName(String tableName) { - return true; - } - - /** * Retrieves catalogs for the database associated with this DbLoader. * * @return List with the catalog names, empty Array if none found. */ public List<String> getCatalogs() throws SQLException { - List<String> catalogs = new ArrayList<String>(); - ResultSet rs = getMetaData().getCatalogs(); - - try { - while (rs.next()) { - String catalog_name = rs.getString(1); - catalogs.add(catalog_name); - } - } finally { - rs.close(); - } - return catalogs; + return getStrings(getMetaData().getCatalogs()); } /** @@ -233,23 +174,26 @@ public class DbLoader { * @return List with the schema names, empty Array if none found. */ public List<String> getSchemas() throws SQLException { - List<String> schemas = new ArrayList<String>(); - ResultSet rs = getMetaData().getSchemas(); + return getStrings(getMetaData().getSchemas()); + } + private static List<String> getStrings(ResultSet rs) throws SQLException { + List<String> strings = new ArrayList<String>(); try { while (rs.next()) { - String schema_name = rs.getString(1); - schemas.add(schema_name); + strings.add(rs.getString(1)); } } finally { rs.close(); } - return schemas; + return strings; } /** * Returns all the table types for the given database. Types may be such as - * "TABLE", "VIEW", "SYSTEM TABLE", etc. + * Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", + * "LOCAL TEMPORARY", "ALIAS", "SYNONYM"., etc. * * @return List of Strings, empty array if nothing found. */ @@ -271,32 +215,33 @@ public class DbLoader { * Returns all tables for given combination of the criteria. Tables returned * as DbEntities without any attributes or relationships. * - * @param catalogPattern - * The name of the catalog, may be null. - * @param schemaPattern - * The pattern for schema name, use "%" for wildcard. - * @param tableNamePattern - * The pattern for table names, % for wildcard, if null or "" - * defaults to "%". + * + * @param config + * * @param types * The types of table names to retrieve, null returns all types. - * @return List of TableInfo objects, empty array if nothing found. + * @return + * @since 3.2 */ - public List<DbEntity> getTables(String catalogPattern, String schemaPattern, String tableNamePattern, String[] types) + public List<DbEntity> getTables(DbLoaderConfiguration config, String[] types) throws SQLException { - if (logger.isDebugEnabled()) { - logger.debug("Read tables: catalog=" + catalogPattern + ", schema=" + schemaPattern + ", tableNames=" - + tableNamePattern); + List<DbEntity> tables = new LinkedList<DbEntity>(); + FiltersConfig filters = config.getFiltersConfig(); + for (DbPath path : filters.pathsForQueries()) { + tables.addAll(getDbEntities(filters, path, types)); + } - if (types != null && types.length > 0) { - for (String type : types) { - logger.debug("Read tables: table type=" + type); - } - } + return tables; + } + + private List<DbEntity> getDbEntities(FiltersConfig filters, DbPath dbPath, String[] types) throws SQLException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Read tables: catalog=" + dbPath.catalog + ", schema=" + dbPath.schema + ", types=" + + Arrays.toString(types)); } - ResultSet rs = getMetaData().getTables(catalogPattern, schemaPattern, tableNamePattern, types); + ResultSet rs = getMetaData().getTables(dbPath.catalog, dbPath.schema, WILDCARD, types); List<DbEntity> tables = new ArrayList<DbEntity>(); try { @@ -306,16 +251,22 @@ public class DbLoader { // in errors on reverse engineering as their names have special chars like // "/", etc. So skip them all together - // TODO: Andrus, 10/29/2005 - this type of filtering should be delegated to adapter String name = rs.getString("TABLE_NAME"); - if (name == null || name.startsWith("BIN$") || !includeTableName(name)) { + if (name == null) { continue; } DbEntity table = new DetectedDbEntity(name); - table.setCatalog(rs.getString("TABLE_CAT")); - table.setSchema(rs.getString("TABLE_SCHEM")); - tables.add(table); + + String catalog = rs.getString("TABLE_CAT"); + table.setCatalog(catalog); + + String schema = rs.getString("TABLE_SCHEM"); + table.setSchema(schema); + + if (filters.filter(new DbPath(catalog, schema)).tableFilter().isInclude(table)) { + tables.add(table); + } } } finally { rs.close(); @@ -328,104 +279,46 @@ public class DbLoader { * * @param map * DataMap to be populated with DbEntities. + * @param config * @param tables * The list of org.apache.cayenne.ashwood.dbutil.Table objects - * for which DbEntities must be created. - * @return false if loading must be immediately aborted. + * for which DbEntities must be created. @return false if loading must be immediately aborted. */ - public boolean loadDbEntities(DataMap map, List<? extends DbEntity> tables) throws SQLException { - this.dbEntityList = new ArrayList<DbEntity>(); + public List<DbEntity> loadDbEntities(DataMap map, DbLoaderConfiguration config, Collection<? extends DbEntity> tables) throws SQLException { + /** List of db entities to process. */ + List<DbEntity> dbEntityList = new ArrayList<DbEntity>(); for (DbEntity dbEntity : tables) { + // Check if there already is a DbEntity under such name // if so, consult the delegate what to do DbEntity oldEnt = map.getDbEntity(dbEntity.getName()); if (oldEnt != null) { if (delegate == null) { - // no delegate, don't know what to do, cancel import - break; + break; // no delegate, don't know what to do, cancel import + // TODO continue? } try { if (delegate.overwriteDbEntity(oldEnt)) { - logger.debug("Overwrite: " + oldEnt.getName()); + LOGGER.debug("Overwrite: " + oldEnt.getName()); map.removeDbEntity(oldEnt.getName(), true); delegate.dbEntityRemoved(oldEnt); } else { - logger.debug("Keep old: " + oldEnt.getName()); + LOGGER.debug("Keep old: " + oldEnt.getName()); - // cay-479 - need to track entities that were not loaded - // for + // cay-479 - need to track entities that were not loaded for // relationships exported to entities that were skippedEntities.add(oldEnt); continue; } } catch (CayenneException ex) { - logger.debug("Load canceled."); + LOGGER.debug("Load canceled."); - // cancel immediately - return false; + return null; // cancel immediately } } - // Create DbAttributes from column information -- - ResultSet rs = getMetaData().getColumns(dbEntity.getCatalog(), dbEntity.getSchema(), dbEntity.getName(), - "%"); - - try { - while (rs.next()) { - // for a reason not quiet apparent to me, Oracle sometimes - // returns duplicate record sets for the same table, messing - // up table - // names. E.g. for the system table "WK$_ATTR_MAPPING" - // columns are - // returned twice - as "WK$_ATTR_MAPPING" and - // "WK$$_ATTR_MAPPING"... - // Go figure - - String tableName = rs.getString("TABLE_NAME"); - if (!dbEntity.getName().equals(tableName)) { - logger.info("Incorrectly returned columns for '" + tableName + ", skipping."); - continue; - } - - // gets attribute's (column's) information - String columnName = rs.getString("COLUMN_NAME"); - - boolean allowNulls = rs.getBoolean("NULLABLE"); - int columnType = rs.getInt("DATA_TYPE"); - int columnSize = rs.getInt("COLUMN_SIZE"); - String typeName = rs.getString("TYPE_NAME"); - - // ignore precision of non-decimal columns - int decimalDigits = -1; - if (TypesMapping.isDecimal(columnType)) { - decimalDigits = rs.getInt("DECIMAL_DIGITS"); - if (rs.wasNull()) { - decimalDigits = -1; - } - } - - // create attribute delegating this task to adapter - DbAttribute attr = adapter.buildAttribute(columnName, typeName, columnType, columnSize, - decimalDigits, allowNulls); - if (adapter.supportsGeneratedKeys()) { - - // TODO: this actually throws on some drivers... need to - // ensure that 'supportsGeneratedKeys' check is enough - // to prevent an exception here. - String autoIncrement = rs.getString("IS_AUTOINCREMENT"); - if ("YES".equals(autoIncrement)) { - attr.setGenerated(true); - } - } - - attr.setEntity(dbEntity); - dbEntity.addAttribute(attr); - } - } finally { - rs.close(); - } map.addDbEntity(dbEntity); @@ -433,108 +326,166 @@ public class DbLoader { if (delegate != null) { delegate.dbEntityAdded(dbEntity); } + loadDbAttributes(config.getFiltersConfig(), dbEntity); - // delegate might have thrown this entity out... so check if it is - // still + // delegate might have thrown this entity out... so check if it is still // around before continuing processing if (map.getDbEntity(dbEntity.getName()) == dbEntity) { - this.dbEntityList.add(dbEntity); + dbEntityList.add(dbEntity); } } // get primary keys for each table and store it in dbEntity + getPrimaryKeysForEachTableAndStoreItInDbEntity(map, tables); + + // cay-479 - iterate skipped DbEntities to populate exported keys + for (DbEntity skippedEntity : skippedEntities) { + loadDbRelationships(map, skippedEntity, config); + } + + return dbEntityList; + + } + + private void getPrimaryKeysForEachTableAndStoreItInDbEntity(DataMap map, Collection<? extends DbEntity> tables) + throws SQLException { + for (DbEntity dbEntity : map.getDbEntities()) { - if (tables.contains(dbEntity)) { - String tableName = dbEntity.getName(); - ResultSet rs = metaData.getPrimaryKeys(dbEntity.getCatalog(), dbEntity.getSchema(), tableName); - try { - while (rs.next()) { - String columnName = rs.getString(4); - DbAttribute attribute = dbEntity.getAttribute(columnName); - - if (attribute != null) { - attribute.setPrimaryKey(true); - } else { - // why an attribute might be null is not quiet clear - // but there is a bug report 731406 indicating that - // it is - // possible - // so just print the warning, and ignore - logger.warn("Can't locate attribute for primary key: " + columnName); - } - - String pkName = rs.getString(6); - if ((pkName != null) && (dbEntity instanceof DetectedDbEntity)) { - ((DetectedDbEntity) dbEntity).setPrimaryKeyName(pkName); - } + if (!tables.contains(dbEntity)) { // TODO is it ok? equals is not overridden + continue; + } + + ResultSet rs = getMetaData().getPrimaryKeys(dbEntity.getCatalog(), dbEntity.getSchema(), dbEntity.getName()); + try { + while (rs.next()) { + String columnName = rs.getString(4); // COLUMN_NAME + DbAttribute attribute = dbEntity.getAttribute(columnName); + + if (attribute != null) { + attribute.setPrimaryKey(true); + } else { + // why an attribute might be null is not quiet clear + // but there is a bug report 731406 indicating that it is possible + // so just print the warning, and ignore + LOGGER.warn("Can't locate attribute for primary key: " + columnName); + } + + String pkName = rs.getString(6); // PK_NAME + if (pkName != null && dbEntity instanceof DetectedDbEntity) { + ((DetectedDbEntity) dbEntity).setPrimaryKeyName(pkName); } - } finally { - rs.close(); } + } finally { + rs.close(); } } + } - // cay-479 - iterate skipped DbEntities to populate exported keys - for (DbEntity skippedEntity : skippedEntities) { - loadDbRelationships(skippedEntity, map); + private void loadDbAttributes(FiltersConfig filters, DbEntity dbEntity) throws SQLException { + ResultSet rs = getMetaData().getColumns(dbEntity.getCatalog(), dbEntity.getSchema(), dbEntity.getName(), "%"); + + try { + while (rs.next()) { + // for a reason not quiet apparent to me, Oracle sometimes + // returns duplicate record sets for the same table, messing up table + // names. E.g. for the system table "WK$_ATTR_MAPPING" columns are + // returned twice - as "WK$_ATTR_MAPPING" and "WK$$_ATTR_MAPPING"... Go figure + + String tableName = rs.getString("TABLE_NAME"); + if (!dbEntity.getName().equals(tableName)) { + LOGGER.info("Incorrectly returned columns for '" + tableName + ", skipping."); + continue; + } + + DbAttribute attr = loadDbAttribute(rs); + attr.setEntity(dbEntity); + DbPath path = new DbPath(dbEntity.getCatalog(), dbEntity.getSchema(), tableName); + Filter<DbAttribute> filter = filters.filter(path).columnFilter(); + if (!filter.isInclude(attr)) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Importing: attribute '" + attr.getEntity().getName() + "." + attr.getName() + + "' is skipped (Path: " + path + "; Filter: " + filter + ")"); + } + continue; + } + + dbEntity.addAttribute(attr); + } + } finally { + rs.close(); + } + } + + private DbAttribute loadDbAttribute(ResultSet rs) throws SQLException { + // gets attribute's (column's) information + int columnType = rs.getInt("DATA_TYPE"); + + // ignore precision of non-decimal columns + int decimalDigits = -1; + if (TypesMapping.isDecimal(columnType)) { + decimalDigits = rs.getInt("DECIMAL_DIGITS"); + if (rs.wasNull()) { + decimalDigits = -1; + } } - return true; + // create attribute delegating this task to adapter + DbAttribute attr = adapter.buildAttribute( + rs.getString("COLUMN_NAME"), + rs.getString("TYPE_NAME"), + columnType, + rs.getInt("COLUMN_SIZE"), + decimalDigits, + rs.getBoolean("NULLABLE")); + + if (adapter.supportsGeneratedKeys()) { + // TODO: this actually throws on some drivers... need to + // ensure that 'supportsGeneratedKeys' check is enough + // to prevent an exception here. + String autoIncrement = rs.getString("IS_AUTOINCREMENT"); + if ("YES".equals(autoIncrement)) { + attr.setGenerated(true); + } + } + return attr; } /** * Creates an ObjEntity for each DbEntity in the map. ObjEntities are * created empty without */ - public void loadObjEntities(DataMap map) { - - Iterator<DbEntity> dbEntities = dbEntityList.iterator(); - if (!dbEntities.hasNext()) { + protected void loadObjEntities(DataMap map, DbLoaderConfiguration config, Collection<DbEntity> entities) { + if (entities.isEmpty()) { return; } - List<ObjEntity> loadedEntities = new ArrayList<ObjEntity>(dbEntityList.size()); + Collection<ObjEntity> loadedEntities = new ArrayList<ObjEntity>(entities.size()); - String packageName = map.getDefaultPackage(); - if (Util.isEmptyString(packageName)) { - packageName = ""; - } else if (!packageName.endsWith(".")) { - packageName = packageName + "."; - } - - // load empty ObjEntities for all the tables - while (dbEntities.hasNext()) { - DbEntity dbEntity = dbEntities.next(); + // doLoad empty ObjEntities for all the tables + for (DbEntity dbEntity : entities) { // check if there are existing entities Collection<ObjEntity> existing = map.getMappedEntities(dbEntity); - if (existing.size() > 0) { + if (!existing.isEmpty()) { loadedEntities.addAll(existing); continue; } - String objEntityName = nameGenerator.createObjEntityName(dbEntity); - // this loop will terminate even if no valid name is found - // to prevent loader from looping forever (though such case is very - // unlikely) - String baseName = objEntityName; - for (int i = 1; i < 1000 && map.getObjEntity(objEntityName) != null; i++) { - objEntityName = baseName + i; - } + String objEntityName = DefaultUniqueNameGenerator.generate(NameCheckers.objEntity, map, + nameGenerator.createObjEntityName(dbEntity)); ObjEntity objEntity = new ObjEntity(objEntityName); objEntity.setDbEntity(dbEntity); + objEntity.setClassName(config.getGenericClassName() != null ? config.getGenericClassName() + : map.getNameWithDefaultPackage(objEntity.getName())); - objEntity.setClassName(getGenericClassName() != null ? getGenericClassName() : packageName - + objEntity.getName()); map.addObjEntity(objEntity); loadedEntities.add(objEntity); } // update ObjEntity attributes and relationships - EntityMergeSupport objEntityMerger = createEntityMerger(map); - objEntityMerger.synchronizeWithDbEntities(loadedEntities); + createEntityMerger(map).synchronizeWithDbEntities(loadedEntities); } /** @@ -545,84 +496,69 @@ public class DbLoader { } /** Loads database relationships into a DataMap. */ - public void loadDbRelationships(DataMap map) throws SQLException { - for (DbEntity pkEntity : dbEntityList) { - loadDbRelationships(pkEntity, map); + protected void loadDbRelationships(DataMap map, DbLoaderConfiguration config, Iterable<DbEntity> entities) throws SQLException { + for (DbEntity pkEntity : entities) { + loadDbRelationships(map, pkEntity, config); } } - private void loadDbRelationships(DbEntity pkEntity, DataMap map) throws SQLException { - DatabaseMetaData md = getMetaData(); - String pkEntName = pkEntity.getName(); - + protected void loadDbRelationships(DataMap map, DbEntity entity, DbLoaderConfiguration config) throws SQLException { // Get all the foreign keys referencing this table - ResultSet rs = null; + ResultSet rs; try { - rs = md.getExportedKeys(pkEntity.getCatalog(), pkEntity.getSchema(), pkEntity.getName()); + rs = getMetaData().getExportedKeys(entity.getCatalog(), entity.getSchema(), entity.getName()); } catch (SQLException cay182Ex) { // Sybase-specific - the line above blows on VIEWS, see CAY-182. - logger.info("Error getting relationships for '" + pkEntName + "', ignoring."); + LOGGER.info("Error getting relationships for '" + entity.getName() + "', ignoring."); return; } try { - if (!rs.next()) { - return; - } - - // these will be initailzed every time a new target entity - // is found in the result set (which should be ordered by table name - // among - // other things) + // these will be initialized every time a new target entity is found + // in the result set (which should be ordered by table name among other things) DbRelationship forwardRelationship = null; DbRelationshipDetected reverseRelationship = null; DbEntity fkEntity = null; ExportedKey key = null; - do { - // extract data from resultset + while (rs.next()) { key = ExportedKey.extractData(rs); short keySeq = rs.getShort("KEY_SEQ"); if (keySeq == 1) { if (forwardRelationship != null) { - postprocessMasterDbRelationship(forwardRelationship, key); + postProcessMasterDbRelationship(forwardRelationship, key); forwardRelationship = null; } // start new entity String fkEntityName = key.getFKTableName(); - String fkName = key.getFKName(); - if (!includeTableName(fkEntityName)) { + fkEntity = map.getDbEntity(fkEntityName); + DbPath path = new DbPath(entity.getCatalog(), entity.getSchema(), entity.getName()); + if (!config.getFiltersConfig().filter(path).tableFilter().isInclude(fkEntity)) { continue; } - fkEntity = map.getDbEntity(fkEntityName); - if (fkEntity == null) { - logger.info("FK warning: no entity found for name '" + fkEntityName + "'"); - } else if (skippedEntities.contains(pkEntity) && skippedEntities.contains(fkEntity)) { - // cay-479 - don't load relationships between two - // skipped entities. + LOGGER.info("FK warning: no entity found for name '" + fkEntityName + "'"); + } else if (skippedEntities.contains(entity) && skippedEntities.contains(fkEntity)) { + // cay-479 - don't doLoad relationships between two skipped entities. continue; } else { // init relationship - String forwardPreferredName = nameGenerator.createDbRelationshipName(key, true); - forwardRelationship = new DbRelationship(uniqueRelName(pkEntity, forwardPreferredName)); - - forwardRelationship.setSourceEntity(pkEntity); + forwardRelationship = new DbRelationship(generateName(entity, key, true)); + forwardRelationship.setSourceEntity(entity); forwardRelationship.setTargetEntity(fkEntity); - pkEntity.addRelationship(forwardRelationship); + entity.addRelationship(forwardRelationship); - String reversePreferredName = nameGenerator.createDbRelationshipName(key, false); - reverseRelationship = new DbRelationshipDetected(uniqueRelName(fkEntity, reversePreferredName)); - reverseRelationship.setFkName(fkName); + reverseRelationship = new DbRelationshipDetected(generateName(fkEntity, key, false)); + reverseRelationship.setFkName(key.getFKName()); reverseRelationship.setToMany(false); reverseRelationship.setSourceEntity(fkEntity); - reverseRelationship.setTargetEntity(pkEntity); + reverseRelationship.setTargetEntity(entity); fkEntity.addRelationship(reverseRelationship); } } @@ -633,15 +569,15 @@ public class DbLoader { String fkName = key.getFKColumnName(); // skip invalid joins... - DbAttribute pkAtt = pkEntity.getAttribute(pkName); + DbAttribute pkAtt = entity.getAttribute(pkName); if (pkAtt == null) { - logger.info("no attribute for declared primary key: " + pkName); + LOGGER.info("no attribute for declared primary key: " + pkName); continue; } DbAttribute fkAtt = fkEntity.getAttribute(fkName); if (fkAtt == null) { - logger.info("no attribute for declared foreign key: " + fkName); + LOGGER.info("no attribute for declared foreign key: " + fkName); continue; } @@ -653,10 +589,10 @@ public class DbLoader { } } - } while (rs.next()); + } if (forwardRelationship != null) { - postprocessMasterDbRelationship(forwardRelationship, key); + postProcessMasterDbRelationship(forwardRelationship, key); forwardRelationship = null; } @@ -665,11 +601,17 @@ public class DbLoader { } } + private String generateName(DbEntity entity, ExportedKey key, boolean toMany) { + String forwardPreferredName = nameGenerator.createDbRelationshipName(key, toMany); + return DefaultUniqueNameGenerator + .generate(NameCheckers.dbRelationship, entity, forwardPreferredName); + } + /** * Detects correct relationship multiplicity and "to dep pk" flag. Only * called on relationships from PK to FK, not the reverse ones. */ - protected void postprocessMasterDbRelationship(DbRelationship relationship, ExportedKey key) { + protected void postProcessMasterDbRelationship(DbRelationship relationship, ExportedKey key) { boolean toPK = true; List<DbJoin> joins = relationship.getJoins(); @@ -681,33 +623,25 @@ public class DbLoader { } - boolean toDependentPK = false; - boolean toMany = true; - - if (toPK) { - toDependentPK = true; - if (relationship.getTargetEntity().getPrimaryKeys().size() == joins.size()) { - toMany = false; - } - } + relationship.setToDependentPK(toPK); + relationship.setToMany(!(toPK && relationship.getTargetEntity().getPrimaryKeys().size() == joins.size())); // if this is really to-one we need to rename the relationship - if (!toMany) { + if (!relationship.isToMany()) { Entity source = relationship.getSourceEntity(); source.removeRelationship(relationship.getName()); - relationship.setName(DbLoader.uniqueRelName(source, nameGenerator.createDbRelationshipName(key, false))); + relationship.setName(DefaultUniqueNameGenerator.generate(NameCheckers.dbRelationship, source, + nameGenerator.createDbRelationshipName(key, false))); + source.addRelationship(relationship); } - - relationship.setToDependentPK(toDependentPK); - relationship.setToMany(toMany); } /** * Flattens many-to-many relationships in the generated model. */ private void flattenManyToManyRelationships(DataMap map) { - List<ObjEntity> entitiesForDelete = new ArrayList<ObjEntity>(); + Collection<ObjEntity> entitiesForDelete = new LinkedList<ObjEntity>(); for (ObjEntity curEntity : map.getObjEntities()) { ManyToManyCandidateEntity entity = ManyToManyCandidateEntity.build(curEntity); @@ -734,24 +668,25 @@ public class DbLoader { } /** + * By default we want to load Tables and Views for mo types + * + * @see DbLoader#getTableTypes() * @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); } + + String tableType = adapter.tableTypeForTable(); if (tableType != null) { list.add(tableType); } - String[] types = new String[list.size()]; - list.toArray(types); - return types; + return list.toArray(new String[list.size()]); } /** @@ -761,7 +696,7 @@ public class DbLoader { * * @since 1.0.7 * @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 @@ -772,7 +707,10 @@ public class DbLoader { throw new SQLException("No supported table types found."); } - load(dataMap, null, schemaPattern, tablePattern, types); + DbLoaderConfiguration configuration = new DbLoaderConfiguration(); + configuration.setFiltersConfig(new FiltersConfig(new EntityFilters( + new DbPath(null, schemaPattern), include(tablePattern), TRUE, NULL))); + load(dataMap, configuration, types); return dataMap; } @@ -782,7 +720,7 @@ public class DbLoader { * of tables to read. * * @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 @@ -790,10 +728,24 @@ public class DbLoader { throws SQLException { dataMap.clear(); - load(dataMap, null, schemaPattern, tablePattern, tableTypes); + DbLoaderConfiguration config = new DbLoaderConfiguration(); + config.setFiltersConfig(new FiltersConfig(new EntityFilters( + new DbPath(null, schemaPattern), transformPatternToFilter(tablePattern), TRUE, NULL))); + + load(dataMap, config, tableTypes); return dataMap; } + private Filter<String> transformPatternToFilter(String tablePattern) { + Filter<String> table; + if (tablePattern == null) { + table = NULL; + } else { + table = include(tablePattern.replaceAll("%", ".*")); + } + return table; + } + /** * Performs database reverse engineering to match the specified catalog, * schema, table name and table type patterns and fills the specified @@ -801,25 +753,38 @@ public class DbLoader { * * @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); } } /** + * Performs database reverse engineering to match the specified catalog, + * schema, table name and table type patterns and fills the specified + * DataMap object with DB and object mapping info. + * + * @since 3.2 + */ + public DataMap load(DbLoaderConfiguration config) throws SQLException { + + DataMap dataMap = new DataMap(); + String[] tableTypes = config.getTableTypes() == null ? this.getDefaultTableTypes() : config.getTableTypes(); + + load(dataMap, config, tableTypes); + loadProcedures(dataMap, config); + + return dataMap; + } + + /** * Loads database stored procedures into the DataMap. * <p> * <i>As of 1.1 there is no boolean property or delegate method to make @@ -833,8 +798,13 @@ public class DbLoader { * {@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); } /** @@ -848,103 +818,56 @@ public class DbLoader { * * @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()) { String schema = columnsRS.getString("PROCEDURE_SCHEM"); String name = columnsRS.getString("PROCEDURE_NAME"); - - // TODO: this will be moved to Delegate... - if (EXCLUDED_PROCEDURES.contains(name)) { + String key = (schema == null ? "" : schema + '.') + name ; + Procedure procedure = procedures.get(key); + if (procedure == null) { continue; } String columnName = columnsRS.getString("COLUMN_NAME"); - short type = columnsRS.getShort("COLUMN_TYPE"); - - String key = (schema != null) ? schema + '.' + name : name; - // skip ResultSet columns, as they are not described in Cayenne - // procedures - // yet... + // skip ResultSet columns, as they are not described in Cayenne procedures yet... + short type = columnsRS.getShort("COLUMN_TYPE"); if (type == DatabaseMetaData.procedureColumnResult) { - logger.debug("skipping ResultSet column: " + key + "." + columnName); + LOGGER.debug("skipping ResultSet column: " + key + "." + columnName); } - Procedure procedure = procedures.get(key); - - if (procedure == null) { - logger.info("invalid procedure column, no procedure found: " + key + "." + columnName); - continue; - } - - ProcedureParameter column = new ProcedureParameter(columnName); - if (columnName == null) { if (type == DatabaseMetaData.procedureColumnReturn) { - logger.debug("null column name, assuming result column: " + key); - column.setName("_return_value"); + LOGGER.debug("null column name, assuming result column: " + key); + columnName = "_return_value"; + procedure.setReturningValue(true); } else { - logger.info("invalid null column name, skipping column : " + key); + LOGGER.info("invalid null column name, skipping column : " + key); continue; } } int columnType = columnsRS.getInt("DATA_TYPE"); - int columnSize = columnsRS.getInt("LENGTH"); // ignore precision of non-decimal columns int decimalDigits = -1; @@ -955,35 +878,86 @@ public class DbLoader { } } - switch (type) { - case DatabaseMetaData.procedureColumnIn: - column.setDirection(ProcedureParameter.IN_PARAMETER); - break; - case DatabaseMetaData.procedureColumnInOut: - column.setDirection(ProcedureParameter.IN_OUT_PARAMETER); - break; - case DatabaseMetaData.procedureColumnOut: - column.setDirection(ProcedureParameter.OUT_PARAMETER); - break; - case DatabaseMetaData.procedureColumnReturn: - procedure.setReturningValue(true); - break; + ProcedureParameter column = new ProcedureParameter(columnName); + int direction = getDirection(type); + if (direction != -1) { + column.setDirection(direction); } - column.setMaxLength(columnSize); + column.setType(columnType); + column.setMaxLength(columnsRS.getInt("LENGTH")); column.setPrecision(decimalDigits); + column.setProcedure(procedure); - column.setType(columnType); procedure.addCallParameter(column); } } finally { columnsRS.close(); } + } - for (final Procedure procedure : procedures.values()) { - // overwrite existing procedures... - dataMap.addProcedure(procedure); + private static int getDirection(short type) { + switch (type) { + case DatabaseMetaData.procedureColumnIn: + return ProcedureParameter.IN_PARAMETER; + case DatabaseMetaData.procedureColumnInOut: + return ProcedureParameter.IN_OUT_PARAMETER; + case DatabaseMetaData.procedureColumnOut: + return ProcedureParameter.OUT_PARAMETER; + default: + return -1; + } + } + + private Map<String, Procedure> loadProcedures(DbLoaderConfiguration config) throws SQLException { + Map<String, Procedure> procedures = new HashMap<String, Procedure>(); + + FiltersConfig filters = config.getFiltersConfig(); + for (DbPath dbPath : filters.pathsForQueries()) { + if (filters.filter(dbPath).procedureFilter().equals(NULL)) { + continue; + } + + procedures.putAll(loadProcedures(filters, dbPath)); + } + + return procedures; + } + + private Map<String, Procedure> loadProcedures(FiltersConfig filters, DbPath dbPath) throws SQLException { + Map<String, Procedure> procedures = new HashMap<String, Procedure>(); + // get procedures + ResultSet rs = getMetaData().getProcedures(dbPath.catalog, dbPath.schema, WILDCARD); + try { + while (rs.next()) { + + String name = rs.getString("PROCEDURE_NAME"); + Procedure procedure = new Procedure(name); + procedure.setCatalog(rs.getString("PROCEDURE_CAT")); + procedure.setSchema(rs.getString("PROCEDURE_SCHEM")); + + if (filters.filter(new DbPath(procedure.getCatalog(), procedure.getSchema())) + .procedureFilter().isInclude(procedure)) { + LOGGER.info("skipping Cayenne PK procedure: " + name); + continue; + } + + switch (rs.getShort("PROCEDURE_TYPE")) { + case DatabaseMetaData.procedureNoResult: + case DatabaseMetaData.procedureResultUnknown: + procedure.setReturningValue(false); + break; + case DatabaseMetaData.procedureReturnsResult: + procedure.setReturningValue(true); + break; + } + + procedures.put(procedure.getFullyQualifiedName(), procedure); + } + } finally { + rs.close(); } + return procedures; } /** @@ -992,7 +966,6 @@ public class DbLoader { * @since 3.0 */ public void setNameGenerator(ObjectNameGenerator strategy) { - // null values are not allowed if (strategy == null) { throw new NullPointerException("Null strategy not allowed"); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoaderConfiguration.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoaderConfiguration.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoaderConfiguration.java deleted file mode 100644 index 02dab76..0000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbLoaderConfiguration.java +++ /dev/null @@ -1,48 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ -package org.apache.cayenne.access; - -/** - * @since 4.0. - */ -public class DbLoaderConfiguration { - - private String catalog; - - private String schema; - - - public String getCatalog() { - return catalog; - } - - public void setCatalog(String catalog) { - this.catalog = catalog; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/DefaultDbLoaderDelegate.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DefaultDbLoaderDelegate.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DefaultDbLoaderDelegate.java deleted file mode 100644 index 807635a..0000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DefaultDbLoaderDelegate.java +++ /dev/null @@ -1,54 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ -package org.apache.cayenne.access; - -import org.apache.cayenne.CayenneException; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.ObjEntity; - -/** - * @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/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java deleted file mode 100644 index 4f68344..0000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/ManyToManyCandidateEntity.java +++ /dev/null @@ -1,126 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ -package org.apache.cayenne.access; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; -import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator; -import org.apache.cayenne.map.naming.ExportedKey; -import org.apache.cayenne.map.naming.NameCheckers; -import org.apache.cayenne.map.naming.ObjectNameGenerator; - -/** - * Class represent ObjEntity that may be optimized using flattened relationships - * as many to many table - */ -class ManyToManyCandidateEntity { - private final ObjEntity joinEntity; - - private final DbRelationship dbRel1; - private final DbRelationship dbRel2; - - private final ObjEntity entity1; - private final ObjEntity entity2; - - private final DbRelationship reverseRelationship1; - private final DbRelationship reverseRelationship2; - - private ManyToManyCandidateEntity(ObjEntity entityValue, List<ObjRelationship> relationships) { - joinEntity = entityValue; - - ObjRelationship rel1 = relationships.get(0); - ObjRelationship rel2 = relationships.get(1); - - dbRel1 = rel1.getDbRelationships().get(0); - dbRel2 = rel2.getDbRelationships().get(0); - - reverseRelationship1 = dbRel1.getReverseRelationship(); - reverseRelationship2 = dbRel2.getReverseRelationship(); - - entity1 = rel1.getTargetEntity(); - entity2 = rel2.getTargetEntity(); - } - - /** - * Method check - if current entity represent many to many temporary table - * @return true if current entity is represent many to many table; otherwise returns false - */ - public static ManyToManyCandidateEntity build(ObjEntity joinEntity) { - ArrayList<ObjRelationship> relationships = new ArrayList<ObjRelationship>(joinEntity.getRelationships()); - if (relationships.size() != 2) { - return null; - } - - ManyToManyCandidateEntity candidateEntity = new ManyToManyCandidateEntity(joinEntity, relationships); - if (candidateEntity.isManyToMany()) { - return candidateEntity; - } - - return null; - } - - private boolean isManyToMany() { - boolean isNotHaveAttributes = joinEntity.getAttributes().size() == 0; - - return isNotHaveAttributes && reverseRelationship1.isToDependentPK() && reverseRelationship2.isToDependentPK() - && !entity1.equals(entity2); - } - - private void addFlattenedRelationship(ObjectNameGenerator nameGenerator, ObjEntity srcEntity, ObjEntity dstEntity, - DbRelationship rel1, DbRelationship rel2) { - - ExportedKey key = new ExportedKey(rel1.getSourceEntity().getName(), - rel1.getSourceAttributes().iterator().next().getName(), - null, - rel2.getTargetEntity().getName(), - rel2.getTargetAttributes().iterator().next().getName(), - null); - - ObjRelationship newRelationship = new ObjRelationship(); - newRelationship.setName(DefaultUniqueNameGenerator.generate(NameCheckers.objRelationship, srcEntity, - nameGenerator.createDbRelationshipName(key, true))); - - newRelationship.setSourceEntity(srcEntity); - newRelationship.setTargetEntity(dstEntity); - - newRelationship.addDbRelationship(rel1); - newRelationship.addDbRelationship(rel2); - - srcEntity.addRelationship(newRelationship); - } - - /** - * Method make direct relationships between 2 entities and remove relationships to - * many to many entity - * - * @param nameGenerator - */ - public void optimizeRelationships(ObjectNameGenerator nameGenerator) { - entity1.removeRelationship(reverseRelationship1.getName()); - entity2.removeRelationship(reverseRelationship2.getName()); - - addFlattenedRelationship(nameGenerator, entity1, entity2, reverseRelationship1, dbRel2); - addFlattenedRelationship(nameGenerator, entity2, entity1, reverseRelationship2, dbRel1); - } - -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java new file mode 100644 index 0000000..c816af8 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/BooleanNameFilter.java @@ -0,0 +1,35 @@ +/***************************************************************** + * 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; + +/** + * @since 3.2. + */ +public class BooleanNameFilter implements NameFilter { + private final boolean isInclude; + + public BooleanNameFilter(boolean isInclude) { + this.isInclude = isInclude; + } + + @Override + public boolean isIncluded(String string) { + return this.isInclude; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.java new file mode 100644 index 0000000..461c687 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DbLoaderConfiguration.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.access.loader; + +import org.apache.cayenne.access.loader.filters.DbPath; +import org.apache.cayenne.access.loader.filters.EntityFilters; +import org.apache.cayenne.access.loader.filters.FilterFactory; +import org.apache.cayenne.access.loader.filters.FiltersConfig; + +/** + * @since 3.2. + */ +public class DbLoaderConfiguration { + + /** + * Returns a name of a generic class that should be used for all + * ObjEntities. The most common generic class is + * {@link org.apache.cayenne.CayenneDataObject}. If generic class name is + * null (which is the default), DbLoader will assign each entity a unique + * class name derived from the table name. + * + */ + private String genericClassName; + +/* + // TODO: Andrus, 10/29/2005 - this type of filtering should be delegated to adapter + */ +/* TODO by default should skip name.startsWith("BIN$") *//* + + private NameFilter tableFilter = NamePatternMatcher.build(null, null, "BIN$"); + + private NameFilter columnFilter; + + private NameFilter proceduresFilter = new NameFilter() { + private final Collection<String> excludedProcedures = Arrays.asList( + "auto_pk_for_table", + "auto_pk_for_table;1" // the last name is some Mac OS X Sybase artifact + ); + + @Override + public boolean isIncluded(String string) { + return !excludedProcedures.contains(string); + } + }; +*/ + + + /** + * Java class implementing org.apache.cayenne.map.naming.NamingStrategy. + * This is used to specify how ObjEntities will be mapped from the imported + * DB schema. + */ + private String namingStrategy; + + private String[] tableTypes; + + private FiltersConfig filtersConfig; + + public String getGenericClassName() { + return genericClassName; + } + + public void setGenericClassName(String genericClassName) { + this.genericClassName = genericClassName; + } + + public String[] getTableTypes() { + return tableTypes; + } + + public void setTableTypes(String[] tableTypes) { + this.tableTypes = tableTypes; + } + + public String getNamingStrategy() { + return namingStrategy; + } + + public void setNamingStrategy(String namingStrategy) { + this.namingStrategy = namingStrategy; + } + + public FiltersConfig getFiltersConfig() { + if (filtersConfig == null) { + // this case is used often in tests where config not initialized properly + return new FiltersConfig(new EntityFilters(new DbPath(), FilterFactory.TRUE, FilterFactory.TRUE, FilterFactory.TRUE)); + } + return filtersConfig; + } + + public void setFiltersConfig(FiltersConfig filtersConfig) { + this.filtersConfig = filtersConfig; + } + + @Override + public String toString() { + return "EntitiesFilters: " + getFiltersConfig(); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DefaultDbLoaderDelegate.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..bc91162 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/DefaultDbLoaderDelegate.java @@ -0,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 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/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntity.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntity.java new file mode 100644 index 0000000..6e392d8 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/ManyToManyCandidateEntity.java @@ -0,0 +1,141 @@ +/***************************************************************** + * 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 java.util.ArrayList; +import java.util.List; + +import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.ObjEntity; +import org.apache.cayenne.map.ObjRelationship; +import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator; +import org.apache.cayenne.map.naming.ExportedKey; +import org.apache.cayenne.map.naming.NameCheckers; +import org.apache.cayenne.map.naming.ObjectNameGenerator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Class represent ObjEntity that may be optimized using flattened relationships + * as many to many table + */ +public class ManyToManyCandidateEntity { + + private static final Log LOG = LogFactory.getLog(ManyToManyCandidateEntity.class); + + private final ObjEntity joinEntity; + + private final DbRelationship dbRel1; + private final DbRelationship dbRel2; + + private final ObjEntity entity1; + private final ObjEntity entity2; + + private final DbRelationship reverseRelationship1; + private final DbRelationship reverseRelationship2; + + private ManyToManyCandidateEntity(ObjEntity entityValue, List<ObjRelationship> relationships) { + joinEntity = entityValue; + + ObjRelationship rel1 = relationships.get(0); + ObjRelationship rel2 = relationships.get(1); + + dbRel1 = rel1.getDbRelationships().get(0); + dbRel2 = rel2.getDbRelationships().get(0); + + reverseRelationship1 = dbRel1.getReverseRelationship(); + reverseRelationship2 = dbRel2.getReverseRelationship(); + + entity1 = rel1.getTargetEntity(); + entity2 = rel2.getTargetEntity(); + } + + /** + * Method check - if current entity represent many to many temporary table + * @return true if current entity is represent many to many table; otherwise returns false + */ + public static ManyToManyCandidateEntity build(ObjEntity joinEntity) { + ArrayList<ObjRelationship> relationships = new ArrayList<ObjRelationship>(joinEntity.getRelationships()); + if (relationships.size() != 2) { + return null; + } + + ManyToManyCandidateEntity candidateEntity = new ManyToManyCandidateEntity(joinEntity, relationships); + if (candidateEntity.isManyToMany()) { + return candidateEntity; + } + + return null; + } + + private boolean isManyToMany() { + boolean isNotHaveAttributes = joinEntity.getAttributes().size() == 0; + + return isNotHaveAttributes + && reverseRelationship1.isToDependentPK() + && reverseRelationship2.isToDependentPK() + && !entity1.equals(entity2); + } + + private void addFlattenedRelationship(ObjectNameGenerator nameGenerator, ObjEntity srcEntity, ObjEntity dstEntity, + DbRelationship rel1, DbRelationship rel2) { + + if (rel1.getSourceAttributes().isEmpty() && rel2.getTargetAttributes().isEmpty()) { + LOG.warn("Wrong call ManyToManyCandidateEntity.addFlattenedRelationship(... , " + srcEntity.getName() + + ", " + dstEntity.getName() + ", ...)"); + + return; + } + + ExportedKey key = new ExportedKey( + rel1.getSourceEntity().getName(), + rel1.getSourceAttributes().iterator().next().getName(), + null, + rel2.getTargetEntity().getName(), + rel2.getTargetAttributes().iterator().next().getName(), + null); + + ObjRelationship newRelationship = new ObjRelationship(); + newRelationship.setName(DefaultUniqueNameGenerator.generate(NameCheckers.objRelationship, srcEntity, + nameGenerator.createDbRelationshipName(key, true))); + + newRelationship.setSourceEntity(srcEntity); + newRelationship.setTargetEntity(dstEntity); + + newRelationship.addDbRelationship(rel1); + newRelationship.addDbRelationship(rel2); + + srcEntity.addRelationship(newRelationship); + } + + /** + * Method make direct relationships between 2 entities and remove relationships to + * many to many entity + * + * @param nameGenerator + */ + public void optimizeRelationships(ObjectNameGenerator nameGenerator) { + entity1.removeRelationship(reverseRelationship1.getName()); + entity2.removeRelationship(reverseRelationship2.getName()); + + addFlattenedRelationship(nameGenerator, entity1, entity2, reverseRelationship1, dbRel2); + addFlattenedRelationship(nameGenerator, entity2, entity1, reverseRelationship2, dbRel1); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e2474202/cayenne-server/src/main/java/org/apache/cayenne/access/loader/NameFilter.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/loader/NameFilter.java b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/NameFilter.java new file mode 100644 index 0000000..6e28b33 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/loader/NameFilter.java @@ -0,0 +1,27 @@ +/***************************************************************** + * 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; + +/** + * @since 3.2. + */ +public interface NameFilter { + + boolean isIncluded(String string); +}