|
|
|
jrbauer
|
Author: jrbauer
Date: Tue Nov 3 22:48:43 2009 New Revision: 832587 URL: http://svn.apache.org/viewvc?rev=832587&view=rev Log: OPENJPA-1083 Fixed a mapping tool failure caused by the inability to discover and drop multi-column foreign key constraints. Multi-column FK's were not getting dropped, but got added after clearing out the tables. Trying to add an existing FK caused an exception when using Oracle. Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/schema/localizer.properties Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java?rev=832587&r1=832586&r2=832587&view=diff ============================================================================== --- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java (original) +++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java Tue Nov 3 22:48:43 2009 @@ -26,6 +26,7 @@ import java.util.List; import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.Localizer; @@ -753,7 +754,7 @@ Schema schema = getTable().getSchema(); ForeignKey[] fks = dbdict.getImportedKeys(conn.getMetaData(), conn.getCatalog(), schema.getName(), - getTable().getName(), conn); + getTable().getName(), conn, false); for ( int i=0; i< fks.length; i++) { Table localtable = schema.getTable(fks[i].getTableName()); Table pkTable = schema.getTable( @@ -768,10 +769,33 @@ fkTemp.setDeferred(fks[i].isDeferred()); fkTemp.setDeleteAction(fks[i].getDeleteAction()); } - if( ! fkTemp.containsColumn( - localtable.getColumn(fks[i].getColumnName(), dbdict))) - fkTemp.join(localtable.getColumn(fks[i].getColumnName(), dbdict), - pkTable.getColumn(fks[i].getPrimaryKeyColumnName(), dbdict)); + if (fks[i].getColumns() == null || fks[i].getColumns().length == 0) { + // Singular column foreign key + if( ! fkTemp.containsColumn( + localtable.getColumn(fks[i].getColumnName(), dbdict))) + fkTemp.join(localtable.getColumn(fks[i].getColumnName(), dbdict), + pkTable.getColumn(fks[i].getPrimaryKeyColumnName(), dbdict)); + } else { + // Add the multi-column foreign key, joining local and pk columns in + // the temporary key + Column[] locCols = fks[i].getColumns(); + Column[] pkCols = fks[i].getPrimaryKeyColumns(); + // Column counts must match + if (locCols != null && pkCols != null & + locCols.length != pkCols.length) { + Log log = dbdict.getLog(); + if (log.isTraceEnabled()) { + log.trace(_loc.get("fk-column-mismatch")); + } + } + for (int j = 0; j < locCols.length; j++) { + if( ! fkTemp.containsColumn( + localtable.getColumn(locCols[j].getName(), dbdict))) { + fkTemp.join(localtable.getColumn(locCols[j].getName(), dbdict), + pkTable.getColumn(pkCols[j].getName(), dbdict)); + } + } + } if( equalsForeignKey(fkTemp)) { if(addFK) @@ -790,5 +814,96 @@ } return retVal; } + + /** + * Joins the column of a single column FK to this FK. + * @param fk + */ + public void addColumn(ForeignKey fk) { + // Convert simple name based fk to a multi-column FK if necessary. + if (getColumns() == null || getColumns().length == 0) { + // If this FK is single column key, covert to a multi-column key + Column[] keyCols = createKeyColumns(this); + if (keyCols[0] != null && keyCols[1] != null) { + setPrimaryKeyColumnName(null); + setColumnName(null); + join(keyCols[0], keyCols[1]); + } + } + // Create the local and primary key columns from the fk and add them + // to this fk. + Column[] keyCols = createKeyColumns(fk); + if (keyCols[0] != null && keyCols[1] != null) { + join(keyCols[0], keyCols[1]); + } + } + /* + * Creates the local and primary key columns for a name-based fk. + * @return Column[] element 0 is local column + * element 1 is the primary key in another table. + */ + private static Column[] createKeyColumns(ForeignKey fk) { + Column fkCol = null; + if (!StringUtils.isEmpty(fk.getColumnName())) { + fkCol = new Column(); + fkCol.setName(fk.getColumnName()); + fkCol.setTableName(fk.getTableName()); + fkCol.setSchemaName(fk.getSchemaName()); + } + + Column pkCol = null; + if (!StringUtils.isEmpty(fk.getPrimaryKeyColumnName())) { + pkCol = new Column(); + pkCol.setName(fk.getPrimaryKeyColumnName()); + pkCol.setTableName(fk.getPrimaryKeyTableName()); + pkCol.setSchemaName(fk.getPrimaryKeySchemaName()); + } + return new Column[] { fkCol, pkCol }; + } + + /* + * ForeignKey utility class which determines equality based upon the + * non-column state of the keys. + */ + public static class FKMapKey { + + private ForeignKey _fk; + + public FKMapKey(ForeignKey fk) { + _fk = fk; + } + public ForeignKey getFk() { + return _fk; + } + + public int hashCode() { + return getFk().getName() != null ? getFk().getName().hashCode() : getFk().hashCode(); + } + + public boolean equals(Object fkObj) { + if (fkObj == this) { + return true; + } + if (fkObj == null || !(fkObj instanceof FKMapKey)) { + return false; + } + ForeignKey fk = ((FKMapKey)fkObj).getFk(); + if (getFk().getDeleteAction() != fk.getDeleteAction()) + return false; + if (getFk().isDeferred() != fk.isDeferred()) + return false; + if (!getFk().getName().equals(fk.getName())) { + return false; + } + // Assert PK table name and schema + if (!StringUtils.equals(getFk().getPrimaryKeySchemaName(), fk.getPrimaryKeySchemaName()) || + !StringUtils.equals(getFk().getPrimaryKeyTableName(), fk.getPrimaryKeyTableName()) || + !StringUtils.equals(getFk().getSchemaName(), fk.getSchemaName()) || + !StringUtils.equals(getFk().getTableName(), fk.getTableName())) { + return false; + } + return true; + } + } } Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=832587&r1=832586&r2=832587&view=diff ============================================================================== --- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original) +++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Tue Nov 3 22:48:43 2009 @@ -86,6 +86,7 @@ import org.apache.openjpa.jdbc.schema.Sequence; import org.apache.openjpa.jdbc.schema.Table; import org.apache.openjpa.jdbc.schema.Unique; +import org.apache.openjpa.jdbc.schema.ForeignKey.FKMapKey; import org.apache.openjpa.kernel.Filters; import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.Seq; @@ -4118,6 +4119,16 @@ public ForeignKey[] getImportedKeys(DatabaseMetaData meta, String catalog, String schemaName, String tableName, Connection conn) throws SQLException { + return getImportedKeys(meta, catalog, schemaName, tableName, conn, true); + } + + /** + * Reflect on the schema to return full foreign keys imported by the given + * table pattern. + */ + public ForeignKey[] getImportedKeys(DatabaseMetaData meta, String catalog, + String schemaName, String tableName, Connection conn, boolean partialKeys) + throws SQLException { if (!supportsForeignKeys) return null; if (tableName == null && !supportsNullTableForGetImportedKeys) @@ -4130,19 +4141,50 @@ getSchemaNameForMetadata(schemaName), getTableNameForMetadata(tableName)); - List importedKeyList = new ArrayList(); - while (keys != null && keys.next()) - importedKeyList.add(newForeignKey(keys)); + List<ForeignKey> importedKeyList = new ArrayList<ForeignKey>(); + Map<FKMapKey, ForeignKey> fkMap = new HashMap<FKMapKey, ForeignKey>(); + + while (keys != null && keys.next()) { + ForeignKey nfk = newForeignKey(keys); + if (!partialKeys) { + ForeignKey fk = combineForeignKey(fkMap, nfk); + // If the key returned != new key, fk col was combined + // with existing fk. + if (fk != nfk) { + continue; + } + } + importedKeyList.add(nfk); + } return (ForeignKey[]) importedKeyList.toArray (new ForeignKey[importedKeyList.size()]); } finally { - if (keys != null) + if (keys != null) { try { keys.close(); } catch (Exception e) { } + } } } + + /* + * Combines partial foreign keys into singular key + */ + protected ForeignKey combineForeignKey(Map<FKMapKey, ForeignKey> fkMap, + ForeignKey fk) { + + FKMapKey fkmk = new FKMapKey(fk); + ForeignKey baseKey = fkMap.get(fkmk); + // Found the FK, add the additional column + if (baseKey != null) { + baseKey.addColumn(fk); + return baseKey; + } + // fkey is new + fkMap.put(fkmk, fk); + return fk; + } /** * Create a new foreign key from the information in the schema metadata. Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java?rev=832587&r1=832586&r2=832587&view=diff ============================================================================== --- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java (original) +++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java Tue Nov 3 22:48:43 2009 @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -49,6 +50,7 @@ import org.apache.openjpa.jdbc.schema.PrimaryKey; import org.apache.openjpa.jdbc.schema.Sequence; import org.apache.openjpa.jdbc.schema.Table; +import org.apache.openjpa.jdbc.schema.ForeignKey.FKMapKey; import org.apache.openjpa.lib.jdbc.DelegatingDatabaseMetaData; import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement; import org.apache.openjpa.lib.util.J2DoPrivHelper; @@ -756,7 +758,7 @@ } public ForeignKey[] getImportedKeys(DatabaseMetaData meta, String catalog, - String schemaName, String tableName, Connection conn) + String schemaName, String tableName, Connection conn, boolean partialKeys) throws SQLException { StringBuffer delAction = new StringBuffer("DECODE(t1.DELETE_RULE"). append(", 'NO ACTION', ").append(meta.importedKeyNoAction). @@ -805,9 +807,20 @@ setString(stmnt, idx++, tableName.toUpperCase(), null); setTimeouts(stmnt, conf, false); rs = stmnt.executeQuery(); - List fkList = new ArrayList(); - while (rs != null && rs.next()) - fkList.add(newForeignKey(rs)); + List<ForeignKey> fkList = new ArrayList<ForeignKey>(); + Map<FKMapKey, ForeignKey> fkMap = new HashMap<FKMapKey, ForeignKey>(); + + while (rs != null && rs.next()) { + ForeignKey nfk = newForeignKey(rs); + if (!partialKeys) { + ForeignKey fk = combineForeignKey(fkMap, nfk); + // Only add the fk to the import list if it is new + if (fk != nfk) { + continue; + } + } + fkList.add(nfk); + } return (ForeignKey[]) fkList.toArray (new ForeignKey[fkList.size()]); } finally { Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/schema/localizer.properties URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/schema/localizer.properties?rev=832587&r1=832586&r2=832587&view=diff ============================================================================== --- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/schema/localizer.properties (original) +++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/schema/localizer.properties Tue Nov 3 22:48:43 2009 @@ -154,3 +154,5 @@ no-column: Can not find column "{0}" in table "{1}" except-read-fk-name: An exception occurred when obtaining the foreign key \ names from the database. +fk-column-mismatch: Unable to create multi-column foreign key. The key \ + columns do not match primary keys in foreign table. |
||||||||||||||||
| Free Embeddable Forum Powered by Nabble | Help |