diff --git a/jOOQ-meta/pom.xml b/jOOQ-meta/pom.xml index 73257f4a81..ec2b4bd807 100644 --- a/jOOQ-meta/pom.xml +++ b/jOOQ-meta/pom.xml @@ -131,6 +131,54 @@ + + + vertabelo-xml + + generate + + + true + true + false + src/main/resources/xsd + src/main/resources/xjb/vertabelo + + vertabelo-2.1.xsd + + org.jooq.util.vertabelo.jaxb + + -Xxew + -Xxew:instantiate lazy + -Xxew:delete + -Xfluent-api + -Xdefault-value + -Xannotate + + + + com.github.jaxb-xew-plugin + jaxb-xew-plugin + 1.0 + + + org.jvnet.jaxb2_commons + jaxb2-fluent-api + 3.0 + + + org.jvnet.jaxb2_commons + jaxb2-default-value + 1.1 + + + org.jvnet.jaxb2_commons + jaxb2-basics-annotate + 0.6.2 + + + + @@ -165,4 +213,4 @@ jooq - \ No newline at end of file + diff --git a/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLDatabase.java b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLDatabase.java new file mode 100644 index 0000000000..18fceb76e3 --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLDatabase.java @@ -0,0 +1,449 @@ +package org.jooq.util.vertabelo; + +import java.io.File; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.xml.bind.JAXB; + +import org.jooq.DSLContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; +import org.jooq.tools.JooqLogger; +import org.jooq.tools.StringUtils; +import org.jooq.util.AbstractDatabase; +import org.jooq.util.ArrayDefinition; +import org.jooq.util.CheckConstraintDefinition; +import org.jooq.util.ColumnDefinition; +import org.jooq.util.DataTypeDefinition; +import org.jooq.util.DefaultCheckConstraintDefinition; +import org.jooq.util.DefaultDataTypeDefinition; +import org.jooq.util.DefaultRelations; +import org.jooq.util.DefaultSequenceDefinition; +import org.jooq.util.EnumDefinition; +import org.jooq.util.PackageDefinition; +import org.jooq.util.RoutineDefinition; +import org.jooq.util.SchemaDefinition; +import org.jooq.util.SequenceDefinition; +import org.jooq.util.TableDefinition; +import org.jooq.util.UDTDefinition; +import org.jooq.util.vertabelo.jaxb.AlternateKey; +import org.jooq.util.vertabelo.jaxb.AlternateKeyColumn; +import org.jooq.util.vertabelo.jaxb.Column; +import org.jooq.util.vertabelo.jaxb.DatabaseModel; +import org.jooq.util.vertabelo.jaxb.Property; +import org.jooq.util.vertabelo.jaxb.Reference; +import org.jooq.util.vertabelo.jaxb.ReferenceColumn; +import org.jooq.util.vertabelo.jaxb.Sequence; +import org.jooq.util.vertabelo.jaxb.Table; +import org.jooq.util.vertabelo.jaxb.TableCheck; +import org.jooq.util.vertabelo.jaxb.View; + +/** + * The Vertabelo XML Database + * + * @author Michał Kołodziejski + */ +public class VertabeloXMLDatabase extends AbstractDatabase { + + interface TableOperation { + void invoke(Table table, String schemaName); + } + + interface ViewOperation { + void invoke(View view, String schemaName); + } + + + private static final JooqLogger log = JooqLogger.getLogger(VertabeloXMLDatabase.class); + + // codegen properties + private static final String XML_FILE_PARAM = "xml-file"; + private static final String DIALECT_PARAM = "dialect"; + + // XML additional properties + private static final String SCHEMA_ADDITIONAL_PROPERTY_NAME = "Schema"; + private static final String PK_ADDITIONAL_PROPERTY_NAME = "Primary key name"; + + + protected DatabaseModel databaseModel; + + protected DatabaseModel databaseModel() { + if(databaseModel == null) { + databaseModel = JAXB.unmarshal(new File(getProperties().getProperty(XML_FILE_PARAM)), DatabaseModel.class); + } + + return databaseModel; + } + + + @Override + protected DSLContext create0() { + @SuppressWarnings("deprecation") + SQLDialect dialect = SQLDialect.SQL99; + + try { + dialect = SQLDialect.valueOf(getProperties().getProperty(DIALECT_PARAM)); + } + catch (Exception ignore) {} + + return DSL.using(dialect); + } + + + @Override + protected void loadPrimaryKeys(final DefaultRelations relations) throws SQLException { + + filterTablesBySchema(databaseModel().getTables(), new TableOperation() { + @Override + public void invoke(Table table, String schemaName) { + + SchemaDefinition schema = getSchema(schemaName); + TableDefinition tableDefinition = getTable(schema, table.getName()); + + if (tableDefinition != null) { + String pkName = getTablePkName(table); + + // iterate through all columns and find PK columns + for (Column column : table.getColumns()) { + if (column.isPK()) { + relations.addPrimaryKey(pkName, tableDefinition.getColumn(column.getName())); + } + } + } + } + }); + } + + + private String getTablePkName(Table table) { + Property pkAdditionalProperty = VertabeloXMLDatabase.findAdditionalProperty(PK_ADDITIONAL_PROPERTY_NAME, + table.getProperties()); + String pkName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(pkAdditionalProperty); + if (StringUtils.isEmpty(pkName)) { + pkName = table.getName().toUpperCase() + "_PK"; + } + return pkName; + } + + + @Override + protected void loadUniqueKeys(final DefaultRelations relations) throws SQLException { + + filterTablesBySchema(databaseModel().getTables(), new TableOperation() { + @Override + public void invoke(Table table, String schemaName) { + SchemaDefinition schema = getSchema(schemaName); + TableDefinition tableDefinition = getTable(schema, table.getName()); + + if (tableDefinition != null) { + // iterate through all UNIQUE keys for this table + for (AlternateKey alternateKey : table.getAlternateKeys()) { + + // iterate through all columns of this key + for (AlternateKeyColumn alternateKeyColumn : alternateKey.getColumns()) { + Column column = (Column) alternateKeyColumn.getColumn(); + relations.addUniqueKey(alternateKey.getName(), tableDefinition.getColumn(column.getName())); + } + + } + } + } + }); + + } + + + @Override + protected void loadForeignKeys(final DefaultRelations relations) throws SQLException { + + for (final Reference reference : databaseModel().getReferences()) { + final Table pkTable = (Table) reference.getPKTable(); + final Table fkTable = (Table) reference.getFKTable(); + + filterTablesBySchema(Arrays.asList(pkTable), new TableOperation() { + @Override + public void invoke(Table table, String schemaName) { + SchemaDefinition schema = getSchema(schemaName); + TableDefinition pkTableDefinition = getTable(schema, pkTable.getName()); + TableDefinition fkTableDefinition = getTable(schema, fkTable.getName()); + + // we need to find unique key among PK and all alternate keys... + String uniqueKeyName = findUniqueConstraintNameForReference(reference); + if(uniqueKeyName == null) { + // no matching key - ignore this foreign key + return; + } + + for (ReferenceColumn referenceColumn : reference.getReferenceColumns()) { + Column fkColumn = (Column) referenceColumn.getFKColumn(); + ColumnDefinition fkColumnDefinition = fkTableDefinition.getColumn(fkColumn.getName()); + + relations.addForeignKey( + reference.getName(), + uniqueKeyName, + fkColumnDefinition, + pkTableDefinition.getSchema()); + } + } + }); + + } + } + + + private String findUniqueConstraintNameForReference(Reference reference) { + // list of referenced columns + List uniqueKeyColumns = new ArrayList(); + for (ReferenceColumn referenceColumn : reference.getReferenceColumns()) { + uniqueKeyColumns.add((Column) referenceColumn.getPKColumn()); + } + + + // list of PK columns + Table pkTable = (Table) reference.getPKTable(); + List pkColumns = new ArrayList(); + for (Column column : pkTable.getColumns()) { + if (column.isPK()) { + pkColumns.add(column); + } + } + + if (uniqueKeyColumns.equals(pkColumns)) { + // PK matches FK + log.info("Primary key constraint matches foreign key: " + reference.getName()); + return getTablePkName((Table) reference.getPKTable()); + } + + // need to check alternate keys + for (AlternateKey alternateKey : pkTable.getAlternateKeys()) { + List akColumns = new ArrayList(); + for (AlternateKeyColumn column : alternateKey.getColumns()) { + akColumns.add((Column) column.getColumn()); + } + + if (uniqueKeyColumns.equals(akColumns)) { + // AK matches FK + log.info("Alternate key constraint matches foreign key: " + reference.getName()); + return alternateKey.getName(); + } + } + + // no match + log.info("No matching unique constraint for foreign key: " + reference.getName()); + return null; + } + + + + @Override + protected void loadCheckConstraints(final DefaultRelations relations) throws SQLException { + + filterTablesBySchema(databaseModel().getTables(), new TableOperation() { + @Override + public void invoke(Table table, String schemaName) { + SchemaDefinition schema = getSchema(schemaName); + TableDefinition tableDefinition = getTable(schema, table.getName()); + + if (tableDefinition != null) { + + // iterate through all table checks + for (TableCheck tableCheck : table.getTableChecks()) { + CheckConstraintDefinition checkConstraintDefinition = new DefaultCheckConstraintDefinition( + schema, + tableDefinition, + tableCheck.getName(), + tableCheck.getCheckExpression()); + + relations.addCheckConstraint(tableDefinition, checkConstraintDefinition); + } + + // iterate through all columns and find columns with checks + for (Column column : table.getColumns()) { + if (! StringUtils.isBlank(column.getCheckExpression())) { + CheckConstraintDefinition checkConstraintDefinition = new DefaultCheckConstraintDefinition( + schema, + tableDefinition, + table.getName() + "_" + column.getName() + "_check", + column.getCheckExpression()); + + relations.addCheckConstraint(tableDefinition, checkConstraintDefinition); + } + } + } + } + }); + } + + + @Override + protected List getSchemata0() throws SQLException { + List result = new ArrayList(); + List schemaNames = new ArrayList(); + + // search in tables + for (Table table : databaseModel().getTables()) { + Property additionalProperty = findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, table.getProperties()); + addUniqueSchemaName(additionalProperty, schemaNames); + } + + // search in views + for (View view : databaseModel().getViews()) { + Property additionalProperty = findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, view.getProperties()); + addUniqueSchemaName(additionalProperty, schemaNames); + } + + + // transform + for (String schemaName : schemaNames) { + result.add(new SchemaDefinition(this, schemaName, null)); + } + + return result; + } + + + private void addUniqueSchemaName(Property additionalProperty, List schemaNames) { + String schemaName = ""; // default to empty string + if (additionalProperty != null) { + // additional property is set + schemaName = additionalProperty.getValue(); + } + + if (!schemaNames.contains(schemaName)) { + schemaNames.add(schemaName); + } + } + + + @Override + protected List getSequences0() throws SQLException { + List result = new ArrayList(); + + for (Sequence sequence : databaseModel().getSequences()) { + Property additionalProperty = VertabeloXMLDatabase.findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, + sequence.getProperties()); + String schemaName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(additionalProperty); + + if (getInputSchemata().contains(schemaName)) { + SchemaDefinition schema = getSchema(schemaName); + + DataTypeDefinition type = new DefaultDataTypeDefinition( + this, + schema, + "BIGINT" + ); + + result.add(new DefaultSequenceDefinition(schema, sequence.getName(), type)); + } + } + + return result; + } + + + @Override + protected List getTables0() throws SQLException { + final List result = new ArrayList(); + + // tables + filterTablesBySchema(databaseModel().getTables(), new TableOperation() { + @Override + public void invoke(Table table, String schemaName) { + SchemaDefinition schema = getSchema(schemaName); + result.add(new VertabeloXMLTableDefinition(schema, table)); + } + }); + + // views + filterViewsBySchema(databaseModel().getViews(), new ViewOperation() { + @Override + public void invoke(View view, String schemaName) { + SchemaDefinition schema = getSchema(schemaName); + result.add(new VertabeloXMLTableDefinition(schema, view)); + } + }); + + return result; + } + + + @Override + protected List getEnums0() { + List result = new ArrayList(); + return result; + } + + @Override + protected List getUDTs0() { + List result = new ArrayList(); + return result; + } + + @Override + protected List getArrays0() { + List result = new ArrayList(); + return result; + } + + @Override + protected List getRoutines0() { + List result = new ArrayList(); + return result; + } + + @Override + protected List getPackages0() { + List result = new ArrayList(); + return result; + } + + + protected void filterTablesBySchema(List tables, TableOperation operation) { + for (Table table : tables) { + Property schemaAdditionalProperty = VertabeloXMLDatabase.findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, + table.getProperties()); + String schemaName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(schemaAdditionalProperty); + + if (getInputSchemata().contains(schemaName)) { + + operation.invoke(table, schemaName); + + } + } + } + + + protected void filterViewsBySchema(List views, ViewOperation operation) { + for (View view : views) { + Property schemaAdditionalProperty = VertabeloXMLDatabase.findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, + view.getProperties()); + String schemaName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(schemaAdditionalProperty); + + if (getInputSchemata().contains(schemaName)) { + + operation.invoke(view, schemaName); + + } + } + } + + + public static Property findAdditionalProperty(String name, List properties) { + for (Property property : properties) { + if (property.getName().equalsIgnoreCase(name)) { + return property; + } + } + return null; + } + + public static String getAdditionalPropertyValueOrEmpty(Property additionalProperty) { + if (additionalProperty != null) { + return additionalProperty.getValue(); + } + return ""; + } +} diff --git a/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLTableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLTableDefinition.java new file mode 100644 index 0000000000..88677b3185 --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLTableDefinition.java @@ -0,0 +1,144 @@ +package org.jooq.util.vertabelo; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.jooq.DataType; +import org.jooq.impl.DefaultDataType; +import org.jooq.tools.StringUtils; +import org.jooq.util.AbstractTableDefinition; +import org.jooq.util.ColumnDefinition; +import org.jooq.util.DataTypeDefinition; +import org.jooq.util.DefaultColumnDefinition; +import org.jooq.util.DefaultDataTypeDefinition; +import org.jooq.util.SchemaDefinition; +import org.jooq.util.vertabelo.jaxb.Column; +import org.jooq.util.vertabelo.jaxb.Property; +import org.jooq.util.vertabelo.jaxb.Table; +import org.jooq.util.vertabelo.jaxb.View; +import org.jooq.util.vertabelo.jaxb.ViewColumn; + +/** + * Definition of the Vertabelo XML Table + * + * @author Michał Kołodziejski + */ +public class VertabeloXMLTableDefinition extends AbstractTableDefinition { + + protected Table table; + protected View view; + + public VertabeloXMLTableDefinition(SchemaDefinition schema, Table table) { + super(schema, table.getName(), ""); + + this.table = table; + } + + public VertabeloXMLTableDefinition(SchemaDefinition schema, View view) { + super(schema, view.getName(), ""); + + this.view = view; + } + + + @Override + protected List getElements0() throws SQLException { + if(table != null) { + // table + return getTableElements(); + + } else { + // view + return getViewElements(); + } + } + + + protected List getTableElements() { + List result = new ArrayList(); + + String schemaName = getSchemaName(); + SchemaDefinition schema = getDatabase().getSchema(schemaName); + + int position = 0; + for(Column column : table.getColumns()) { + ++position; + + // convert data type + DataType dataType = DefaultDataType.getDataType(getDialect(), column.getType()); + + DataTypeDefinition type = new DefaultDataTypeDefinition( + getDatabase(), + schema, + dataType.getTypeName(), + dataType.hasLength() ? dataType.length() : null, + dataType.hasPrecision() ? dataType.precision() : null, + dataType.hasScale() ? dataType.scale() : null, + column.isNullable(), + !StringUtils.isEmpty(column.getDefaultValue())); + + ColumnDefinition columnDefinition = new DefaultColumnDefinition( + this, + column.getName(), + position, + type, + false, + column.getDescription()); + + result.add(columnDefinition); + } + + return result; + } + + + protected List getViewElements() { + List result = new ArrayList(); + + String schemaName = getSchemaName(); + SchemaDefinition schema = getDatabase().getSchema(schemaName); + + int position = 0; + for(ViewColumn column : view.getViewColumns()) { + ++position; + + // convert data type + DataType dataType = DefaultDataType.getDataType(getDialect(), column.getType()); + + DataTypeDefinition type = new DefaultDataTypeDefinition( + getDatabase(), + schema, + dataType.getTypeName(), + dataType.hasLength() ? dataType.length() : null, + dataType.hasPrecision() ? dataType.precision() : null, + dataType.hasScale() ? dataType.scale() : null, + true, + false); + + ColumnDefinition columnDefinition = new DefaultColumnDefinition( + this, + column.getName(), + position, + type, + false, + column.getDescription()); + + result.add(columnDefinition); + } + + return result; + } + + protected String getSchemaName() { + Property additionalProperty; + + if(table != null) { + additionalProperty = VertabeloXMLDatabase.findAdditionalProperty("Schema", table.getProperties()); + } else { + additionalProperty = VertabeloXMLDatabase.findAdditionalProperty("Schema", view.getProperties()); + } + + return VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(additionalProperty); + } +} diff --git a/jOOQ-meta/src/main/resources/xjb/vertabelo/binding.xjb b/jOOQ-meta/src/main/resources/xjb/vertabelo/binding.xjb new file mode 100644 index 0000000000..03fefad849 --- /dev/null +++ b/jOOQ-meta/src/main/resources/xjb/vertabelo/binding.xjb @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jOOQ-meta/src/main/resources/xsd/vertabelo-2.1.xsd b/jOOQ-meta/src/main/resources/xsd/vertabelo-2.1.xsd new file mode 100644 index 0000000000..e38cf7de9d --- /dev/null +++ b/jOOQ-meta/src/main/resources/xsd/vertabelo-2.1.xsd @@ -0,0 +1,439 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +