From: <hib...@li...> - 2006-08-04 08:42:04
|
Author: max...@jb... Date: 2006-08-04 04:41:47 -0400 (Fri, 04 Aug 2006) New Revision: 10211 Added: trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/ManyToManyTest.java Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/JDBCBinder.java trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DelegatingReverseEngineeringStrategy.java trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringStrategy.java trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/AbstractExporter.java trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/POJOClass.java trunk/HibernateExt/tools/src/templates/pojo/PojoConstructors.ftl trunk/HibernateExt/tools/src/test/org/hibernate/tool/BaseTestCase.java trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/SeamAntTest.java trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Constructors.hbm.xml trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Hbm2JavaConstructorTest.java trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/ManyToManyTest.java trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OverrideBinderTest.java trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/foreignkeytest.reveng.xml trunk/HibernateExt/tools/src/testsupport/NoopReverseEngineeringStrategy.java Log: HBX-132 support many-to-many (core impl, not exposed in ui/reveng.xml/ant) HBX-584 single field constructor problem + reveng primarykey handling Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/JDBCBinder.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/JDBCBinder.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/JDBCBinder.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -4,6 +4,7 @@ */ package org.hibernate.cfg; +import java.beans.Introspector; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; @@ -137,6 +138,12 @@ continue; // TODO: just create one big embedded composite id instead. }*/ + + if(revengStrategy.isManyToManyTable(table)) { + log.debug( "Ignoring " + table + " as class since rev.eng. says it is a many-to-many" ); + continue; + } + RootClass rc = new RootClass(); TableIdentifier tableIdentifier = TableIdentifier.create(table); String className = revengStrategy.tableToClassName( tableIdentifier ); @@ -169,11 +176,12 @@ } + // bind collections. private void bindIncomingForeignKeys(PersistentClass rc, Set processed, List foreignKeys, Mapping mapping) { if(foreignKeys!=null) { for (Iterator iter = foreignKeys.iterator(); iter.hasNext();) { ForeignKey foreignKey = (ForeignKey) iter.next(); - + if(revengStrategy.excludeForeignKeyAsCollection( foreignKey.getName(), TableIdentifier.create(foreignKey.getTable() ), @@ -182,6 +190,7 @@ foreignKey.getReferencedColumns())) { log.debug("Rev.eng excluded one-to-many for foreignkey " + foreignKey.getName()); } else { + Property property = bindOneToMany(rc, foreignKey, processed, mapping); rc.addProperty(property); } @@ -222,46 +231,54 @@ Table collectionTable = foreignKey.getTable(); Collection collection = new org.hibernate.mapping.Set(rc); // MASTER TODO: allow overriding collection type - + collection.setCollectionTable(collectionTable); // CHILD+ - boolean b = isUniqueReference(foreignKey); - TableIdentifier foreignKeyTable = TableIdentifier.create( foreignKey.getTable() ); - TableIdentifier foreignKeyReferencedTable = TableIdentifier.create( foreignKey.getReferencedTable() ); - String collectionRole = revengStrategy.foreignKeyToCollectionName( - foreignKey.getName(), - foreignKeyTable, - foreignKey.getColumns(), - foreignKeyReferencedTable, - foreignKey.getReferencedColumns(), - b - ); - collectionRole = makeUnique(rc,collectionRole); + + boolean manyToMany = revengStrategy.isManyToManyTable( collectionTable ); + if(manyToMany) { + log.debug("Rev.eng said here is a many-to-many"); + // TODO: handle "the other side should influence the name" + } - String fullRolePath = StringHelper.qualify(rc.getEntityName(), collectionRole); - if (mappings.getCollection(fullRolePath)!=null) { - log.debug(fullRolePath + " found twice!"); + + + if(manyToMany) { + + ManyToOne element = new ManyToOne( collection.getCollectionTable() ); + //TODO: find the other foreignkey and choose the other side. + Iterator foreignKeyIterator = foreignKey.getTable().getForeignKeyIterator(); + List keys = new ArrayList(); + while ( foreignKeyIterator.hasNext() ) { + Object next = foreignKeyIterator.next(); + if(next!=foreignKey) { + keys.add(next); + } + } + + if(keys.size()>1) { + throw new JDBCBinderException("more than one other foreign key to choose from!"); // todo: hande better ? + } + + ForeignKey fk = (ForeignKey) keys.get( 0 ); + + String tableToClassName = bindCollection( rc, foreignKey, fk, collection ); + + element.setReferencedEntityName( tableToClassName ); + element.addColumn( fk.getColumn( 0 ) ); + collection.setElement( element ); + + } else { + String tableToClassName = bindCollection( rc, foreignKey, null, collection ); + + OneToMany oneToMany = new OneToMany( collection.getOwner() ); + + oneToMany.setReferencedEntityName( tableToClassName ); // Child + mappings.addSecondPass( new JDBCCollectionSecondPass(mappings, collection) ); + + collection.setElement(oneToMany); } - collection.setRole(fullRolePath); // Master.setOfChildren+ - collection.setInverse(revengStrategy.isForeignKeyCollectionInverse(foreignKey.getName(), - foreignKeyTable, - foreignKey.getColumns(), - foreignKeyReferencedTable, - foreignKey.getReferencedColumns())); // TODO: allow overriding this - collection.setLazy(revengStrategy.isForeignKeyCollectionLazy(foreignKey.getName(), - foreignKeyTable, - foreignKey.getColumns(), - foreignKeyReferencedTable, - foreignKey.getReferencedColumns())); - collection.setFetchMode(FetchMode.SELECT); - - OneToMany oneToMany = new OneToMany( collection.getOwner() ); - oneToMany.setReferencedEntityName( revengStrategy.tableToClassName( foreignKeyTable ) ); // Child - - mappings.addSecondPass( new JDBCCollectionSecondPass(mappings, collection) ); - collection.setElement(oneToMany); - // bind keyvalue KeyValue referencedKeyValue; String propRef = collection.getReferencedPropertyName(); @@ -289,10 +306,74 @@ mappings.addCollection(collection); - return makeProperty(collectionRole, collection, true, true, true, "all", null); + return makeProperty(StringHelper.unqualify( collection.getRole() ), collection, true, true, true, "all", null); // TODO: cascade isn't all by default } + private String bindCollection(PersistentClass rc, ForeignKey fromForeignKey, ForeignKey toForeignKey, Collection collection) { + ForeignKey targetKey = fromForeignKey; + String collectionRole = null; + boolean collectionLazy = false; + boolean collectionInverse = false; + TableIdentifier foreignKeyTable = null; + String tableToClassName; + + if(toForeignKey!=null) { + targetKey = toForeignKey; + } + + boolean uniqueReference = isUniqueReference(targetKey); + foreignKeyTable = TableIdentifier.create( targetKey.getTable() ); + TableIdentifier foreignKeyReferencedTable = TableIdentifier.create( targetKey.getReferencedTable() ); + + if(toForeignKey==null) { + + collectionRole = revengStrategy.foreignKeyToCollectionName( + fromForeignKey.getName(), + foreignKeyTable, + fromForeignKey.getColumns(), + foreignKeyReferencedTable, + fromForeignKey.getReferencedColumns(), + uniqueReference + ); + + tableToClassName = revengStrategy.tableToClassName( foreignKeyTable ); + } else { + + collectionRole = revengStrategy.foreignKeyToManyToManyName( + fromForeignKey, TableIdentifier.create( fromForeignKey.getTable()), toForeignKey, uniqueReference ); + + tableToClassName = revengStrategy.tableToClassName( foreignKeyReferencedTable ); + } + + collectionInverse = revengStrategy.isForeignKeyCollectionInverse(targetKey.getName(), + foreignKeyTable, + targetKey.getColumns(), + foreignKeyReferencedTable, + targetKey.getReferencedColumns()); + + collectionLazy = revengStrategy.isForeignKeyCollectionLazy(targetKey.getName(), + foreignKeyTable, + targetKey.getColumns(), + foreignKeyReferencedTable, + targetKey.getReferencedColumns()); + + collectionRole = makeUnique(rc,collectionRole); + + String fullRolePath = StringHelper.qualify(rc.getEntityName(), collectionRole); + if (mappings.getCollection(fullRolePath)!=null) { + log.debug(fullRolePath + " found twice!"); + } + + collection.setRole(fullRolePath); // Master.setOfChildren+ + collection.setInverse(collectionInverse); // TODO: allow overriding this + collection.setLazy(collectionLazy); + collection.setFetchMode(FetchMode.SELECT); + + + return tableToClassName; + } + /** return true if this foreignkey is the only reference from this table to the same foreign table */ private boolean isUniqueReference(ForeignKey foreignKey) { @@ -326,10 +407,14 @@ final TableIdentifier tableIdentifier = TableIdentifier.create(table); - String tableIdentifierStrategyName = revengStrategy.getTableIdentifierStrategyName(tableIdentifier); - boolean naturalId = "assigned".equals(tableIdentifierStrategyName); + String tableIdentifierStrategyName = null; + boolean naturalId; + if (keyColumns.size()>1) { + tableIdentifierStrategyName = "assigned"; + naturalId = true; + id = handleCompositeKey(rc, processed, keyColumns, mapping); idPropertyname = revengStrategy.tableToIdentifierPropertyName(tableIdentifier); if(idPropertyname==null) { @@ -337,6 +422,8 @@ } } else { + tableIdentifierStrategyName = revengStrategy.getTableIdentifierStrategyName(tableIdentifier); + naturalId = "assigned".equals( tableIdentifierStrategyName ); Column pkc = (Column) keyColumns.get(0); checkColumn(pkc); @@ -363,6 +450,7 @@ } /** + * bind many-to-ones * @param table * @param rc * @param primaryKey @@ -784,7 +872,7 @@ PersistentClass persistentClass = mappings.getClass(oneToMany.getReferencedEntityName() ); if (persistentClass==null) throw new MappingException( - "Association references unmapped class: " + oneToMany.getReferencedEntityName() + "Association " + collection.getRole() + " references unmapped class: " + oneToMany.getReferencedEntityName() ); oneToMany.setAssociatedClass(persistentClass); // Child @@ -809,4 +897,5 @@ JDBCBinder.bindCollectionSecondPass(collection, persistentClasses, mappings, inheritedMetas); } } + } Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DefaultReverseEngineeringStrategy.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -1,8 +1,10 @@ package org.hibernate.cfg.reveng; import java.beans.Introspector; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; @@ -12,6 +14,9 @@ import org.hibernate.connection.ConnectionProvider; import org.hibernate.exception.SQLExceptionConverter; import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.PrimaryKey; +import org.hibernate.mapping.Table; import org.hibernate.util.StringHelper; public class DefaultReverseEngineeringStrategy implements ReverseEngineeringStrategy { @@ -193,5 +198,53 @@ public void setSettings(ReverseEngineeringSettings settings) { this.settings = settings; } + + public boolean isManyToManyTable(Table table) { + if(settings.getAutoManyToManyDetection()) { + Iterator foreignKeyIterator = table.getForeignKeyIterator(); + List foreignKeys = new ArrayList(); + while ( foreignKeyIterator.hasNext() ) { + ForeignKey fkey = (ForeignKey) foreignKeyIterator.next(); + foreignKeys.add( fkey ); + if(foreignKeys.size()>2) { + return false; // early exit if we have more than one fk. + } + } + + Set columns = new HashSet(); + Iterator columnIterator = table.getColumnIterator(); + while ( columnIterator.hasNext() ) { + Column column = (Column) columnIterator.next(); + columns.add(column); + } + + foreignKeyIterator = table.getForeignKeyIterator(); + while ( !columns.isEmpty() && foreignKeyIterator.hasNext() ) { + ForeignKey element = (ForeignKey) foreignKeyIterator.next(); + columns.removeAll( element.getColumns() ); + } + // what if one of the columns is not the primary key? + return columns.isEmpty(); + } else { + return false; + } + } + + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference) { + String propertyName = Introspector.decapitalize( StringHelper.unqualify( tableToClassName(TableIdentifier.create( toKey.getReferencedTable()) )) ); + propertyName = pluralize( propertyName ); + + if(!uniqueReference) { + //TODO: maybe use the middleTable name here ? + if(toKey.getColumns()!=null && toKey.getColumns().size()==1) { + String columnName = ( (Column) toKey.getColumns().get(0) ).getName(); + propertyName = propertyName + "For" + toUpperCamelCase(columnName); + } + else { // composite key or no columns at all safeguard + propertyName = propertyName + "For" + toUpperCamelCase(toKey.getName()); + } + } + return propertyName; + } } Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DelegatingReverseEngineeringStrategy.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DelegatingReverseEngineeringStrategy.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/DelegatingReverseEngineeringStrategy.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -5,6 +5,8 @@ import org.hibernate.connection.ConnectionProvider; import org.hibernate.exception.SQLExceptionConverter; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; public class DelegatingReverseEngineeringStrategy implements ReverseEngineeringStrategy { @@ -108,6 +110,14 @@ public void setSettings(ReverseEngineeringSettings settings) { if(delegate!=null) delegate.setSettings(settings); - } + } + + public boolean isManyToManyTable(Table table) { + return delegate==null?true:delegate.isManyToManyTable( table ); + } + + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference) { + return delegate==null?null:delegate.foreignKeyToManyToManyName( fromKey, middleTable, toKey, uniqueReference ); + } } Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/JDBCReader.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -16,7 +16,6 @@ import org.apache.commons.logging.LogFactory; import org.hibernate.JDBCException; import org.hibernate.MappingException; -import org.hibernate.cfg.JDBCBinder; import org.hibernate.cfg.JDBCBinderException; import org.hibernate.cfg.reveng.dialect.MetaDataDialect; import org.hibernate.connection.ConnectionProvider; @@ -84,17 +83,24 @@ } tables = dbs.iterateTables(); - Map oneToManyCandidates = new HashMap(); + List fks = new ArrayList(); while ( tables.hasNext() ) { Table table = (Table) tables.next(); // Done here after the basic process of collections as we might not have touched // all referenced tables (this ensure the columns are the same instances througout the basic JDBC derived model. // after this stage it should be "ok" to divert from keeping columns in sync as it can be required if the same //column is used with different aliases in the ORM mapping. - Map foreignKeys = processForeignKeys(dbs, table, progress); - mergeMultiMap(oneToManyCandidates, foreignKeys); + ForeignKeysInfo foreignKeys = processForeignKeys(dbs, table, progress); + fks.add( foreignKeys ); } + Map oneToManyCandidates = new HashMap(); + for (Iterator iter = fks.iterator(); iter.hasNext();) { + ForeignKeysInfo element = (ForeignKeysInfo) iter.next(); + Map map = element.process( revengStrategy ); + mergeMultiMap( oneToManyCandidates, map ); + } + dbs.setOneToManyCandidates(oneToManyCandidates); return dbs; @@ -104,8 +110,43 @@ } } + static class ForeignKeysInfo { + + final Map dependentTables; + final Map dependentColumns; + final Map referencedColumns; + private final Table referencedTable; + + public ForeignKeysInfo(Table referencedTable, Map tables, Map columns, Map refColumns) { + this.referencedTable = referencedTable; + this.dependentTables = tables; + this.dependentColumns = columns; + this.referencedColumns = refColumns; + } + + Map process(ReverseEngineeringStrategy revengStrategy) { + Map oneToManyCandidates = new HashMap(); + Iterator iterator = dependentTables.entrySet().iterator(); + while (iterator.hasNext() ) { + Map.Entry entry = (Map.Entry) iterator.next(); + String fkName = (String) entry.getKey(); + Table fkTable = (Table) entry.getValue(); + List columns = (List) dependentColumns.get(fkName); + List refColumns = (List) referencedColumns.get(fkName); + + String className = revengStrategy.tableToClassName(TableIdentifier.create(referencedTable) ); + + ForeignKey key = fkTable.createForeignKey(fkName, columns, className, refColumns); + key.setReferencedTable(referencedTable); + + addToMultiMap(oneToManyCandidates, className, key); + } + // map<className, foreignkey> + return oneToManyCandidates; + } + } - protected Map processForeignKeys(DatabaseCollector dbs, Table referencedTable, ProgressListener progress) throws JDBCBinderException { + protected ForeignKeysInfo processForeignKeys(DatabaseCollector dbs, Table referencedTable, ProgressListener progress) throws JDBCBinderException { // foreign key name to list of columns Map dependentColumns = new HashMap(); // foreign key name to Table @@ -250,26 +291,9 @@ } - Map oneToManyCandidates = new HashMap(); - Iterator iterator = dependentTables.entrySet().iterator(); - while (iterator.hasNext() ) { - Map.Entry entry = (Map.Entry) iterator.next(); - String fkName = (String) entry.getKey(); - Table fkTable = (Table) entry.getValue(); - List columns = (List) dependentColumns.get(fkName); - List refColumns = (List) referencedColumns.get(fkName); - - String className = revengStrategy.tableToClassName(TableIdentifier.create(referencedTable) ); - - ForeignKey key = fkTable.createForeignKey(fkName, columns, className, refColumns); - key.setReferencedTable(referencedTable); - - - addToMultiMap(oneToManyCandidates, className, key); - } - // map<className, foreignkey> - return oneToManyCandidates; - } + return new ForeignKeysInfo(referencedTable, dependentTables, dependentColumns, referencedColumns); + + } /** @@ -755,7 +779,7 @@ return false; } - private void addToMultiMap(Map multimap, String key, Object item) { + static private void addToMultiMap(Map multimap, String key, Object item) { List existing = (List) multimap.get(key); if(existing == null) { existing = new ArrayList(); Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringSettings.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -9,6 +9,7 @@ private boolean autoOptimisticLockDetection = true; private boolean createCollectionForForeignKey = true; private boolean createManyToOneForForeignKey = true; + private boolean autoManyToManyDetection; //public ReverseEngineeringSettings(ReverseEngineeringStrategy rootStrategy) { // this.rootStrategy = rootStrategy; @@ -60,7 +61,16 @@ this.createManyToOneForForeignKey = createManyToOneForForeignKey; return this; } + + public ReverseEngineeringSettings setAutoManyToManyDetection(boolean b) { + this.autoManyToManyDetection = b; + return this; + } + public boolean getAutoManyToManyDetection() { + return autoManyToManyDetection; + } + /** return the top/root strategy. Allows a lower strategy to ask another question. Dangerous to do since recursive loops can easily occur! */ /*public ReverseEngineeringStrategy getRootStrategy() { return rootStrategy; Modified: trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringStrategy.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringStrategy.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/cfg/reveng/ReverseEngineeringStrategy.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -5,6 +5,8 @@ import org.hibernate.connection.ConnectionProvider; import org.hibernate.exception.SQLExceptionConverter; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; public interface ReverseEngineeringStrategy { @@ -33,7 +35,7 @@ /** * - * @return a fully-qualified class name (must be aligned with tableToPackageName and tableToSimpleClassName) + * @return a fully-qualified class name */ public String tableToClassName(TableIdentifier tableIdentifier); @@ -43,37 +45,7 @@ * @return a property name */ public String columnToPropertyName(TableIdentifier table, String column); - - /** Should this foreignkey be excluded as a oneToMany */ - public boolean excludeForeignKeyAsCollection(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns); - - /** Should this foreignkey be excluded as a many-to-one */ - public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns); - - /** is the collection inverse or not ? */ - public boolean isForeignKeyCollectionInverse(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns); - - /** is the collection lazy or not ? */ - public boolean isForeignKeyCollectionLazy(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns); - - /** - * Return a collection role name for a Collection based on the foreignkey. - * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct - * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct - * @param uniqueReference true if there is no other references to the same table - * @return - */ - public String foreignKeyToCollectionName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference); - - /** - * - * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct - * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct - * @param uniqueReference true if there is no other references to the same table - * @return - */ - public String foreignKeyToEntityName(String keyname, TableIdentifier fromTable, List fromColumnNames, TableIdentifier referencedTable, List referencedColumnNames, boolean uniqueReference); - + public boolean excludeTable(TableIdentifier ti); public boolean excludeColumn(TableIdentifier identifier, String columnName); @@ -135,8 +107,50 @@ public String tableToCompositeIdName(TableIdentifier identifier); + /** Should this foreignkey be excluded as a oneToMany */ + public boolean excludeForeignKeyAsCollection(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns); + /** Should this foreignkey be excluded as a many-to-one */ + public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns); + /** is the collection inverse or not ? */ + public boolean isForeignKeyCollectionInverse(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns); + + /** is the collection lazy or not ? */ + public boolean isForeignKeyCollectionLazy(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns); + + /** + * Return a collection role name for a Collection based on the foreignkey. + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @param uniqueReference true if there is no other references to the same table + * @return + */ + public String foreignKeyToCollectionName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference); + + /** + * + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @param uniqueReference true if there is no other references to the same table + * @return + */ + public String foreignKeyToEntityName(String keyname, TableIdentifier fromTable, List fromColumnNames, TableIdentifier referencedTable, List referencedColumnNames, boolean uniqueReference); - + /** + * @param table + * @return true if this table is considered to be a many-to-many table. + */ + public boolean isManyToManyTable(Table table); + + /** + * + * @param middleTable + * @param uniqueReference true if there is no other references to the same table + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @return + */ + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference); + } Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/AbstractExporter.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/AbstractExporter.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/AbstractExporter.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -96,7 +96,7 @@ getArtifactCollector().formatFiles(); } - abstract public void doStart(); + abstract protected void doStart(); public String[] getTemplatePaths() { return templatePaths; Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -60,7 +60,7 @@ } } - public void doStart() { + protected void doStart() { if(filePattern==null) throw new ExporterException("File pattern not set on GenericExporter"); if(templateName==null) throw new ExporterException("Template pattern not set on GenericExporter"); Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -804,10 +804,13 @@ List propClosure = getPropertyClosureForMinimalConstructor(); if(propClosure.isEmpty()) return false; // minimal=default if(propClosure.equals(getPropertyClosureForFullConstructor())) return false; // minimal=full - return true; } + public boolean needsFullConstructor() { + return !getPropertyClosureForFullConstructor().isEmpty(); + } + public String getJavaTypeName(Property p, boolean useGenerics) { return c2j.getJavaTypeName(p, useGenerics, this); } Modified: trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/POJOClass.java =================================================================== --- trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/POJOClass.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/java/org/hibernate/tool/hbm2x/pojo/POJOClass.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -94,6 +94,7 @@ public List getPropertyClosureForSuperclassFullConstructor(); public boolean needsMinimalConstructor(); + public boolean needsFullConstructor(); public List getPropertiesForMinimalConstructor(); public List getPropertyClosureForMinimalConstructor(); public List getPropertyClosureForSuperclassMinimalConstructor(); Modified: trunk/HibernateExt/tools/src/templates/pojo/PojoConstructors.ftl =================================================================== --- trunk/HibernateExt/tools/src/templates/pojo/PojoConstructors.ftl 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/templates/pojo/PojoConstructors.ftl 2006-08-04 08:41:47 UTC (rev 10211) @@ -14,6 +14,7 @@ </#foreach> } </#if> +<#if pojo.needsFullConstructor()> /** full constructor */ public ${pojo.getDeclarationName()}(${c2j.asParameterList(pojo.getPropertyClosureForFullConstructor(), jdk5, pojo)}) { <#if pojo.isSubclass() && !pojo.getPropertyClosureForSuperclassFullConstructor().isEmpty()> @@ -23,4 +24,4 @@ this.${field.name} = ${field.name}; </#foreach> } - +</#if> Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/BaseTestCase.java =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/BaseTestCase.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/BaseTestCase.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -49,7 +49,7 @@ } - protected void assertFileAndExists(File file) { + static protected void assertFileAndExists(File file) { assertTrue(file + " does not exist", file.exists() ); assertTrue(file + " not a file", file.isFile() ); assertTrue(file + " does not have any contents", file.length()>0); Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/SeamAntTest.java =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/SeamAntTest.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/ant/SeamAntTest.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -117,10 +117,5 @@ assertNotNull(document); return document; } - - protected void assertFileAndExists(File file) { - assertTrue(file + " does not exist", file.exists() ); - assertTrue(file + " not a file", file.isFile() ); - assertTrue(file + " does not have any contents", file.length()>0); - } + } Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Constructors.hbm.xml =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Constructors.hbm.xml 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Constructors.hbm.xml 2006-08-04 08:41:47 UTC (rev 10211) @@ -59,5 +59,13 @@ <version name="aVersion" type="long"/> <property name="name" type="string"/> </class> + + <!-- HBX-584 --> + <class name="SingleFieldClass"> + <id name="id" type="string"> + <generator class="native"/> + </id> + </class> + </hibernate-mapping> Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Hbm2JavaConstructorTest.java =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Hbm2JavaConstructorTest.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/Hbm2JavaConstructorTest.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -108,6 +108,12 @@ } + public void testSingleFieldLogic() { + + + } + + public void testMinimal() { POJOClass bp = new EntityPOJOClass(getCfg().getClassMapping("BrandProduct"), new Cfg2JavaTool()); Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/ManyToManyTest.java =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/ManyToManyTest.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/hbm2x/hbm2hbmxml/ManyToManyTest.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -17,7 +17,6 @@ import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; -import org.dom4j.Node; import org.dom4j.XPath; import org.dom4j.io.SAXReader; import org.hibernate.cfg.Configuration; Added: trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/ManyToManyTest.java =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/ManyToManyTest.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/ManyToManyTest.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -0,0 +1,161 @@ +/* + * Created on 2004-12-01 + * + */ +package org.hibernate.tool.test.jdbc2cfg; + +import java.io.File; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.hibernate.MappingException; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.JDBCMetaDataConfiguration; +import org.hibernate.cfg.reveng.DefaultReverseEngineeringStrategy; +import org.hibernate.cfg.reveng.ReverseEngineeringSettings; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.tool.JDBCMetaDataBinderTestCase; +import org.hibernate.tool.hbm2x.HibernateMappingExporter; + +/** + * @author max + * + */ +public class ManyToManyTest extends JDBCMetaDataBinderTestCase { + + public static Test suite() { + return new TestSuite(ManyToManyTest.class); + } + + private JDBCMetaDataConfiguration localCfg; + + protected void configure(JDBCMetaDataConfiguration cfg) { + super.configure( cfg ); + + } + + protected void setUp() throws Exception { + super.setUp(); + + localCfg = new JDBCMetaDataConfiguration(); + + DefaultReverseEngineeringStrategy c = new DefaultReverseEngineeringStrategy(); + c.setSettings(new ReverseEngineeringSettings().setAutoManyToManyDetection(true)); + localCfg.setReverseEngineeringStrategy(c); + localCfg.readFromJDBC(); + } + protected void tearDown() throws Exception { + localCfg = null; + + super.tearDown(); + } + + public void testDefaultBiDirectional() { + + PersistentClass project = cfg.getClassMapping("Project"); + + assertNotNull(project.getProperty("worksOns")); + //assertNotNull(project.getProperty("employee")); + assertEquals(3, project.getPropertyClosureSpan()); + assertEquals("projectId", project.getIdentifierProperty().getName()); + + PersistentClass employee = cfg.getClassMapping("Employee"); + + assertNotNull(employee.getProperty("worksOns")); + assertNotNull(employee.getProperty("employees")); + assertNotNull(employee.getProperty("employee")); + //assertNotNull(employee.getProperty("projects")); + assertEquals(6, employee.getPropertyClosureSpan()); + assertEquals("id", employee.getIdentifierProperty().getName()); + + PersistentClass worksOn = cfg.getClassMapping("WorksOn"); + + assertNotNull(worksOn.getProperty("project")); + assertNotNull(worksOn.getProperty("employee")); + assertEquals(2, worksOn.getPropertyClosureSpan()); + assertEquals("id", worksOn.getIdentifierProperty().getName()); + } + + public void testAutoCreation() { + + assertNull("No middle class should be generated.", localCfg.getClassMapping( "WorksOn" )); + + assertNotNull("Should create worksontext since one of the foreign keys is not part of pk", localCfg.getClassMapping( "WorksOnContext" )); + + PersistentClass projectClass = localCfg.getClassMapping("Project"); + assertNotNull( projectClass ); + + PersistentClass employeeClass = localCfg.getClassMapping("Employee"); + assertNotNull( employeeClass ); + + assertPropertyNotExist( projectClass, "worksOns" ); + assertPropertyNotExist( employeeClass, "worksOns" ); + + Property property = employeeClass.getProperty( "projects" ); + assertNotNull( property); + assertNotNull( projectClass.getProperty( "employees" )); + + } + + public void testBuildMappings() { + + localCfg.buildMappings(); + } + + public void testGenerateAndReadable() { + + localCfg.buildMappings(); + + HibernateMappingExporter hme = new HibernateMappingExporter(localCfg, getOutputDir()); + hme.start(); + + assertFileAndExists( new File(getOutputDir(), "Employee.hbm.xml") ); + assertFileAndExists( new File(getOutputDir(), "Project.hbm.xml") ); + assertFileAndExists( new File(getOutputDir(), "WorksOnContext.hbm.xml") ); + + assertFalse(new File(getOutputDir(), "WorksOn.hbm.xml").exists() ); + + assertEquals(3, getOutputDir().listFiles().length); + + Configuration configuration = new Configuration() + .addFile( new File(getOutputDir(), "Employee.hbm.xml") ) + .addFile( new File(getOutputDir(), "Project.hbm.xml") ) + .addFile( new File(getOutputDir(), "WorksOnContext.hbm.xml") ); + + configuration.buildMappings(); + + } + + + private void assertPropertyNotExist(PersistentClass projectClass, String prop) { + try { + projectClass.getProperty(prop); + fail("property " + prop + " should not exist on " + projectClass); + } catch(MappingException e) { + // expected + } + } + + protected String[] getCreateSQL() { + return new String[] { + "create table PROJECT ( project_id integer not null, name varchar(50), primary key (project_id) )", + "create table EMPLOYEE ( id integer not null, name varchar(50), manager_id integer, primary key (id), constraint employee_manager foreign key (manager_id) references EMPLOYEE)", + "create table WORKS_ON ( project_id integer not null, employee_id integer not null, primary key (project_id, employee_id), constraint workson_employee foreign key (employee_id) references EMPLOYEE, foreign key (project_id) references PROJECT )", + "create table WORKS_ON_CONTEXT ( project_id integer not null, employee_id integer not null, created_by integer, primary key (project_id, employee_id), constraint workson_ctx_employee foreign key (employee_id) references EMPLOYEE, foreign key (project_id) references PROJECT, foreign key (created_by) references EMPLOYEE )", + //"alter table PROJECT add constraint project_manager foreign key (team_lead) references EMPLOYEE" + }; + } + + protected String[] getDropSQL() { + return new String[] { + //"alter table PROJECT drop constraint project_manager", + "drop table WORKS_ON_CONTEXT", + "drop table WORKS_ON", + "drop table EMPLOYEE", + "drop table PROJECT", + }; + } + +} Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OverrideBinderTest.java =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OverrideBinderTest.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/OverrideBinderTest.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -184,8 +184,6 @@ assertNull(repository.getTableIdentifierProperties(new TableIdentifier("blah"))); TableIdentifier ordersTable = new TableIdentifier(null,null, "ORDERS"); - - assertEquals("customOrderId", repository.tableToIdentifierPropertyName(ordersTable)); assertEquals(null, repository.tableToIdentifierPropertyName(new TableIdentifier("blah"))); @@ -219,7 +217,6 @@ assertNotNull(sv.getIdentifierGeneratorProperties()); assertEquals("seq_table", sv.getIdentifierGeneratorProperties().getProperty("table")); - } Modified: trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/foreignkeytest.reveng.xml =================================================================== --- trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/foreignkeytest.reveng.xml 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/test/org/hibernate/tool/test/jdbc2cfg/foreignkeytest.reveng.xml 2006-08-04 08:41:47 UTC (rev 10211) @@ -16,5 +16,6 @@ <set property="managedProjects"/> </foreign-key> </table> - + + </hibernate-reverse-engineering> \ No newline at end of file Modified: trunk/HibernateExt/tools/src/testsupport/NoopReverseEngineeringStrategy.java =================================================================== --- trunk/HibernateExt/tools/src/testsupport/NoopReverseEngineeringStrategy.java 2006-08-03 20:42:49 UTC (rev 10210) +++ trunk/HibernateExt/tools/src/testsupport/NoopReverseEngineeringStrategy.java 2006-08-04 08:41:47 UTC (rev 10211) @@ -6,6 +6,8 @@ import org.hibernate.cfg.reveng.TableIdentifier; import org.hibernate.connection.ConnectionProvider; import org.hibernate.exception.SQLExceptionConverter; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; /** * noop naming strategy for testing validation of parameters in ant test. @@ -140,4 +142,14 @@ // TODO Auto-generated method stub } + + public boolean isManyToManyTable(Table table) { + // TODO Auto-generated method stub + return false; + } + + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference) { + // TODO Auto-generated method stub + return null; + } } |