All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jaxdb.ddlx.Schemas Maven / Gradle / Ivy

Go to download

DDLx is a XML definition of the DDL specification. It is a vendor-agnostic, SQL compliant XSD used to create SQL Schemas in XML. The DDLx Schema utilizes the power of XML Schema Validation to provides a cohesive structured model for the creation of SQL Schemas.

There is a newer version: 0.6.0
Show newest version
/* Copyright (c) 2017 JAX-DB
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * You should have received a copy of The MIT License (MIT) along with this
 * program. If not, see .
 */

package org.jaxdb.ddlx;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.jaxdb.vendor.DBVendor;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.$Column;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.$Columns;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.$Constraints;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.$Enum;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.$Named;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.$Table;
import org.jaxdb.www.ddlx_0_4.xLygluGCXAA.Schema;
import org.jaxsb.runtime.BindingProxy;
import org.jaxsb.runtime.Bindings;
import org.libj.util.CollectionUtil;
import org.libj.util.RefDigraph;

// TODO: In addition to JAXSB Schema objects, allow JAXDB Schema objects also.
public final class Schemas {
  public static int[] drop(final Connection connection, final Schema[] schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length), schemas), true, false, false);
  }

  public static int[] drop(final Connection connection, final Schema schema, final Schema ... schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length + 1), schema, schemas), true, false, false);
  }

  public static int[] drop(final Connection connection, final Collection schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, schemas, true, false, false);
  }

  public static int[] dropBatched(final Connection connection, final Schema[] schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length), schemas), true, false, true);
  }

  public static int[] dropBatched(final Connection connection, final Schema schema, final Schema ... schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length + 1), schema, schemas), true, false, true);
  }

  public static int[] dropBatched(final Connection connection, final Collection schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, schemas, true, false, true);
  }

  public static int[] create(final Connection connection, final Schema[] schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length), schemas), false, true, false);
  }

  public static int[] create(final Connection connection, final Schema schema, final Schema ... schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length + 1), schema, schemas), false, true, false);
  }

  public static int[] create(final Connection connection, final Collection schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, schemas, false, true, false);
  }

  public static int[] createBatched(final Connection connection, final Schema[] schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length), schemas), false, true, true);
  }

  public static int[] createBatched(final Connection connection, final Schema schema, final Schema ... schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length + 1), schema, schemas), false, true, true);
  }

  public static int[] createBatched(final Connection connection, final Collection schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, schemas, false, true, true);
  }

  public static int[] recreate(final Connection connection, final Schema[] schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length), schemas), true, true, false);
  }

  public static int[] recreate(final Connection connection, final Schema schema, final Schema ... schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length + 1), schema, schemas), true, true, false);
  }

  public static int[] recreate(final Connection connection, final Collection schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, schemas, true, true, false);
  }

  public static int[] recreateBatched(final Connection connection, final Schema[] schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length), schemas), true, true, true);
  }

  public static int[] recreateBatched(final Connection connection, final Schema schema, final Schema ... schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, CollectionUtil.asCollection(new ArrayList<>(schemas.length + 1), schema, schemas), true, true, true);
  }

  public static int[] recreateBatched(final Connection connection, final Collection schemas) throws GeneratorExecutionException, SQLException {
    return exec(connection, schemas, true, true, true);
  }

  private static int[] exec(final Connection connection, final Collection schemas, final boolean drop, final boolean create, final boolean batched) throws GeneratorExecutionException, SQLException {
    if (!drop && !create)
      return null;

    final DBVendor vendor = DBVendor.valueOf(connection.getMetaData());
    Compiler.getCompiler(vendor).init(connection);
    final java.sql.Statement sqlStatement = connection.createStatement();
    final int[] counts = new int[schemas.size()];
    int i = 0;
    if (batched) {
      for (final Schema schema : schemas) {
        final LinkedHashSet statements = new Generator(new DDLxAudit(schema)).parse(vendor);
        for (final Statement statement : statements)
          if (drop && statement instanceof DropStatement || create && statement instanceof CreateStatement)
            sqlStatement.addBatch(statement.getSql());

        int count = 0;
        for (final int result : sqlStatement.executeBatch())
          count += result;

        counts[i++] = count;
      }
    }
    else {
      for (final Schema schema : schemas) {
        final LinkedHashSet statements = new Generator(new DDLxAudit(schema)).parse(vendor);
        int count = 0;
        for (final Statement statement : statements)
          if (drop && statement instanceof DropStatement || create && statement instanceof CreateStatement)
            count += sqlStatement.executeUpdate(statement.getSql());

        counts[i++] = count;
      }
    }

    return counts;
  }

  private static final Comparator<$Table> tableNameComparator = (o1, o2) -> o1 == null ? o2 == null ? 0 : 1 : o2 == null ? -1 : o1.getName$().text().compareTo(o2.getName$().text());

  private static Schema topologicalSort(final Schema schema) {
    final List<$Table> tables = new ArrayList<>(schema.getTable());
    schema.getTable().clear();
    tables.sort(tableNameComparator);
    final RefDigraph<$Table,String> digraph = new RefDigraph<>(table -> table.getName$().text().toLowerCase());
    for (final $Table table : tables) {
      digraph.add(table);
      for (final $Column column : table.getColumn())
        if (column.getForeignKey() != null)
          digraph.add(table, column.getForeignKey().getReferences$().text().toLowerCase());
    }

    if (digraph.hasCycle())
      throw new IllegalStateException("Cycle exists in relational model: " + CollectionUtil.toString(digraph.getCycle(), " -> "));

    final ListIterator<$Table> topological = digraph.getTopologicalOrder().listIterator(digraph.size());
    while (topological.hasPrevious())
      schema.getTable().add(topological.previous());

    return schema;
  }

  public static Schema flatten(final Schema schema) {
    final Schema flat = (Schema)Bindings.clone(schema);
    final Map tableNameToTable = new HashMap<>();
    // First, register the table names to be referenceable by the @extends attribute
    for (final $Table table : flat.getTable())
      tableNameToTable.put(table.getName$().text(), table);

    final Set flatTables = new HashSet<>();
    for (final $Table table : flat.getTable())
      flattenTable(table, tableNameToTable, flatTables);

    final Iterator<$Table> iterator = flat.getTable().iterator();
    while (iterator.hasNext())
      if (iterator.next().getAbstract$().text())
        iterator.remove();

    return Schemas.topologicalSort(flat);
  }

  private static $Column clone(final $Column column) {
    return column instanceof $Enum ? new $Enum(($Enum)column.clone()) {
      private static final long serialVersionUID = -8441464393229662197L;
      private final String declaringTableName = column.id() != null ? column.id() : (($Table)BindingProxy.owner(column)).getName$().text();

      @Override
      public String id() {
        return declaringTableName;
      }

      @Override
      protected $Named inherits() {
        return column;
      }
    } : column.clone();
  }

  private static void flattenTable(final $Table table, final Map tableNameToTable, final Set flatTables) {
    if (flatTables.contains(table.getName$().text()))
      return;

    flatTables.add(table.getName$().text());
    if (table.getExtends$() == null)
      return;

    final $Table superTable = tableNameToTable.get(table.getExtends$().text());
    flattenTable(superTable, tableNameToTable, flatTables);
    if (superTable.getColumn() != null) {
      if (table.getColumn() != null) {
        for (int i = 0; i < superTable.getColumn().size(); i++)
          table.getColumn().add(i, clone(superTable.getColumn().get(i)));
      }
      else {
        for (final $Column column : superTable.getColumn())
          table.addColumn(clone(column));
      }
    }

    if (superTable.getConstraints() != null) {
      final $Constraints parentConstraints = superTable.getConstraints();
      if (table.getConstraints() == null) {
        table.setConstraints(parentConstraints.clone());
      }
      else {
        if (parentConstraints.getPrimaryKey() != null)
          table.getConstraints().setPrimaryKey(parentConstraints.getPrimaryKey().clone());

        if (parentConstraints.getUnique() != null)
          for (final $Columns columns : parentConstraints.getUnique())
            table.getConstraints().addUnique(columns.clone());
      }
    }

    if (superTable.getIndexes() != null) {
      if (table.getIndexes() == null) {
        table.setIndexes(superTable.getIndexes().clone());
      }
      else {
        for (final $Table.Indexes.Index index : superTable.getIndexes().getIndex())
          table.getIndexes().addIndex(index.clone());
      }
    }
  }

  public static int[] truncate(final Connection connection, final $Table ... tables) throws SQLException {
    return truncate(connection, Arrays.asList(tables));
  }

  public static int[] truncate(final Connection connection, final Collection tables) throws SQLException {
    final Compiler compiler = Compiler.getCompiler(DBVendor.valueOf(connection.getMetaData()));
    final java.sql.Statement statement = connection.createStatement();
    for (final $Table table : tables)
      statement.addBatch(compiler.truncate(table.getName$().text()));

    return statement.executeBatch();
  }

  private Schemas() {
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy