This is an automated email from the ASF dual-hosted git repository. mbrohl pushed a commit to branch release18.12 in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/release18.12 by this push: new 1ed929c Fixed: Induction from DB does not represent relations properly. (OFBIZ-12178) 1ed929c is described below commit 1ed929cfc6675ef4cece8941b6333a53c5e1b477 Author: Benjamin Jugl <benjamin.j...@ecomify.de> AuthorDate: Thu Dec 2 12:42:31 2021 +0100 Fixed: Induction from DB does not represent relations properly. (OFBIZ-12178) --- .../org/apache/ofbiz/entity/jdbc/DatabaseUtil.java | 260 ++++++++++++++------- .../org/apache/ofbiz/entity/model/ModelEntity.java | 27 +++ .../apache/ofbiz/entity/model/ModelRelation.java | 67 +++--- framework/webtools/config/WebtoolsUiLabels.xml | 8 + .../webtools/template/entity/ModelInduceFromDb.ftl | 3 + 5 files changed, 251 insertions(+), 114 deletions(-) diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/DatabaseUtil.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/DatabaseUtil.java index f20dad1..8d9b252 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/DatabaseUtil.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/DatabaseUtil.java @@ -45,6 +45,7 @@ import java.util.concurrent.Future; import org.apache.ofbiz.base.concurrent.ExecutionPool; import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.UtilTimer; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.GenericEntityException; @@ -58,6 +59,7 @@ import org.apache.ofbiz.entity.model.ModelFieldTypeReader; import org.apache.ofbiz.entity.model.ModelIndex; import org.apache.ofbiz.entity.model.ModelKeyMap; import org.apache.ofbiz.entity.model.ModelRelation; +import org.apache.ofbiz.entity.model.ModelUtil; import org.apache.ofbiz.entity.model.ModelViewEntity; import org.apache.ofbiz.entity.transaction.TransactionFactoryLoader; import org.apache.ofbiz.entity.transaction.TransactionUtil; @@ -720,7 +722,7 @@ public class DatabaseUtil { // get ALL column info, put into hashmap by table name Map<String, Map<String, ColumnCheckInfo>> colInfo = getColumnInfo(tableNames, true, messages, executor); - + Map<String, Map<String, ReferenceCheckInfo>> refInfo = getReferenceInfo(tableNames, messages); // go through each table and make a ModelEntity object, add to list // for each entity make corresponding ModelField objects // then print out XML for the entities/fields @@ -731,7 +733,8 @@ public class DatabaseUtil { // iterate over the table names is alphabetical order for (String tableName: new TreeSet<String>(colInfo.keySet())) { Map<String, ColumnCheckInfo> colMap = colInfo.get(tableName); - ModelEntity newEntity = new ModelEntity(tableName, colMap, modelFieldTypeReader, isCaseSensitive); + Map<String, ReferenceCheckInfo> refMap = refInfo.get(tableName); + ModelEntity newEntity = new ModelEntity(tableName, colMap, refMap, modelFieldTypeReader, isCaseSensitive); newEntList.add(newEntity); } @@ -1229,7 +1232,17 @@ public class DatabaseUtil { return pkCount; } - public Map<String, Map<String, ReferenceCheckInfo>> getReferenceInfo(Set<String> tableNames, Collection<String> messages) { + /** + * Gets reference info. + * @param tableNames the table names + * @param messages the messages + * @return the reference info + */ + public Map<String, Map<String, ReferenceCheckInfo>> getReferenceInfo(Set<String> tableNames, + Collection<String> messages) { + if (UtilValidate.isEmpty(tableNames)) { + return new HashMap<>(); + } Connection connection = getConnectionLogged(messages); if (connection == null) { return null; @@ -1241,39 +1254,30 @@ public class DatabaseUtil { } catch (SQLException e) { String message = "Unable to get database meta data... Error was:" + e.toString(); Debug.logError(message, module); - if (messages != null) messages.add(message); + if (messages != null) { + messages.add(message); + } try { connection.close(); } catch (SQLException e2) { - String message2 = "Unable to close database connection, continuing anyway... Error was:" + e2.toString(); + String message2 = "Unable to close database connection, continuing anyway... Error was:" + e2 + .toString(); Debug.logError(message2, module); - if (messages != null) messages.add(message2); + if (messages != null) { + messages.add(message2); + } } return null; } - /* - try { - if (Debug.infoOn()) Debug.logInfo("Database Product Name is " + dbData.getDatabaseProductName(), module); - if (Debug.infoOn()) Debug.logInfo("Database Product Version is " + dbData.getDatabaseProductVersion(), module); - } catch (SQLException e) { - Debug.logWarning("Unable to get Database name & version information", module); - } - try { - if (Debug.infoOn()) Debug.logInfo("Database Driver Name is " + dbData.getDriverName(), module); - if (Debug.infoOn()) Debug.logInfo("Database Driver Version is " + dbData.getDriverVersion(), module); - } catch (SQLException e) { - Debug.logWarning("Unable to get Driver name & version information", module); - } - */ - - if (Debug.infoOn()) Debug.logInfo("Getting Foreign Key (Reference) Info From Database", module); + if (Debug.infoOn()) { + Debug.logInfo("Getting Foreign Key (Reference) Info From Database", module); + } - Map<String, Map<String, ReferenceCheckInfo>> refInfo = new HashMap<String, Map<String, ReferenceCheckInfo>>(); + Map<String, Map<String, ReferenceCheckInfo>> retMap = new HashMap<>(); try { - // ResultSet rsCols = dbData.getCrossReference(null, null, null, null, null, null); String lookupSchemaName = getSchemaName(dbData); boolean needsUpperCase = false; try { @@ -1281,92 +1285,106 @@ public class DatabaseUtil { } catch (SQLException e) { String message = "Error getting identifier case information... Error was:" + e.toString(); Debug.logError(message, module); - if (messages != null) messages.add(message); + if (messages != null) { + messages.add(message); + } } - ResultSet rsCols = dbData.getImportedKeys(null, lookupSchemaName, null); int totalFkRefs = 0; - // Iterator tableNamesIter = tableNames.iterator(); - // while (tableNamesIter.hasNext()) { - // String tableName = (String) tableNamesIter.next(); - // ResultSet rsCols = dbData.getImportedKeys(null, null, tableName); - // Debug.logVerbose("Getting imported keys for table " + tableName, module); + for (String tableName : tableNames) { + String shortTableName = tableName.split("\\.").length > 0 ? tableName.split("\\.")[tableName.split( + "\\.").length - 1] : tableName; + ResultSet rsCols = dbData.getImportedKeys(null, lookupSchemaName, shortTableName); - while (rsCols.next()) { - try { - ReferenceCheckInfo rcInfo = new ReferenceCheckInfo(); + Map<String, ReferenceCheckInfo> tableRefInfo = retMap.get(tableName); + if (tableRefInfo == null) { + tableRefInfo = new HashMap<>(); + totalFkRefs++; + } + while (rsCols.next()) { + try { + String fkName = toUpper(rsCols.getString("FK_NAME"), needsUpperCase); + int seq = rsCols.getInt("KEY_SEQ"); + String fkTableName = toUpper(rsCols.getString("FKTABLE_NAME"), needsUpperCase); + String fkColumnName = toUpper(rsCols.getString("FKCOLUMN_NAME"), needsUpperCase); + String pkTableName = toUpper(rsCols.getString("PKTABLE_NAME"), needsUpperCase); + String pkColumnName = toUpper(rsCols.getString("PKCOLUMN_NAME"), needsUpperCase); - rcInfo.pkTableName = rsCols.getString("PKTABLE_NAME"); - if (needsUpperCase && rcInfo.pkTableName != null) { - rcInfo.pkTableName = rcInfo.pkTableName.toUpperCase(); - } - rcInfo.pkColumnName = rsCols.getString("PKCOLUMN_NAME"); - if (needsUpperCase && rcInfo.pkColumnName != null) { - rcInfo.pkColumnName = rcInfo.pkColumnName.toUpperCase(); - } + ReferenceCheckInfo rcInfo = tableRefInfo.get(fkName); + if (rcInfo == null) { + rcInfo = new ReferenceCheckInfo(); - rcInfo.fkTableName = rsCols.getString("FKTABLE_NAME"); - if (needsUpperCase && rcInfo.fkTableName != null) { - rcInfo.fkTableName = rcInfo.fkTableName.toUpperCase(); - } - // ignore the column info if the FK table name is not in the list we are concerned with - if (!tableNames.contains(rcInfo.fkTableName)) { - continue; - } - rcInfo.fkColumnName = rsCols.getString("FKCOLUMN_NAME"); - if (needsUpperCase && rcInfo.fkColumnName != null) { - rcInfo.fkColumnName = rcInfo.fkColumnName.toUpperCase(); - } - rcInfo.fkName = rsCols.getString("FK_NAME"); - if (needsUpperCase && rcInfo.fkName != null) { - rcInfo.fkName = rcInfo.fkName.toUpperCase(); - } + } - if (Debug.verboseOn()) Debug.logVerbose("Got: " + rcInfo.toString(), module); + rcInfo.fkName = fkName; + rcInfo.fkTableName = fkTableName; + rcInfo.pkTableName = pkTableName; + + if (seq > 1) { + rcInfo.pkColumnName = rcInfo.pkColumnName.concat(",").concat(pkColumnName); + rcInfo.fkColumnName = rcInfo.fkColumnName.concat(",").concat(fkColumnName); + } else { + rcInfo.pkColumnName = pkColumnName; + rcInfo.fkColumnName = fkColumnName; + } - Map<String, ReferenceCheckInfo> tableRefInfo = refInfo.get(rcInfo.fkTableName); - if (tableRefInfo == null) { - tableRefInfo = new HashMap<String, ReferenceCheckInfo>(); - refInfo.put(rcInfo.fkTableName, tableRefInfo); - if (Debug.verboseOn()) Debug.logVerbose("Adding new Map for table: " + rcInfo.fkTableName, module); + if (Debug.verboseOn()) { + Debug.logVerbose("Got: " + rcInfo.toString(), module); + } + tableRefInfo.put(fkName, rcInfo); + retMap.put(tableName, tableRefInfo); + } catch (SQLException e) { + String message = "Error getting fk reference info for table. Error was:" + e.toString(); + Debug.logError(message, module); + if (messages != null) { + messages.add(message); + } + continue; } - if (!tableRefInfo.containsKey(rcInfo.fkName)) totalFkRefs++; - tableRefInfo.put(rcInfo.fkName, rcInfo); + } + try { + rsCols.close(); } catch (SQLException e) { - String message = "Error getting fk reference info for table. Error was:" + e.toString(); + String message = "Unable to close ResultSet for fk reference list, continuing anyway... Error was:" + + e.toString(); Debug.logError(message, module); - if (messages != null) messages.add(message); - continue; + if (messages != null) { + messages.add(message); + } } } - - // if (Debug.infoOn()) Debug.logInfo("There are " + totalFkRefs + " in the database", module); - try { - rsCols.close(); - } catch (SQLException e) { - String message = "Unable to close ResultSet for fk reference list, continuing anyway... Error was:" + e.toString(); - Debug.logError(message, module); - if (messages != null) messages.add(message); + if (Debug.infoOn()) { + Debug.logInfo("There are " + totalFkRefs + " foreign key refs in the database", module); } - // } - if (Debug.infoOn()) Debug.logInfo("There are " + totalFkRefs + " foreign key refs in the database", module); } catch (SQLException e) { - String message = "Error getting fk reference meta data Error was:" + e.toString() + ". Not checking fk refs."; + String message = "Error getting fk reference meta data Error was:" + e.toString() + + ". Not checking fk refs."; Debug.logError(message, module); - if (messages != null) messages.add(message); - refInfo = null; + if (messages != null) { + messages.add(message); + } + retMap = null; } finally { try { connection.close(); } catch (SQLException e) { String message = "Unable to close database connection, continuing anyway... Error was:" + e.toString(); Debug.logError(message, module); - if (messages != null) messages.add(message); + if (messages != null) { + messages.add(message); + } } } - return refInfo; + return retMap; + } + + private String toUpper(String string, boolean needsUpperCase) { + if (needsUpperCase && string != null) { + string = string.toUpperCase(); + } + return string; } public Map<String, Set<String>> getIndexInfo(Set<String> tableNames, Collection<String> messages, boolean[] needsUpperCase) { @@ -2802,9 +2820,79 @@ public class DatabaseUtil { /** Comma separated list of column names in the primary tables foreign keys */ public String fkColumnName; + /** + * Gets pk table name + * @return the pk table name + */ + public String getPkTableName() { + return pkTableName; + } + + /** + * Gets pk column name + * @return the pk column name + */ + public String getPkColumnName() { + return pkColumnName; + } + + /** + * Gets fk name + * @return the fk name + */ + public String getFkName() { + return fkName; + } + + /** + * Gets fk table name + * @return the fk table name + */ + public String getFkTableName() { + return fkTableName; + } + + /** + * Gets fk column name + * @return the fk column name + */ + public String getFkColumnName() { + return fkColumnName; + } + @Override public String toString() { - return "FK Reference from table " + fkTableName + " called " + fkName + " to PK in table " + pkTableName; + StringBuilder ret = new StringBuilder(); + ret.append("FK Reference from table "); + ret.append(fkTableName); + ret.append(" called "); + ret.append(fkName); + ret.append(" to PK in table "); + ret.append(pkTableName); + return ret.toString(); + } + + /** + * Converts the column information into ModelKeyMaps + * @return a list of ModelKeyMaps + */ + public List<ModelKeyMap> toModelKeyMapList() { + List<ModelKeyMap> keyMaps = new ArrayList<>(); + + List<String> fieldNames = StringUtil.split(fkColumnName, ","); + List<String> relFieldNames = StringUtil.split(pkColumnName, ","); + if (fieldNames.size() != relFieldNames.size()) { + Debug.logError("Count of related fields for relation does not match!", module); + return null; + } + for (int i = 0; i < fieldNames.size(); i++) { + String fieldName = ModelUtil.dbNameToVarName(fieldNames.get(i)); + String relFieldName = ModelUtil.dbNameToVarName(relFieldNames.get(i)); + ModelKeyMap keyMap = new ModelKeyMap(fieldName, relFieldName); + keyMaps.add(keyMap); + } + + return keyMaps; } - } + } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelEntity.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelEntity.java index c1a3cb1..86df75d 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelEntity.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelEntity.java @@ -245,6 +245,33 @@ public class ModelEntity implements Comparable<ModelEntity>, Serializable { } } + public ModelEntity(String tableName, Map<String, DatabaseUtil.ColumnCheckInfo> colMap, + Map<String, DatabaseUtil.ReferenceCheckInfo> refMap, ModelFieldTypeReader modelFieldTypeReader, + boolean isCaseSensitive) { + // if there is a dot in the name, remove it and everything before it, should be + // the schema name + this.modelReader = null; + this.modelInfo = ModelInfo.DEFAULT; + this.tableName = tableName; + int dotIndex = this.tableName.indexOf('.'); + if (dotIndex >= 0) { + this.tableName = this.tableName.substring(dotIndex + 1); + } + this.entityName = ModelUtil.dbNameToClassName(this.tableName); + for (Map.Entry<String, DatabaseUtil.ColumnCheckInfo> columnEntry : colMap.entrySet()) { + DatabaseUtil.ColumnCheckInfo ccInfo = columnEntry.getValue(); + ModelField newField = ModelField.create(this, ccInfo, modelFieldTypeReader); + addField(newField); + } + if (UtilValidate.isNotEmpty(refMap)) { + for (Map.Entry<String, DatabaseUtil.ReferenceCheckInfo> referenceEntry : refMap.entrySet()) { + DatabaseUtil.ReferenceCheckInfo rcInfo = referenceEntry.getValue(); + ModelRelation newRel = ModelRelation.create(this, rcInfo, false); + addRelation(newRel); + } + } + } + protected void populateBasicInfo(Element entityElement) { this.entityName = UtilXml.checkEmpty(entityElement.getAttribute("entity-name")).intern(); this.tableName = UtilXml.checkEmpty(entityElement.getAttribute("table-name"), ModelUtil.javaNameToDbName(this.entityName)).intern(); diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelRelation.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelRelation.java index 4e3489f..014d827 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelRelation.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelRelation.java @@ -28,6 +28,7 @@ import java.util.TreeSet; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.jdbc.DatabaseUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -39,6 +40,34 @@ import org.w3c.dom.Element; @SuppressWarnings("serial") public final class ModelRelation extends ModelChild { + /* + * Developers - this is an immutable class. Once constructed, the object should not change state. + * Therefore, 'setter' methods are not allowed. If client code needs to modify the object's + * state, then it can create a new copy with the changed values. + */ + + /** the title, gives a name/description to the relation */ + private final String title; + + /** the type: either "one" or "many" or "one-nofk" */ + private final String type; + + /** the name of the related entity */ + private final String relEntityName; + + /** the name to use for a database foreign key, if applies */ + private final String fkName; + + /** keyMaps defining how to lookup the relatedTable using columns from this table */ + private final List<ModelKeyMap> keyMaps; + + private final boolean isAutoRelation; + + /** A String to uniquely identify this relation. */ + private final String fullName; + + private final String combinedName; + /** * Returns a new <code>ModelRelation</code> instance, initialized with the specified values. * @@ -100,33 +129,15 @@ public final class ModelRelation extends ModelChild { return new ModelRelation(modelEntity, description, type, title, relEntityName, fkName, keyMaps, isAutoRelation); } - /* - * Developers - this is an immutable class. Once constructed, the object should not change state. - * Therefore, 'setter' methods are not allowed. If client code needs to modify the object's - * state, then it can create a new copy with the changed values. - */ - - /** the title, gives a name/description to the relation */ - private final String title; - - /** the type: either "one" or "many" or "one-nofk" */ - private final String type; - - /** the name of the related entity */ - private final String relEntityName; - - /** the name to use for a database foreign key, if applies */ - private final String fkName; - - /** keyMaps defining how to lookup the relatedTable using columns from this table */ - private final List<ModelKeyMap> keyMaps; - - private final boolean isAutoRelation; - - /** A String to uniquely identify this relation. */ - private final String fullName; - - private final String combinedName; + public static ModelRelation create(ModelEntity modelEntity, DatabaseUtil.ReferenceCheckInfo refInfo, boolean isAutoRelation) { + String fkName = refInfo.getFkName(); + String title = null; + String type = null; + String description = null; + String relEntityName = ModelUtil.dbNameToClassName(refInfo.getPkTableName()); + List<ModelKeyMap> keyMaps = refInfo.toModelKeyMapList(); + return new ModelRelation(modelEntity, description, type, title, relEntityName, fkName, keyMaps, isAutoRelation); + } private ModelRelation(ModelEntity modelEntity, String description, String type, String title, String relEntityName, String fkName, List<ModelKeyMap> keyMaps, boolean isAutoRelation) { super(modelEntity, description); @@ -149,7 +160,7 @@ public final class ModelRelation extends ModelChild { } sb.append("]"); this.fullName = sb.toString(); - this.combinedName = title.concat(relEntityName); + this.combinedName = title != null ? title.concat(relEntityName) : relEntityName; } /** Returns the combined name (title + related entity name). */ diff --git a/framework/webtools/config/WebtoolsUiLabels.xml b/framework/webtools/config/WebtoolsUiLabels.xml index 038000f..cc06e39 100644 --- a/framework/webtools/config/WebtoolsUiLabels.xml +++ b/framework/webtools/config/WebtoolsUiLabels.xml @@ -6029,4 +6029,12 @@ <value xml:lang="de">Ermittlung der Datenstruktur fehlgeschlagen. Bitte gültige Datenquelle angeben.</value> <value xml:lang="en">Could not determine Structure for this datasource. Please choose a valid datasource.</value> </property> + <property key="typeAndTitleDisclaimer"> + <value xml:lang="de">Bitte beachten Sie, dass die Attribute Type und Title der Referenzen nicht aus der Datenbank heraus rekonstruiert werden können.</value> + <value xml:lang="en">Please note that the attributes Type and Title for references cannot be derived from the database.</value> + </property> + <property key="pleaseAddManually"> + <value xml:lang="de">Diese müssen händisch nachgepflegt werden.</value> + <value xml:lang="en">These must be added manually.</value> + </property> </resource> diff --git a/framework/webtools/template/entity/ModelInduceFromDb.ftl b/framework/webtools/template/entity/ModelInduceFromDb.ftl index bb40ce6..c0c5ca1 100644 --- a/framework/webtools/template/entity/ModelInduceFromDb.ftl +++ b/framework/webtools/template/entity/ModelInduceFromDb.ftl @@ -54,6 +54,9 @@ under the License. </table> </form> </hr> +<div>${uiLabelMap.typeAndTitleDisclaimer}</div> +<div>${uiLabelMap.pleaseAddManually}</div> +</hr> <div> <textarea cols="60" rows="50" name="${uiLabelMap.ModelInduceInducedText}">${inducedText!}</textarea> </div>