diff --git a/h2-migration.xml b/h2-migration.xml new file mode 100644 index 000000000..c7c2b7139 --- /dev/null +++ b/h2-migration.xml @@ -0,0 +1,1273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java b/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java index c73b9bd94..e9142c670 100644 --- a/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java +++ b/src/main/java/com/avaje/ebean/config/dbplatform/DatabasePlatform.java @@ -7,6 +7,7 @@ import javax.sql.DataSource; import com.avaje.ebean.BackgroundExecutor; import com.avaje.ebean.Query; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -145,6 +146,8 @@ public class DatabasePlatform { */ protected boolean disallowBatchOnCascade; + protected PlatformDdl platformDdl; + /** * Instantiates a new database platform. */ @@ -164,6 +167,10 @@ public class DatabasePlatform { return name; } + public PlatformDdl getPlatformDdl() { + return platformDdl; + } + /** * Return true if the JDBC driver does not allow additional queries to execute * when a resultSet is being 'streamed' as is the case with findEach() etc. diff --git a/src/main/java/com/avaje/ebean/config/dbplatform/H2Platform.java b/src/main/java/com/avaje/ebean/config/dbplatform/H2Platform.java index e9df9c592..c993f6506 100644 --- a/src/main/java/com/avaje/ebean/config/dbplatform/H2Platform.java +++ b/src/main/java/com/avaje/ebean/config/dbplatform/H2Platform.java @@ -1,6 +1,7 @@ package com.avaje.ebean.config.dbplatform; import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; import javax.sql.DataSource; @@ -12,6 +13,7 @@ public class H2Platform extends DatabasePlatform { public H2Platform() { super(); this.name = "h2"; + this.platformDdl = new H2Ddl(this.dbTypeMap); this.dbEncrypt = new H2DbEncrypt(); // like ? escape'' not working in the latest version H2 so just using no // escape clause for now noting that backslash is an escape char for like in H2 diff --git a/src/main/java/com/avaje/ebean/config/dbplatform/PostgresPlatform.java b/src/main/java/com/avaje/ebean/config/dbplatform/PostgresPlatform.java index 15b7b4ecf..87bb951d5 100644 --- a/src/main/java/com/avaje/ebean/config/dbplatform/PostgresPlatform.java +++ b/src/main/java/com/avaje/ebean/config/dbplatform/PostgresPlatform.java @@ -1,6 +1,8 @@ package com.avaje.ebean.config.dbplatform; import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; import javax.sql.DataSource; import java.sql.Types; @@ -16,6 +18,8 @@ public class PostgresPlatform extends DatabasePlatform { public PostgresPlatform() { super(); this.name = "postgres"; + this.platformDdl = new PostgresDdl(this.dbTypeMap); + // OnQueryOnly.CLOSE as a performance optimisation on Postgres this.onQueryOnly = OnQueryOnly.CLOSE; this.likeClause = "like ? escape''"; diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java new file mode 100644 index 000000000..d627c5458 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.ddlgeneration.platform.BaseColumnDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.BaseTableDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.io.IOException; +import java.util.List; + +/** + * + */ +public class BaseDdlHandler implements DdlHandler { + + protected final ColumnDdl columnDdl; + + protected final TableDdl tableDdl; + + public BaseDdlHandler(DdlNamingConvention namingConvention, PlatformDdl platformDdl) { + this.tableDdl = new BaseTableDdl(namingConvention, platformDdl); + this.columnDdl = new BaseColumnDdl(); + } + + @Override + public void generate(DdlWrite writer, ChangeSet changeSet) throws IOException { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + generate(writer, (CreateTable) change); + } else if (change instanceof AddColumn) { + generate(writer, (AddColumn) change); + } else if (change instanceof DropColumn) { + generate(writer, (DropColumn) change); + } + } + } + + @Override + public void generate(DdlWrite writer, CreateTable createTable) throws IOException { + tableDdl.generate(writer, createTable); + } + + @Override + public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { + columnDdl.generate(writer, addColumn); + } + + @Override + public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException { + columnDdl.generate(writer, dropColumn); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java new file mode 100644 index 000000000..00192f92a --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java @@ -0,0 +1,23 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.io.IOException; + +/** + * Write AddColumn or DropColumn. + */ +public interface ColumnDdl { + + /** + * Write a AddColumn change. + */ + void generate(DdlWrite writer, AddColumn addColumn) throws IOException; + + /** + * Write a DropColumn change. + */ + void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlBuffer.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlBuffer.java new file mode 100644 index 000000000..345f11a24 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlBuffer.java @@ -0,0 +1,52 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.model.MConfiguration; + +import java.io.IOException; + +/** + * Buffer to append generated DDL to. + */ +public interface DdlBuffer { + + /** + * Return the configuration (default tablespaces etc). + */ + MConfiguration getConfiguration(); + + /** + * Append DDL content to the buffer. + */ + DdlBuffer append(String content) throws IOException; + + /** + * Append DDL content to the buffer with space padding. + */ + DdlBuffer append(String type, int space) throws IOException; + + /** + * Append a value that is potentially null or empty and proceed it with a space if so. + */ + DdlBuffer appendWithSpace(String foreignKeyRestrict) throws IOException; + + /** + * Append new line character to the buffer. + */ + DdlBuffer newLine() throws IOException; + + /** + * Append the end of statement content. + */ + DdlBuffer endOfStatement() throws IOException; + + /** + * End of a change - add some whitespace. + */ + DdlBuffer end() throws IOException; + + /** + * Return the buffer content. + */ + String getBuffer(); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java new file mode 100644 index 000000000..715657724 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java @@ -0,0 +1,21 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.io.IOException; + +/** + */ +public interface DdlHandler { + + void generate(DdlWrite writer, ChangeSet changeSet) throws IOException; + + void generate(DdlWrite writer, CreateTable createTable) throws IOException; + + void generate(DdlWrite writer, AddColumn addColumn) throws IOException; + + void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java new file mode 100644 index 000000000..7101d5d0d --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java @@ -0,0 +1,97 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.ddlgeneration.platform.BaseDdlBuffer; +import com.avaje.ebean.dbmigration.model.MConfiguration; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * Write context holding the buffers for both apply and rollback DDL. + */ +public class DdlWrite { + + private final ModelContainer currentModel; + + private final DdlBuffer apply; + + private final DdlBuffer applyForeignKeys; + + private final DdlBuffer applyHistory; + + private final DdlBuffer rollbackForeignKeys; + + private final DdlBuffer rollbackLast; + + /** + * Create without any configuration or current model (no history support). + */ + public DdlWrite() { + this(new MConfiguration(), new ModelContainer()); + } + + /** + * Create with a configuration. + */ + public DdlWrite(MConfiguration configuration, ModelContainer currentModel) { + this.currentModel = currentModel; + this.apply = new BaseDdlBuffer(configuration); + this.applyForeignKeys = new BaseDdlBuffer(configuration); + this.applyHistory = new BaseDdlBuffer(configuration); + this.rollbackForeignKeys = new BaseDdlBuffer(configuration); + this.rollbackLast = new BaseDdlBuffer(configuration); + } + + /** + * Return the Table information from the current model. + * + * This is typically required for the history support (used to determine the list of columns + * included in the history when creating or recreating the associated trigger/stored procedure). + * + */ + public MTable getTable(String tableName) { + return currentModel.getTable(tableName); + } + + /** + * Return the buffer that APPLY DDL is written to. + */ + public DdlBuffer apply() { + return apply; + } + + /** + * Return the buffer that APPLY DDL is written to for foreign keys and their associated indexes. + * + * Statements added to this buffer are executed after all the normal apply statements and + * typically 'add foreign key' is added to this buffer. + */ + public DdlBuffer applyForeignKeys() { + return applyForeignKeys; + } + + /** + * Return the buffer that apply history DDL is written to. + */ + public DdlBuffer applyHistory() { + return applyHistory; + } + + /** + * Return the buffer that ROLLBACK DDL is written to for foreign keys and associated indexes. + */ + public DdlBuffer rollbackForeignKeys() { + return rollbackForeignKeys; + } + + /** + * Return the buffer that ROLLBACK DDL is written to (typically drop tables). + * + * Statements added to this rollback buffer are executed after foreign key rollback + * has been executed. + */ + public DdlBuffer rollback() { + return rollbackLast; + } + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/TableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/TableDdl.java new file mode 100644 index 000000000..f4b9153c7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/TableDdl.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.CreateTable; + +import java.io.IOException; + +/** + * Write table DDL. + */ +public interface TableDdl { + + /** + * Generate the create table DDL. + */ + void generate(DdlWrite writer, CreateTable createTable) throws IOException; +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java new file mode 100644 index 000000000..e92f2593c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java @@ -0,0 +1,69 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.ColumnDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.io.IOException; +import java.util.List; + +/** + */ +public class BaseColumnDdl implements ColumnDdl { + + + @Override + public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { + + String tableName = addColumn.getTableName(); + List columns = addColumn.getColumn(); + for (Column column : columns) { + // apply + alterTableAddColumn(writer.apply(), tableName, column); + + // rollback + alterTableDropColumn(writer.rollback(), tableName, column.getName()); + } + } + + @Override + public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException { + + String tableName = dropColumn.getTableName(); + + alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName()); + + // no good rollback option here, it is best if drop columns + // are put into a separate changeSet that is run last + } + + protected void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException { + + buffer.append("alter table ").append(tableName) + .append(" drop column ").append(columnName) + .endOfStatement().end(); + } + + protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column) throws IOException { + + buffer.append("alter table ").append(tableName) + .append(" add column ").append(column.getName()) + .append(" ").append(column.getType()); + + if (Boolean.TRUE.equals(column.isNotnull())) { + buffer.append(" not null"); + } + if (hasValue(column.getCheckConstraint())) { + buffer.append(" ").append(column.getCheckConstraint()); + } + buffer.endOfStatement().end(); + } + + + protected boolean hasValue(String value) { + return value != null && !value.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java new file mode 100644 index 000000000..9df76aaef --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java @@ -0,0 +1,84 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.model.MConfiguration; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + +/** + * Base implementation of DdlBuffer using an underlying writer. + */ +public class BaseDdlBuffer implements DdlBuffer { + + protected final Writer writer; + + protected final MConfiguration configuration; + + public BaseDdlBuffer(MConfiguration configuration) { + this.configuration = configuration; + this.writer = new StringWriter(); + } + + @Override + public MConfiguration getConfiguration() { + return configuration; + } + + @Override + public DdlBuffer appendWithSpace(String foreignKeyRestrict) throws IOException { + if (foreignKeyRestrict != null && !foreignKeyRestrict.isEmpty()) { + writer.append(" ").append(foreignKeyRestrict); + } + return this; + } + + @Override + public DdlBuffer append(String content) throws IOException { + writer.append(content); + return this; + } + + @Override + public DdlBuffer append(String content, int space) throws IOException { + writer.append(content); + appendSpace(space, content); + return this; + } + + protected void appendSpace(int max, String content) throws IOException { + int space = max - content.length(); + if (space > 0) { + for (int i = 0; i < space; i++) { + append(" "); + } + } + } + + @Override + public DdlBuffer endOfStatement() throws IOException { + writer.append(";\n"); + return this; + } + + /** + * Used to demarcate the end of a series of statements. + * This should be just whitespace or a sql comment. + */ + @Override + public DdlBuffer end() throws IOException { + writer.append("\n"); + return this; + } + + @Override + public DdlBuffer newLine() throws IOException { + writer.append("\n"); + return this; + } + + public String getBuffer() { + return writer.toString(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java new file mode 100644 index 000000000..d03923b76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -0,0 +1,356 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.TableDdl; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Base implementation for 'create table' and 'alter table' statements. + */ +public class BaseTableDdl implements TableDdl { + + protected final DdlNamingConvention namingConvention; + + protected final PlatformDdl platformDdl; + + public BaseTableDdl(DdlNamingConvention namingConvention, PlatformDdl platformDdl) { + this.namingConvention = namingConvention; + this.platformDdl = platformDdl; + } + + /** + * Generate the appropriate 'create table' and matching 'drop table' statements + * and add them to the 'apply' and 'rollback' buffers. + */ + @Override + public void generate(DdlWrite writer, CreateTable createTable) throws IOException { + + String tableName = createTable.getName(); + List columns = createTable.getColumn(); + List pk = determinePrimaryKeyColumns(columns); + + DdlBuffer apply = writer.apply(); + apply.append("create table ").append(tableName).append(" ("); + for (int i = 0; i < columns.size(); i++) { + apply.newLine(); + writeColumnDefinition(apply, columns.get(i)); + if (i < columns.size() - 1) { + apply.append(","); + } + } + + writeCheckConstraints(apply, createTable); + writeUniqueConstraints(apply, createTable); + writeCompoundUniqueConstraints(apply, createTable); + if (!pk.isEmpty()) { + writePrimaryKeyConstraint(apply, tableName, pk); + } + + apply.newLine().append(")").endOfStatement(); + apply.end(); + + writeAddForeignKeys(writer, createTable); + + // add drop table to the rollback buffer + dropTable(writer.rollback(), tableName); + + if (isTrue(createTable.isWithHistory())) { + createWithHistory(writer, createTable.getName()); + } + + } + + private void createWithHistory(DdlWrite writer, String name) throws IOException { + + MTable table = writer.getTable(name); + platformDdl.createWithHistory(writer, table); + } + + protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException { + + List columns = createTable.getColumn(); + for (Column column : columns) { + String references = column.getReferences(); + if (hasValue(references)) { + writeForeignKey(write, createTable.getName(), column.getName(), references); + } + } + //createTable. + + } + + protected void writeForeignKey(DdlWrite write, String tableName, String columnName, String references) throws IOException { + + String fkName = determineForeignKeyConstraintName(tableName, columnName); + + int pos = references.lastIndexOf('.'); + if (pos == -1) { + throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]"); + } + String refTableName = references.substring(0, pos); + String refColumnName = references.substring(pos + 1); + + String[] cols = {columnName}; + String[] refCols = {refColumnName}; + + writeForeignKey(write, fkName, tableName, cols, refTableName, refCols); + } + + protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns) throws IOException { + + DdlBuffer fkeyBuffer = write.applyForeignKeys(); + fkeyBuffer + .append("alter table ").append(tableName) + .append(" add constraint ").append(fkName) + .append(" foreign key ("); + appendColumns(columns, fkeyBuffer); + fkeyBuffer + .append(") references ") + .append(refTable); + appendColumns(refColumns, fkeyBuffer); + fkeyBuffer.appendWithSpace(platformDdl.getForeignKeyRestrict()) + .endOfStatement(); + + String indexName = determineForeignKeyIndexName(tableName, columns); + + fkeyBuffer.append("create index ").append(indexName).append(" on ").append(tableName); + appendColumns(columns, fkeyBuffer); + fkeyBuffer.endOfStatement(); + + fkeyBuffer.end(); + + write.rollbackForeignKeys() + .append("drop index ").append(indexName) + .endOfStatement(); + + write.rollbackForeignKeys() + .append("alter table ").append(tableName).append(" drop constraint ").append(fkName) + .endOfStatement(); + + write.rollbackForeignKeys().end(); + + } + + private void appendColumns(String[] columns, DdlBuffer buffer) throws IOException { + buffer.append(" ("); + for (int i = 0; i 0) { + buffer.append(","); + } + buffer.append(columns[i]); + } + buffer.append(")"); + } + + + /** + * Add 'drop table' statement to the buffer. + */ + protected void dropTable(DdlBuffer buffer, String tableName) throws IOException { + + buffer.append("drop table ").append(tableName).endOfStatement().end(); + } + + /** + * Write all the check constraints. + */ + protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { + + List columns = createTable.getColumn(); + for (Column column : columns) { + String checkConstraint = column.getCheckConstraint(); + if (hasValue(checkConstraint)) { + writeCheckConstraint(apply, createTable.getName(), column, checkConstraint); + } + } + } + + /** + * Write a check constraint. + */ + protected void writeCheckConstraint(DdlBuffer buffer, String tableName, Column column, String checkConstraint) throws IOException { + + String ckName = determineCheckConstraintName(tableName, column.getName()); + + buffer.append(",").newLine(); + buffer.append(" constraint ").append(ckName); + buffer.append(" ").append(checkConstraint); + } + + protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) { + //TODO: Write compound unique constraints + } + + /** + * Write the unique constraints inline with the create table statement. + */ + protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { + + List columns = createTable.getColumn(); + for (Column column : columns) { + if (isTrue(column.isUnique())) { + inlineUniqueConstraintSingle(apply, createTable.getName(), column); + } + } + } + + /** + * Write the unique constraint inline with the create table statement. + */ + protected void inlineUniqueConstraintSingle(DdlBuffer buffer, String tableName, Column column) throws IOException { + + String uqName = determineUniqueConstraintName(tableName, column.getName()); + + buffer.append(",").newLine(); + buffer.append(" constraint ").append(uqName).append(" unique "); + buffer.append("("); + buffer.append(column.getName()); + buffer.append(")"); + } + + /** + * Write the primary key constraint inline with the create table statement. + */ + protected void writePrimaryKeyConstraint(DdlBuffer buffer, String tableName, List pk) throws IOException { + + String pkName = determinePrimaryKeyName(tableName, pk); + + buffer.append(",").newLine(); + buffer.append(" constraint ").append(pkName).append(" primary key "); + buffer.append("("); + for (int i = 0; i < pk.size(); i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(pk.get(i).getName()); + } + buffer.append(")"); + } + + /** + * Write alter table add primary key statement. + */ + public void alterTableAddPrimaryKey(DdlBuffer buffer, String tableName, List pk) throws IOException { + + String pkName = determinePrimaryKeyName(tableName, pk); + + buffer.append("alter table ").append(tableName); + buffer.append(" add primary key ").append(pkName).append(" ("); + for (int i = 0; i < pk.size(); i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(pk.get(i).getName()); + } + buffer.append(")").endOfStatement(); + } + + /** + * Write the column definition to the create table statement. + */ + protected void writeColumnDefinition(DdlBuffer buffer, Column column) throws IOException { + + String platformType = convertToPlatformType(column.getType(), isTrue(column.isIdentity())); + + buffer.append(" "); + buffer.append(column.getName(), 30); + buffer.append(platformType); + if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) { + buffer.append(" not null"); + } + + // add check constraints later as we really want to give them a nice name + // so that the database can potentially provide a nice SQL error + } + + /** + * Convert the expected logical type into a platform specific one. + * + * For example clob -> text for postgres. + * + */ + protected String convertToPlatformType(String type, boolean identity) { + return platformDdl.convert(type, identity); + } + + /** + * Return the primary key constraint name. + */ + protected String determinePrimaryKeyName(String tableName, List pkColumns) { + + // collect the primary key column names + List pkColumnNames = new ArrayList(pkColumns.size()); + for (int i = 0; i < pkColumns.size(); i++) { + pkColumnNames.add(pkColumns.get(i).getName()); + } + return namingConvention.primaryKeyName(tableName, pkColumnNames); + } + + /** + * Return the foreign key constraint name given a single column foreign key. + */ + protected String determineForeignKeyConstraintName(String tableName, String columnName) { + + return namingConvention.foreignKeyConstraintName(tableName, columnName); + } + + /** + * Return the foreign key constraint name given a single column foreign key. + */ + protected String determineForeignKeyIndexName(String tableName, String[] columns) { + + return namingConvention.foreignKeyIndexName(tableName, columns); + } + + /** + * Return the unique constraint name. + */ + protected String determineUniqueConstraintName(String tableName, String columnName) { + + return namingConvention.uniqueConstraintName(tableName, columnName); + } + + /** + * Return the constraint name. + */ + protected String determineCheckConstraintName(String tableName, String columnName) { + + return namingConvention.checkConstraintName(tableName, columnName); + } + + /** + * Return the list of columns that make the primary key. + */ + protected List determinePrimaryKeyColumns(List columns) { + List pk = new ArrayList(3); + for (Column column : columns) { + if (isTrue(column.isPrimaryKey())) { + pk.add(column); + } + } + return pk; + } + + /** + * Return true if null or trimmed string is empty. + */ + protected boolean hasValue(String value) { + return value != null && !value.trim().isEmpty(); + } + + /** + * Null safe Boolean true test. + */ + protected boolean isTrue(Boolean value) { + return Boolean.TRUE.equals(value); + } + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormalise.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormalise.java new file mode 100644 index 000000000..05571c045 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormalise.java @@ -0,0 +1,42 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +/** + * Used to normalise table and column names which means stripping out + * quoted identifier characters and any catalog or schema prefix. + */ +public class DbNameNormalise { + + protected boolean lowerCase = true; + + protected String[] quotedIdentifiers = {"\"", "'", "[", "]", "`"}; + + /** + * Normalise the table name by trimming catalog and schema and removing any + * quoted identifier characters (",',[,] etc). + */ + public String normalise(String tableName) { + + tableName = trimQuotes(tableName); + int lastPeriod = tableName.lastIndexOf('.'); + if (lastPeriod > -1) { + tableName = tableName.substring(lastPeriod + 1); + } + if (lowerCase) { + tableName = tableName.toLowerCase(); + } + return tableName; + } + + /** + * Trim off the platform quoted identifier quotes like [ ' and ". + */ + protected String trimQuotes(String tableName) { + + // remove quoted identifier characters + for (int i = 0; i < quotedIdentifiers.length; i++) { + tableName = tableName.replace(quotedIdentifiers[i], ""); + } + return tableName; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConvention.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConvention.java new file mode 100644 index 000000000..caecbb7aa --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConvention.java @@ -0,0 +1,96 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean; + +import java.util.List; + +/** + * Naming convention used for constraint names. + */ +public class DdlNamingConvention { + + protected String pkPrefix = "pk_"; + protected String pkSuffix = ""; + + protected String fkPrefix = "fk_"; + protected String fkMiddle = "_"; + protected String fkSuffix = ""; + + protected String fkIndexPrefix = "ix_"; + protected String fkIndexMiddle = "_"; + protected String fkIndexSuffix = ""; + + protected String uqPrefix = "uq_"; + protected String uqSuffix = ""; + + protected String ckPrefix = "ck_"; + protected String ckSuffix = ""; + + protected final DbNameNormalise normalise; + + public DdlNamingConvention() { + this.normalise = new DbNameNormalise(); + } + + /** + * Return the primary key constraint name. + */ + public String primaryKeyName(String tableName, List pkColumns) { + + return pkPrefix + normalise(tableName) + pkSuffix; + } + + /** + * Return the foreign key constraint name given a single column foreign key. + */ + public String foreignKeyConstraintName(String tableName, String columnName) { + return fkPrefix + normalise(tableName) + fkMiddle + normalise(columnName) + fkSuffix; + } + + /** + * Return the index name associated with a foreign key constraint given a single column foreign key. + */ + public String foreignKeyIndexName(String tableName, String[] columns) { + + String cols = columns.length == 1 ? normalise(columns[0]) : joinColumns(columns); + return fkIndexPrefix + normalise(tableName) + fkIndexMiddle + cols + fkIndexSuffix; + } + + private String joinColumns(String[] columns) { + + //TODO: Fix this to handle maximum constraint name limits + StringBuilder sb = new StringBuilder(30); + for (int i = 0; i < columns.length; i++) { + if (i > 0) { + sb.append("-"); + } + sb.append(columns[i]); + } + return sb.toString(); + } + + /** + * Return the unique constraint name. + */ + public String uniqueConstraintName(String tableName, String columnName) { + + return uqPrefix + normalise(tableName) + "_" + normalise(columnName) + uqSuffix; + } + + /** + * Return the check constraint name. + */ + public String checkConstraintName(String tableName, String columnName) { + + return ckPrefix + normalise(tableName) + "_" + normalise(columnName) + ckSuffix; + } + + /** + * Normalise the table name by trimming catalog and schema and removing any + * quoted identifier characters (",',[,] etc). + */ + protected String normalise(String tableName) { + + return normalise.normalise(tableName); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2Ddl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2Ddl.java new file mode 100644 index 000000000..fe4dd8104 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2Ddl.java @@ -0,0 +1,14 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; + +/** + * H2 platform specific DDL. + */ +public class H2Ddl extends PlatformDdl { + + public H2Ddl(DbTypeMap platformTypes) { + super(platformTypes, new H2HistoryDdl()); + this.foreignKeyRestrict = "on delete restrict on update restrict"; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdl.java new file mode 100644 index 000000000..66161e874 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdl.java @@ -0,0 +1,17 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * + */ +public class H2HistoryDdl implements PlatformHistoryDdl { + + @Override + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + // does nothing, not supported yet + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java new file mode 100644 index 000000000..0a629a2b7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * + */ +public class PlatformDdl { + + protected final PlatformHistoryDdl historyDdl; + + protected final PlatformTypeConverter typeConverter; + + protected String foreignKeyRestrict = ""; + + public PlatformDdl(DbTypeMap platformTypes, PlatformHistoryDdl historyDdl) { + this.typeConverter = new PlatformTypeConverter(platformTypes); + this.historyDdl = historyDdl; + } + + /** + * Modify and return the column definition for autoincrement or identity definition. + */ + public String asIdentityColumn(String columnDefn) { + return columnDefn; + } + + /** + * Return the foreign key on delete on update restrict clause. + */ + public String getForeignKeyRestrict() { + return foreignKeyRestrict; + } + + /** + * Convert the standard type to the platform specific type. + */ + public String convert(String type, boolean identity) { + String platformType = typeConverter.convert(type); + return identity ? asIdentityColumn(platformType) : platformType; + } + + /** + * Add history support to this table using the platform specific mechanism. + */ + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + historyDdl.createWithHistory(writer, table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java new file mode 100644 index 000000000..dfcb392c8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java @@ -0,0 +1,17 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * Defines the implementation for adding history support to a table. + */ +public interface PlatformHistoryDdl { + + /** + * Add history support to the table using platform specific mechanism. + */ + void createWithHistory(DdlWrite writer, MTable table) throws IOException; +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverter.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverter.java new file mode 100644 index 000000000..7c3362faa --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverter.java @@ -0,0 +1,84 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; + +/** + * Converts a logical column definition into platform specific one. + * + * This translates standard sql types into platform specific ones. + */ +public class PlatformTypeConverter { + + protected final DbTypeMap platformTypes; + + /** + * Construct with the platform specific types. + */ + public PlatformTypeConverter(DbTypeMap platformTypes) { + this.platformTypes = platformTypes; + } + + /** + * Convert the standard type to the platform specific type. + */ + public String convert(String columnDefinition) { + + int open = columnDefinition.indexOf('('); + if (open > -1) { + // no scale or precision + return convertWithScale(columnDefinition, open); + } else { + return convertNoScale(columnDefinition); + } + } + + /** + * Convert a type that has scale and possibly precision. + */ + protected String convertWithScale(String columnDefinition, int open) { + + int close = columnDefinition.lastIndexOf(')'); + if (close == -1) { + // assume already platform specific, leave as is + return columnDefinition; + } + + String type = columnDefinition.substring(0,open); + try { + DbType dbType = platformTypes.lookup(type); + int comma = columnDefinition.indexOf(',',open); + if (comma > -1) { + // scale and precision - decimal(10,4) + int scale = Integer.parseInt(columnDefinition.substring(open+1, comma)); + int precision = Integer.parseInt(columnDefinition.substring(comma+1, close)); + return dbType.renderType(scale,precision); + + } else { + // scale - varchar(10) + int scale = Integer.parseInt(columnDefinition.substring(open+1, close)); + return dbType.renderType(scale,0); + } + + } catch (IllegalArgumentException e) { + // assume already platform specific, leave as is + return columnDefinition; + } + } + + /** + * Convert a simple type with not scale or precision. + */ + protected String convertNoScale(String columnDefinition) { + + try { + DbType dbType = platformTypes.lookup(columnDefinition); + return dbType.renderType(0,0); + + } catch (IllegalArgumentException e) { + // assume already platform specific, leave as is + return columnDefinition; + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java new file mode 100644 index 000000000..350af181e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java @@ -0,0 +1,31 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; + +/** + * Postgres specific DDL. + */ +public class PostgresDdl extends PlatformDdl { + + public PostgresDdl(DbTypeMap platformTypes) { + super(platformTypes, new PostgresHistoryDdl()); + this.foreignKeyRestrict = "on delete restrict on update restrict"; + } + + /** + * Map bigint, integer and smallint into their equivalent serial types. + */ + public String asIdentityColumn(String columnDefn) { + + if ("bigint".equalsIgnoreCase(columnDefn)) { + return "bigserial"; + } + if ("integer".equalsIgnoreCase(columnDefn)) { + return "serial"; + } + if ("smallint".equalsIgnoreCase(columnDefn)) { + return "smallserial"; + } + return columnDefn; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java new file mode 100644 index 000000000..f18efcf9e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java @@ -0,0 +1,135 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; +import java.util.Collection; + +/** + * + */ +public class PostgresHistoryDdl implements PlatformHistoryDdl { + + DbNameNormalise normalise = new DbNameNormalise(); + + protected String historyTableName(String baseTableName) { + return baseTableName + "_history"; + } + + protected String procedureName(String baseTableName) { + return baseTableName + "_history_version"; + } + + protected String triggerName(String baseTableName) { + return baseTableName + "_history_upd"; + } + + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + + // naming convention + + addHistoryTable(writer, table); + addStoredFunction(writer, table); + addTrigger(writer, table); + } + + public void addHistoryTable(DdlWrite writer, MTable table) throws IOException { + + String baseTableName = this.normalise.normalise(table.getName()); + + DdlBuffer buffer = writer.applyHistory(); + + buffer + .append("alter table ").append(baseTableName) + .append(" add column sys_period tstzrange not null") + .endOfStatement().end(); + + buffer + .append("create table ").append(baseTableName).append("_history") + .append(" (like ").append(baseTableName).append(")") + .endOfStatement().end(); + + buffer + .append("create view ").append(baseTableName).append("_with_history") + .append(" as select * from").append(baseTableName) + .append(" union all select * from").append(baseTableName).append("_history") + .endOfStatement().end(); + + } + + public void addTrigger(DdlWrite writer, MTable table) throws IOException { + + String baseTableName = this.normalise.normalise(table.getName()); + String procedureName = procedureName(baseTableName); + String triggerName = triggerName(baseTableName); + + DdlBuffer buffer = writer.applyHistory(); + buffer + .append("create trigger ").append(triggerName).newLine() + .append(" before insert or update or delete on ").append(baseTableName).newLine() + .append(" for each row execute procedure ").append(procedureName).append("();").newLine().newLine(); + + } + + + public void addStoredFunction(DdlWrite writer, MTable table) throws IOException { + + String baseTableName = this.normalise.normalise(table.getName()); + String procedureName = procedureName(baseTableName); + DdlBuffer buffer = writer.applyHistory(); + + buffer + .append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine() + .append("begin").newLine(); + buffer + .append(" if (TG_OP = 'INSERT') then").newLine() + .append(" NEW.sys_period = tstzrange(CURRENT_TIMESTAMP,null);").newLine() + .append(" return new;").newLine().newLine(); + buffer + .append(" elsif (TG_OP = 'UPDATE') then").newLine(); + appendInsertIntoHistory(buffer, table); + buffer + .append(" NEW.sys_period = tstzrange(CURRENT_TIMESTAMP,null);").newLine() + .append(" return new;").newLine().newLine(); + buffer + .append(" elsif (TG_OP = 'DELETE') then").newLine(); + appendInsertIntoHistory(buffer, table); + buffer + .append(" return old;").newLine().newLine(); + + buffer + .append(" end if;").newLine() + .append("end;").newLine() + .append("$$ LANGUAGE plpgsql;").newLine(); + + buffer.end(); + } + + protected void appendInsertIntoHistory(DdlBuffer buffer, MTable table) throws IOException { + + String historyTable = historyTableName(table.getName()); + + buffer.append(" insert into ").append(historyTable).append(" (sys_period,"); + appendColumnNames(buffer, table, ""); + buffer.append(") values (tstzrange(lower(OLD.sys_period), CURRENT_TIMESTAMP), "); + appendColumnNames(buffer, table, "OLD."); + buffer.append(");").newLine(); + } + + protected void appendColumnNames(DdlBuffer buffer, MTable table, String columnPrefix) throws IOException { + + //id, line1, line2, city, country_code, version, when_created, when_updated + Collection columns = table.getColumns().values(); + int i = 0; + for (MColumn column : columns) { + if (++i > 1) { + buffer.append(", "); + } + buffer.append(columnPrefix); + buffer.append(column.getName()); + } + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/AddColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/AddColumn.java new file mode 100644 index 000000000..91d01e3cb --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/AddColumn.java @@ -0,0 +1,99 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * </sequence> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column" +}) +@XmlRootElement(name = "addColumn") +public class AddColumn { + + @XmlElement(required = true) + protected List column; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java new file mode 100644 index 000000000..6a9aac565 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java @@ -0,0 +1,143 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="columns" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="references" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "addForeignKey") +public class AddForeignKey { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "columns", required = true) + protected String columns; + @XmlAttribute(name = "references", required = true) + protected String references; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the columns property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumns() { + return columns; + } + + /** + * Sets the value of the columns property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumns(String value) { + this.columns = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java new file mode 100644 index 000000000..31b8a130c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="resourcePath" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "application") +public class Application { + + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "resourcePath", required = true) + protected String resourcePath; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the resourcePath property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getResourcePath() { + return resourcePath; + } + + /** + * Sets the value of the resourcePath property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setResourcePath(String value) { + this.resourcePath = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java new file mode 100644 index 000000000..950b038dd --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java @@ -0,0 +1,69 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}application" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "application" +}) +@XmlRootElement(name = "applications") +public class Applications { + + protected List application; + + /** + * Gets the value of the application property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the application property. + * + * + * For example, to add a new item, do as follows: + * + * getApplication().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Application } + * + * + */ + public List getApplication() { + if (application == null) { + application = new ArrayList(); + } + return this.application; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java new file mode 100644 index 000000000..3ca1a3fca --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "apply") +public class Apply { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java new file mode 100644 index 000000000..502f7875f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java @@ -0,0 +1,160 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <choice> + * <group ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetChildren" maxOccurs="unbounded" minOccurs="0"/> + * </choice> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /> + * <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSetChildren" +}) +@XmlRootElement(name = "changeSet") +public class ChangeSet { + + @XmlElements({ + @XmlElement(name = "configuration", type = Configuration.class), + @XmlElement(name = "sql", type = Sql.class), + @XmlElement(name = "createTable", type = CreateTable.class), + @XmlElement(name = "dropTable", type = DropTable.class), + @XmlElement(name = "renameTable", type = RenameTable.class), + @XmlElement(name = "addHistoryTable", type = CreateHistoryTable.class), + @XmlElement(name = "createView", type = CreateView.class), + @XmlElement(name = "dropView", type = DropView.class), + @XmlElement(name = "renameView", type = RenameView.class), + @XmlElement(name = "addColumn", type = AddColumn.class), + @XmlElement(name = "dropColumn", type = DropColumn.class), + @XmlElement(name = "renameColumn", type = RenameColumn.class), + @XmlElement(name = "addForeignKey", type = AddForeignKey.class), + @XmlElement(name = "dropForeignKey", type = DropForeignKey.class) + }) + protected List changeSetChildren; + @XmlAttribute(name = "id", required = true) + @XmlSchemaType(name = "positiveInteger") + protected BigInteger id; + @XmlAttribute(name = "comment") + protected String comment; + + /** + * Gets the value of the changeSetChildren property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSetChildren property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSetChildren().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Configuration } + * {@link Sql } + * {@link CreateTable } + * {@link DropTable } + * {@link RenameTable } + * {@link CreateHistoryTable } + * {@link CreateView } + * {@link DropView } + * {@link RenameView } + * {@link AddColumn } + * {@link DropColumn } + * {@link RenameColumn } + * {@link AddForeignKey } + * {@link DropForeignKey } + * + * + */ + public List getChangeSetChildren() { + if (changeSetChildren == null) { + changeSetChildren = new ArrayList(); + } + return this.changeSetChildren; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link BigInteger } + * + */ + public BigInteger getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link BigInteger } + * + */ + public void setId(BigInteger value) { + this.id = value; + } + + /** + * Gets the value of the comment property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getComment() { + return comment; + } + + /** + * Sets the value of the comment property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setComment(String value) { + this.comment = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java new file mode 100644 index 000000000..5abc4568c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java @@ -0,0 +1,402 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column"/> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}columnAttributes"/> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "column") +public class Column { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "type", required = true) + protected String type; + @XmlAttribute(name = "defaultValue") + protected String defaultValue; + @XmlAttribute(name = "remarks") + protected String remarks; + @XmlAttribute(name = "notnull") + protected Boolean notnull; + @XmlAttribute(name = "checkConstraint") + protected String checkConstraint; + @XmlAttribute(name = "unique") + protected Boolean unique; + @XmlAttribute(name = "primaryKey") + protected Boolean primaryKey; + @XmlAttribute(name = "identity") + protected Boolean identity; + @XmlAttribute(name = "references") + protected String references; + @XmlAttribute(name = "deleteCascade") + protected Boolean deleteCascade; + @XmlAttribute(name = "deferrable") + protected Boolean deferrable; + @XmlAttribute(name = "initiallyDeferred") + protected Boolean initiallyDeferred; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the defaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * Sets the value of the defaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDefaultValue(String value) { + this.defaultValue = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + + /** + * Gets the value of the notnull property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNotnull() { + return notnull; + } + + /** + * Sets the value of the notnull property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNotnull(Boolean value) { + this.notnull = value; + } + + /** + * Gets the value of the checkConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCheckConstraint() { + return checkConstraint; + } + + /** + * Sets the value of the checkConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCheckConstraint(String value) { + this.checkConstraint = value; + } + + /** + * Gets the value of the unique property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUnique() { + return unique; + } + + /** + * Sets the value of the unique property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUnique(Boolean value) { + this.unique = value; + } + + /** + * Gets the value of the primaryKey property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isPrimaryKey() { + return primaryKey; + } + + /** + * Sets the value of the primaryKey property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setPrimaryKey(Boolean value) { + this.primaryKey = value; + } + + /** + * Gets the value of the identity property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isIdentity() { + return identity; + } + + /** + * Sets the value of the identity property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setIdentity(Boolean value) { + this.identity = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + + /** + * Gets the value of the deleteCascade property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeleteCascade() { + return deleteCascade; + } + + /** + * Sets the value of the deleteCascade property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeleteCascade(Boolean value) { + this.deleteCascade = value; + } + + /** + * Gets the value of the deferrable property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeferrable() { + return deferrable; + } + + /** + * Sets the value of the deferrable property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeferrable(Boolean value) { + this.deferrable = value; + } + + /** + * Gets the value of the initiallyDeferred property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isInitiallyDeferred() { + return initiallyDeferred; + } + + /** + * Sets the value of the initiallyDeferred property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setInitiallyDeferred(Boolean value) { + this.initiallyDeferred = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java new file mode 100644 index 000000000..54c2c3162 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java @@ -0,0 +1,64 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}defaultTablespace"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "defaultTablespace" +}) +@XmlRootElement(name = "configuration") +public class Configuration { + + @XmlElement(required = true) + protected DefaultTablespace defaultTablespace; + + /** + * Gets the value of the defaultTablespace property. + * + * @return + * possible object is + * {@link DefaultTablespace } + * + */ + public DefaultTablespace getDefaultTablespace() { + return defaultTablespace; + } + + /** + * Sets the value of the defaultTablespace property. + * + * @param value + * allowed object is + * {@link DefaultTablespace } + * + */ + public void setDefaultTablespace(DefaultTablespace value) { + this.defaultTablespace = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java new file mode 100644 index 000000000..c3cd8aa8f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "createDropHistoryTable", namespace = "http://ebean-orm.github.io/xml/ns/dbmigration") +public class CreateDropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java new file mode 100644 index 000000000..f3e779cff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "addHistoryTable") +public class CreateHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java new file mode 100644 index 000000000..1546a9b49 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java @@ -0,0 +1,237 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column", + "uniqueConstraint" +}) +@XmlRootElement(name = "createTable") +public class CreateTable { + + @XmlElement(required = true) + protected List column; + protected List uniqueConstraint; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "withHistory") + protected Boolean withHistory; + @XmlAttribute(name = "tablespace") + protected String tablespace; + @XmlAttribute(name = "indexTablespace") + protected String indexTablespace; + @XmlAttribute(name = "remarks") + protected String remarks; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the uniqueConstraint property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + * + * For example, to add a new item, do as follows: + * + * getUniqueConstraint().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * This is typically required for the history support (used to determine the list of columns + * included in the history when creating or recreating the associated trigger/stored procedure). + *
+ * Statements added to this buffer are executed after all the normal apply statements and + * typically 'add foreign key' is added to this buffer. + */ + public DdlBuffer applyForeignKeys() { + return applyForeignKeys; + } + + /** + * Return the buffer that apply history DDL is written to. + */ + public DdlBuffer applyHistory() { + return applyHistory; + } + + /** + * Return the buffer that ROLLBACK DDL is written to for foreign keys and associated indexes. + */ + public DdlBuffer rollbackForeignKeys() { + return rollbackForeignKeys; + } + + /** + * Return the buffer that ROLLBACK DDL is written to (typically drop tables). + *
+ * Statements added to this rollback buffer are executed after foreign key rollback + * has been executed. + */ + public DdlBuffer rollback() { + return rollbackLast; + } + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/TableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/TableDdl.java new file mode 100644 index 000000000..f4b9153c7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/TableDdl.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.CreateTable; + +import java.io.IOException; + +/** + * Write table DDL. + */ +public interface TableDdl { + + /** + * Generate the create table DDL. + */ + void generate(DdlWrite writer, CreateTable createTable) throws IOException; +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java new file mode 100644 index 000000000..e92f2593c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java @@ -0,0 +1,69 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.ColumnDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.io.IOException; +import java.util.List; + +/** + */ +public class BaseColumnDdl implements ColumnDdl { + + + @Override + public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { + + String tableName = addColumn.getTableName(); + List columns = addColumn.getColumn(); + for (Column column : columns) { + // apply + alterTableAddColumn(writer.apply(), tableName, column); + + // rollback + alterTableDropColumn(writer.rollback(), tableName, column.getName()); + } + } + + @Override + public void generate(DdlWrite writer, DropColumn dropColumn) throws IOException { + + String tableName = dropColumn.getTableName(); + + alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName()); + + // no good rollback option here, it is best if drop columns + // are put into a separate changeSet that is run last + } + + protected void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException { + + buffer.append("alter table ").append(tableName) + .append(" drop column ").append(columnName) + .endOfStatement().end(); + } + + protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column) throws IOException { + + buffer.append("alter table ").append(tableName) + .append(" add column ").append(column.getName()) + .append(" ").append(column.getType()); + + if (Boolean.TRUE.equals(column.isNotnull())) { + buffer.append(" not null"); + } + if (hasValue(column.getCheckConstraint())) { + buffer.append(" ").append(column.getCheckConstraint()); + } + buffer.endOfStatement().end(); + } + + + protected boolean hasValue(String value) { + return value != null && !value.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java new file mode 100644 index 000000000..9df76aaef --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseDdlBuffer.java @@ -0,0 +1,84 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.model.MConfiguration; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + +/** + * Base implementation of DdlBuffer using an underlying writer. + */ +public class BaseDdlBuffer implements DdlBuffer { + + protected final Writer writer; + + protected final MConfiguration configuration; + + public BaseDdlBuffer(MConfiguration configuration) { + this.configuration = configuration; + this.writer = new StringWriter(); + } + + @Override + public MConfiguration getConfiguration() { + return configuration; + } + + @Override + public DdlBuffer appendWithSpace(String foreignKeyRestrict) throws IOException { + if (foreignKeyRestrict != null && !foreignKeyRestrict.isEmpty()) { + writer.append(" ").append(foreignKeyRestrict); + } + return this; + } + + @Override + public DdlBuffer append(String content) throws IOException { + writer.append(content); + return this; + } + + @Override + public DdlBuffer append(String content, int space) throws IOException { + writer.append(content); + appendSpace(space, content); + return this; + } + + protected void appendSpace(int max, String content) throws IOException { + int space = max - content.length(); + if (space > 0) { + for (int i = 0; i < space; i++) { + append(" "); + } + } + } + + @Override + public DdlBuffer endOfStatement() throws IOException { + writer.append(";\n"); + return this; + } + + /** + * Used to demarcate the end of a series of statements. + * This should be just whitespace or a sql comment. + */ + @Override + public DdlBuffer end() throws IOException { + writer.append("\n"); + return this; + } + + @Override + public DdlBuffer newLine() throws IOException { + writer.append("\n"); + return this; + } + + public String getBuffer() { + return writer.toString(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java new file mode 100644 index 000000000..d03923b76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -0,0 +1,356 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.TableDdl; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Base implementation for 'create table' and 'alter table' statements. + */ +public class BaseTableDdl implements TableDdl { + + protected final DdlNamingConvention namingConvention; + + protected final PlatformDdl platformDdl; + + public BaseTableDdl(DdlNamingConvention namingConvention, PlatformDdl platformDdl) { + this.namingConvention = namingConvention; + this.platformDdl = platformDdl; + } + + /** + * Generate the appropriate 'create table' and matching 'drop table' statements + * and add them to the 'apply' and 'rollback' buffers. + */ + @Override + public void generate(DdlWrite writer, CreateTable createTable) throws IOException { + + String tableName = createTable.getName(); + List columns = createTable.getColumn(); + List pk = determinePrimaryKeyColumns(columns); + + DdlBuffer apply = writer.apply(); + apply.append("create table ").append(tableName).append(" ("); + for (int i = 0; i < columns.size(); i++) { + apply.newLine(); + writeColumnDefinition(apply, columns.get(i)); + if (i < columns.size() - 1) { + apply.append(","); + } + } + + writeCheckConstraints(apply, createTable); + writeUniqueConstraints(apply, createTable); + writeCompoundUniqueConstraints(apply, createTable); + if (!pk.isEmpty()) { + writePrimaryKeyConstraint(apply, tableName, pk); + } + + apply.newLine().append(")").endOfStatement(); + apply.end(); + + writeAddForeignKeys(writer, createTable); + + // add drop table to the rollback buffer + dropTable(writer.rollback(), tableName); + + if (isTrue(createTable.isWithHistory())) { + createWithHistory(writer, createTable.getName()); + } + + } + + private void createWithHistory(DdlWrite writer, String name) throws IOException { + + MTable table = writer.getTable(name); + platformDdl.createWithHistory(writer, table); + } + + protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException { + + List columns = createTable.getColumn(); + for (Column column : columns) { + String references = column.getReferences(); + if (hasValue(references)) { + writeForeignKey(write, createTable.getName(), column.getName(), references); + } + } + //createTable. + + } + + protected void writeForeignKey(DdlWrite write, String tableName, String columnName, String references) throws IOException { + + String fkName = determineForeignKeyConstraintName(tableName, columnName); + + int pos = references.lastIndexOf('.'); + if (pos == -1) { + throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]"); + } + String refTableName = references.substring(0, pos); + String refColumnName = references.substring(pos + 1); + + String[] cols = {columnName}; + String[] refCols = {refColumnName}; + + writeForeignKey(write, fkName, tableName, cols, refTableName, refCols); + } + + protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns) throws IOException { + + DdlBuffer fkeyBuffer = write.applyForeignKeys(); + fkeyBuffer + .append("alter table ").append(tableName) + .append(" add constraint ").append(fkName) + .append(" foreign key ("); + appendColumns(columns, fkeyBuffer); + fkeyBuffer + .append(") references ") + .append(refTable); + appendColumns(refColumns, fkeyBuffer); + fkeyBuffer.appendWithSpace(platformDdl.getForeignKeyRestrict()) + .endOfStatement(); + + String indexName = determineForeignKeyIndexName(tableName, columns); + + fkeyBuffer.append("create index ").append(indexName).append(" on ").append(tableName); + appendColumns(columns, fkeyBuffer); + fkeyBuffer.endOfStatement(); + + fkeyBuffer.end(); + + write.rollbackForeignKeys() + .append("drop index ").append(indexName) + .endOfStatement(); + + write.rollbackForeignKeys() + .append("alter table ").append(tableName).append(" drop constraint ").append(fkName) + .endOfStatement(); + + write.rollbackForeignKeys().end(); + + } + + private void appendColumns(String[] columns, DdlBuffer buffer) throws IOException { + buffer.append(" ("); + for (int i = 0; i 0) { + buffer.append(","); + } + buffer.append(columns[i]); + } + buffer.append(")"); + } + + + /** + * Add 'drop table' statement to the buffer. + */ + protected void dropTable(DdlBuffer buffer, String tableName) throws IOException { + + buffer.append("drop table ").append(tableName).endOfStatement().end(); + } + + /** + * Write all the check constraints. + */ + protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { + + List columns = createTable.getColumn(); + for (Column column : columns) { + String checkConstraint = column.getCheckConstraint(); + if (hasValue(checkConstraint)) { + writeCheckConstraint(apply, createTable.getName(), column, checkConstraint); + } + } + } + + /** + * Write a check constraint. + */ + protected void writeCheckConstraint(DdlBuffer buffer, String tableName, Column column, String checkConstraint) throws IOException { + + String ckName = determineCheckConstraintName(tableName, column.getName()); + + buffer.append(",").newLine(); + buffer.append(" constraint ").append(ckName); + buffer.append(" ").append(checkConstraint); + } + + protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) { + //TODO: Write compound unique constraints + } + + /** + * Write the unique constraints inline with the create table statement. + */ + protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { + + List columns = createTable.getColumn(); + for (Column column : columns) { + if (isTrue(column.isUnique())) { + inlineUniqueConstraintSingle(apply, createTable.getName(), column); + } + } + } + + /** + * Write the unique constraint inline with the create table statement. + */ + protected void inlineUniqueConstraintSingle(DdlBuffer buffer, String tableName, Column column) throws IOException { + + String uqName = determineUniqueConstraintName(tableName, column.getName()); + + buffer.append(",").newLine(); + buffer.append(" constraint ").append(uqName).append(" unique "); + buffer.append("("); + buffer.append(column.getName()); + buffer.append(")"); + } + + /** + * Write the primary key constraint inline with the create table statement. + */ + protected void writePrimaryKeyConstraint(DdlBuffer buffer, String tableName, List pk) throws IOException { + + String pkName = determinePrimaryKeyName(tableName, pk); + + buffer.append(",").newLine(); + buffer.append(" constraint ").append(pkName).append(" primary key "); + buffer.append("("); + for (int i = 0; i < pk.size(); i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(pk.get(i).getName()); + } + buffer.append(")"); + } + + /** + * Write alter table add primary key statement. + */ + public void alterTableAddPrimaryKey(DdlBuffer buffer, String tableName, List pk) throws IOException { + + String pkName = determinePrimaryKeyName(tableName, pk); + + buffer.append("alter table ").append(tableName); + buffer.append(" add primary key ").append(pkName).append(" ("); + for (int i = 0; i < pk.size(); i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(pk.get(i).getName()); + } + buffer.append(")").endOfStatement(); + } + + /** + * Write the column definition to the create table statement. + */ + protected void writeColumnDefinition(DdlBuffer buffer, Column column) throws IOException { + + String platformType = convertToPlatformType(column.getType(), isTrue(column.isIdentity())); + + buffer.append(" "); + buffer.append(column.getName(), 30); + buffer.append(platformType); + if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) { + buffer.append(" not null"); + } + + // add check constraints later as we really want to give them a nice name + // so that the database can potentially provide a nice SQL error + } + + /** + * Convert the expected logical type into a platform specific one. + * + * For example clob -> text for postgres. + * + */ + protected String convertToPlatformType(String type, boolean identity) { + return platformDdl.convert(type, identity); + } + + /** + * Return the primary key constraint name. + */ + protected String determinePrimaryKeyName(String tableName, List pkColumns) { + + // collect the primary key column names + List pkColumnNames = new ArrayList(pkColumns.size()); + for (int i = 0; i < pkColumns.size(); i++) { + pkColumnNames.add(pkColumns.get(i).getName()); + } + return namingConvention.primaryKeyName(tableName, pkColumnNames); + } + + /** + * Return the foreign key constraint name given a single column foreign key. + */ + protected String determineForeignKeyConstraintName(String tableName, String columnName) { + + return namingConvention.foreignKeyConstraintName(tableName, columnName); + } + + /** + * Return the foreign key constraint name given a single column foreign key. + */ + protected String determineForeignKeyIndexName(String tableName, String[] columns) { + + return namingConvention.foreignKeyIndexName(tableName, columns); + } + + /** + * Return the unique constraint name. + */ + protected String determineUniqueConstraintName(String tableName, String columnName) { + + return namingConvention.uniqueConstraintName(tableName, columnName); + } + + /** + * Return the constraint name. + */ + protected String determineCheckConstraintName(String tableName, String columnName) { + + return namingConvention.checkConstraintName(tableName, columnName); + } + + /** + * Return the list of columns that make the primary key. + */ + protected List determinePrimaryKeyColumns(List columns) { + List pk = new ArrayList(3); + for (Column column : columns) { + if (isTrue(column.isPrimaryKey())) { + pk.add(column); + } + } + return pk; + } + + /** + * Return true if null or trimmed string is empty. + */ + protected boolean hasValue(String value) { + return value != null && !value.trim().isEmpty(); + } + + /** + * Null safe Boolean true test. + */ + protected boolean isTrue(Boolean value) { + return Boolean.TRUE.equals(value); + } + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormalise.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormalise.java new file mode 100644 index 000000000..05571c045 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormalise.java @@ -0,0 +1,42 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +/** + * Used to normalise table and column names which means stripping out + * quoted identifier characters and any catalog or schema prefix. + */ +public class DbNameNormalise { + + protected boolean lowerCase = true; + + protected String[] quotedIdentifiers = {"\"", "'", "[", "]", "`"}; + + /** + * Normalise the table name by trimming catalog and schema and removing any + * quoted identifier characters (",',[,] etc). + */ + public String normalise(String tableName) { + + tableName = trimQuotes(tableName); + int lastPeriod = tableName.lastIndexOf('.'); + if (lastPeriod > -1) { + tableName = tableName.substring(lastPeriod + 1); + } + if (lowerCase) { + tableName = tableName.toLowerCase(); + } + return tableName; + } + + /** + * Trim off the platform quoted identifier quotes like [ ' and ". + */ + protected String trimQuotes(String tableName) { + + // remove quoted identifier characters + for (int i = 0; i < quotedIdentifiers.length; i++) { + tableName = tableName.replace(quotedIdentifiers[i], ""); + } + return tableName; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConvention.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConvention.java new file mode 100644 index 000000000..caecbb7aa --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConvention.java @@ -0,0 +1,96 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean; + +import java.util.List; + +/** + * Naming convention used for constraint names. + */ +public class DdlNamingConvention { + + protected String pkPrefix = "pk_"; + protected String pkSuffix = ""; + + protected String fkPrefix = "fk_"; + protected String fkMiddle = "_"; + protected String fkSuffix = ""; + + protected String fkIndexPrefix = "ix_"; + protected String fkIndexMiddle = "_"; + protected String fkIndexSuffix = ""; + + protected String uqPrefix = "uq_"; + protected String uqSuffix = ""; + + protected String ckPrefix = "ck_"; + protected String ckSuffix = ""; + + protected final DbNameNormalise normalise; + + public DdlNamingConvention() { + this.normalise = new DbNameNormalise(); + } + + /** + * Return the primary key constraint name. + */ + public String primaryKeyName(String tableName, List pkColumns) { + + return pkPrefix + normalise(tableName) + pkSuffix; + } + + /** + * Return the foreign key constraint name given a single column foreign key. + */ + public String foreignKeyConstraintName(String tableName, String columnName) { + return fkPrefix + normalise(tableName) + fkMiddle + normalise(columnName) + fkSuffix; + } + + /** + * Return the index name associated with a foreign key constraint given a single column foreign key. + */ + public String foreignKeyIndexName(String tableName, String[] columns) { + + String cols = columns.length == 1 ? normalise(columns[0]) : joinColumns(columns); + return fkIndexPrefix + normalise(tableName) + fkIndexMiddle + cols + fkIndexSuffix; + } + + private String joinColumns(String[] columns) { + + //TODO: Fix this to handle maximum constraint name limits + StringBuilder sb = new StringBuilder(30); + for (int i = 0; i < columns.length; i++) { + if (i > 0) { + sb.append("-"); + } + sb.append(columns[i]); + } + return sb.toString(); + } + + /** + * Return the unique constraint name. + */ + public String uniqueConstraintName(String tableName, String columnName) { + + return uqPrefix + normalise(tableName) + "_" + normalise(columnName) + uqSuffix; + } + + /** + * Return the check constraint name. + */ + public String checkConstraintName(String tableName, String columnName) { + + return ckPrefix + normalise(tableName) + "_" + normalise(columnName) + ckSuffix; + } + + /** + * Normalise the table name by trimming catalog and schema and removing any + * quoted identifier characters (",',[,] etc). + */ + protected String normalise(String tableName) { + + return normalise.normalise(tableName); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2Ddl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2Ddl.java new file mode 100644 index 000000000..fe4dd8104 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2Ddl.java @@ -0,0 +1,14 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; + +/** + * H2 platform specific DDL. + */ +public class H2Ddl extends PlatformDdl { + + public H2Ddl(DbTypeMap platformTypes) { + super(platformTypes, new H2HistoryDdl()); + this.foreignKeyRestrict = "on delete restrict on update restrict"; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdl.java new file mode 100644 index 000000000..66161e874 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdl.java @@ -0,0 +1,17 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * + */ +public class H2HistoryDdl implements PlatformHistoryDdl { + + @Override + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + // does nothing, not supported yet + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java new file mode 100644 index 000000000..0a629a2b7 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * + */ +public class PlatformDdl { + + protected final PlatformHistoryDdl historyDdl; + + protected final PlatformTypeConverter typeConverter; + + protected String foreignKeyRestrict = ""; + + public PlatformDdl(DbTypeMap platformTypes, PlatformHistoryDdl historyDdl) { + this.typeConverter = new PlatformTypeConverter(platformTypes); + this.historyDdl = historyDdl; + } + + /** + * Modify and return the column definition for autoincrement or identity definition. + */ + public String asIdentityColumn(String columnDefn) { + return columnDefn; + } + + /** + * Return the foreign key on delete on update restrict clause. + */ + public String getForeignKeyRestrict() { + return foreignKeyRestrict; + } + + /** + * Convert the standard type to the platform specific type. + */ + public String convert(String type, boolean identity) { + String platformType = typeConverter.convert(type); + return identity ? asIdentityColumn(platformType) : platformType; + } + + /** + * Add history support to this table using the platform specific mechanism. + */ + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + historyDdl.createWithHistory(writer, table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java new file mode 100644 index 000000000..dfcb392c8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformHistoryDdl.java @@ -0,0 +1,17 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; + +/** + * Defines the implementation for adding history support to a table. + */ +public interface PlatformHistoryDdl { + + /** + * Add history support to the table using platform specific mechanism. + */ + void createWithHistory(DdlWrite writer, MTable table) throws IOException; +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverter.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverter.java new file mode 100644 index 000000000..7c3362faa --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverter.java @@ -0,0 +1,84 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; + +/** + * Converts a logical column definition into platform specific one. + * + * This translates standard sql types into platform specific ones. + */ +public class PlatformTypeConverter { + + protected final DbTypeMap platformTypes; + + /** + * Construct with the platform specific types. + */ + public PlatformTypeConverter(DbTypeMap platformTypes) { + this.platformTypes = platformTypes; + } + + /** + * Convert the standard type to the platform specific type. + */ + public String convert(String columnDefinition) { + + int open = columnDefinition.indexOf('('); + if (open > -1) { + // no scale or precision + return convertWithScale(columnDefinition, open); + } else { + return convertNoScale(columnDefinition); + } + } + + /** + * Convert a type that has scale and possibly precision. + */ + protected String convertWithScale(String columnDefinition, int open) { + + int close = columnDefinition.lastIndexOf(')'); + if (close == -1) { + // assume already platform specific, leave as is + return columnDefinition; + } + + String type = columnDefinition.substring(0,open); + try { + DbType dbType = platformTypes.lookup(type); + int comma = columnDefinition.indexOf(',',open); + if (comma > -1) { + // scale and precision - decimal(10,4) + int scale = Integer.parseInt(columnDefinition.substring(open+1, comma)); + int precision = Integer.parseInt(columnDefinition.substring(comma+1, close)); + return dbType.renderType(scale,precision); + + } else { + // scale - varchar(10) + int scale = Integer.parseInt(columnDefinition.substring(open+1, close)); + return dbType.renderType(scale,0); + } + + } catch (IllegalArgumentException e) { + // assume already platform specific, leave as is + return columnDefinition; + } + } + + /** + * Convert a simple type with not scale or precision. + */ + protected String convertNoScale(String columnDefinition) { + + try { + DbType dbType = platformTypes.lookup(columnDefinition); + return dbType.renderType(0,0); + + } catch (IllegalArgumentException e) { + // assume already platform specific, leave as is + return columnDefinition; + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java new file mode 100644 index 000000000..350af181e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java @@ -0,0 +1,31 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; + +/** + * Postgres specific DDL. + */ +public class PostgresDdl extends PlatformDdl { + + public PostgresDdl(DbTypeMap platformTypes) { + super(platformTypes, new PostgresHistoryDdl()); + this.foreignKeyRestrict = "on delete restrict on update restrict"; + } + + /** + * Map bigint, integer and smallint into their equivalent serial types. + */ + public String asIdentityColumn(String columnDefn) { + + if ("bigint".equalsIgnoreCase(columnDefn)) { + return "bigserial"; + } + if ("integer".equalsIgnoreCase(columnDefn)) { + return "serial"; + } + if ("smallint".equalsIgnoreCase(columnDefn)) { + return "smallserial"; + } + return columnDefn; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java new file mode 100644 index 000000000..f18efcf9e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresHistoryDdl.java @@ -0,0 +1,135 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + +import java.io.IOException; +import java.util.Collection; + +/** + * + */ +public class PostgresHistoryDdl implements PlatformHistoryDdl { + + DbNameNormalise normalise = new DbNameNormalise(); + + protected String historyTableName(String baseTableName) { + return baseTableName + "_history"; + } + + protected String procedureName(String baseTableName) { + return baseTableName + "_history_version"; + } + + protected String triggerName(String baseTableName) { + return baseTableName + "_history_upd"; + } + + public void createWithHistory(DdlWrite writer, MTable table) throws IOException { + + // naming convention + + addHistoryTable(writer, table); + addStoredFunction(writer, table); + addTrigger(writer, table); + } + + public void addHistoryTable(DdlWrite writer, MTable table) throws IOException { + + String baseTableName = this.normalise.normalise(table.getName()); + + DdlBuffer buffer = writer.applyHistory(); + + buffer + .append("alter table ").append(baseTableName) + .append(" add column sys_period tstzrange not null") + .endOfStatement().end(); + + buffer + .append("create table ").append(baseTableName).append("_history") + .append(" (like ").append(baseTableName).append(")") + .endOfStatement().end(); + + buffer + .append("create view ").append(baseTableName).append("_with_history") + .append(" as select * from").append(baseTableName) + .append(" union all select * from").append(baseTableName).append("_history") + .endOfStatement().end(); + + } + + public void addTrigger(DdlWrite writer, MTable table) throws IOException { + + String baseTableName = this.normalise.normalise(table.getName()); + String procedureName = procedureName(baseTableName); + String triggerName = triggerName(baseTableName); + + DdlBuffer buffer = writer.applyHistory(); + buffer + .append("create trigger ").append(triggerName).newLine() + .append(" before insert or update or delete on ").append(baseTableName).newLine() + .append(" for each row execute procedure ").append(procedureName).append("();").newLine().newLine(); + + } + + + public void addStoredFunction(DdlWrite writer, MTable table) throws IOException { + + String baseTableName = this.normalise.normalise(table.getName()); + String procedureName = procedureName(baseTableName); + DdlBuffer buffer = writer.applyHistory(); + + buffer + .append("create or replace function ").append(procedureName).append("() returns trigger as $$").newLine() + .append("begin").newLine(); + buffer + .append(" if (TG_OP = 'INSERT') then").newLine() + .append(" NEW.sys_period = tstzrange(CURRENT_TIMESTAMP,null);").newLine() + .append(" return new;").newLine().newLine(); + buffer + .append(" elsif (TG_OP = 'UPDATE') then").newLine(); + appendInsertIntoHistory(buffer, table); + buffer + .append(" NEW.sys_period = tstzrange(CURRENT_TIMESTAMP,null);").newLine() + .append(" return new;").newLine().newLine(); + buffer + .append(" elsif (TG_OP = 'DELETE') then").newLine(); + appendInsertIntoHistory(buffer, table); + buffer + .append(" return old;").newLine().newLine(); + + buffer + .append(" end if;").newLine() + .append("end;").newLine() + .append("$$ LANGUAGE plpgsql;").newLine(); + + buffer.end(); + } + + protected void appendInsertIntoHistory(DdlBuffer buffer, MTable table) throws IOException { + + String historyTable = historyTableName(table.getName()); + + buffer.append(" insert into ").append(historyTable).append(" (sys_period,"); + appendColumnNames(buffer, table, ""); + buffer.append(") values (tstzrange(lower(OLD.sys_period), CURRENT_TIMESTAMP), "); + appendColumnNames(buffer, table, "OLD."); + buffer.append(");").newLine(); + } + + protected void appendColumnNames(DdlBuffer buffer, MTable table, String columnPrefix) throws IOException { + + //id, line1, line2, city, country_code, version, when_created, when_updated + Collection columns = table.getColumns().values(); + int i = 0; + for (MColumn column : columns) { + if (++i > 1) { + buffer.append(", "); + } + buffer.append(columnPrefix); + buffer.append(column.getName()); + } + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/AddColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/AddColumn.java new file mode 100644 index 000000000..91d01e3cb --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/AddColumn.java @@ -0,0 +1,99 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * </sequence> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column" +}) +@XmlRootElement(name = "addColumn") +public class AddColumn { + + @XmlElement(required = true) + protected List column; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java new file mode 100644 index 000000000..6a9aac565 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java @@ -0,0 +1,143 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="columns" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="references" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "addForeignKey") +public class AddForeignKey { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "columns", required = true) + protected String columns; + @XmlAttribute(name = "references", required = true) + protected String references; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the columns property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumns() { + return columns; + } + + /** + * Sets the value of the columns property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumns(String value) { + this.columns = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java new file mode 100644 index 000000000..31b8a130c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="resourcePath" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "application") +public class Application { + + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "resourcePath", required = true) + protected String resourcePath; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the resourcePath property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getResourcePath() { + return resourcePath; + } + + /** + * Sets the value of the resourcePath property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setResourcePath(String value) { + this.resourcePath = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java new file mode 100644 index 000000000..950b038dd --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java @@ -0,0 +1,69 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}application" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "application" +}) +@XmlRootElement(name = "applications") +public class Applications { + + protected List application; + + /** + * Gets the value of the application property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the application property. + * + * + * For example, to add a new item, do as follows: + * + * getApplication().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Application } + * + * + */ + public List getApplication() { + if (application == null) { + application = new ArrayList(); + } + return this.application; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java new file mode 100644 index 000000000..3ca1a3fca --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "apply") +public class Apply { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java new file mode 100644 index 000000000..502f7875f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java @@ -0,0 +1,160 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <choice> + * <group ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetChildren" maxOccurs="unbounded" minOccurs="0"/> + * </choice> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /> + * <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSetChildren" +}) +@XmlRootElement(name = "changeSet") +public class ChangeSet { + + @XmlElements({ + @XmlElement(name = "configuration", type = Configuration.class), + @XmlElement(name = "sql", type = Sql.class), + @XmlElement(name = "createTable", type = CreateTable.class), + @XmlElement(name = "dropTable", type = DropTable.class), + @XmlElement(name = "renameTable", type = RenameTable.class), + @XmlElement(name = "addHistoryTable", type = CreateHistoryTable.class), + @XmlElement(name = "createView", type = CreateView.class), + @XmlElement(name = "dropView", type = DropView.class), + @XmlElement(name = "renameView", type = RenameView.class), + @XmlElement(name = "addColumn", type = AddColumn.class), + @XmlElement(name = "dropColumn", type = DropColumn.class), + @XmlElement(name = "renameColumn", type = RenameColumn.class), + @XmlElement(name = "addForeignKey", type = AddForeignKey.class), + @XmlElement(name = "dropForeignKey", type = DropForeignKey.class) + }) + protected List changeSetChildren; + @XmlAttribute(name = "id", required = true) + @XmlSchemaType(name = "positiveInteger") + protected BigInteger id; + @XmlAttribute(name = "comment") + protected String comment; + + /** + * Gets the value of the changeSetChildren property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSetChildren property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSetChildren().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Configuration } + * {@link Sql } + * {@link CreateTable } + * {@link DropTable } + * {@link RenameTable } + * {@link CreateHistoryTable } + * {@link CreateView } + * {@link DropView } + * {@link RenameView } + * {@link AddColumn } + * {@link DropColumn } + * {@link RenameColumn } + * {@link AddForeignKey } + * {@link DropForeignKey } + * + * + */ + public List getChangeSetChildren() { + if (changeSetChildren == null) { + changeSetChildren = new ArrayList(); + } + return this.changeSetChildren; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link BigInteger } + * + */ + public BigInteger getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link BigInteger } + * + */ + public void setId(BigInteger value) { + this.id = value; + } + + /** + * Gets the value of the comment property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getComment() { + return comment; + } + + /** + * Sets the value of the comment property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setComment(String value) { + this.comment = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java new file mode 100644 index 000000000..5abc4568c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java @@ -0,0 +1,402 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column"/> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}columnAttributes"/> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "column") +public class Column { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "type", required = true) + protected String type; + @XmlAttribute(name = "defaultValue") + protected String defaultValue; + @XmlAttribute(name = "remarks") + protected String remarks; + @XmlAttribute(name = "notnull") + protected Boolean notnull; + @XmlAttribute(name = "checkConstraint") + protected String checkConstraint; + @XmlAttribute(name = "unique") + protected Boolean unique; + @XmlAttribute(name = "primaryKey") + protected Boolean primaryKey; + @XmlAttribute(name = "identity") + protected Boolean identity; + @XmlAttribute(name = "references") + protected String references; + @XmlAttribute(name = "deleteCascade") + protected Boolean deleteCascade; + @XmlAttribute(name = "deferrable") + protected Boolean deferrable; + @XmlAttribute(name = "initiallyDeferred") + protected Boolean initiallyDeferred; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the defaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * Sets the value of the defaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDefaultValue(String value) { + this.defaultValue = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + + /** + * Gets the value of the notnull property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNotnull() { + return notnull; + } + + /** + * Sets the value of the notnull property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNotnull(Boolean value) { + this.notnull = value; + } + + /** + * Gets the value of the checkConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCheckConstraint() { + return checkConstraint; + } + + /** + * Sets the value of the checkConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCheckConstraint(String value) { + this.checkConstraint = value; + } + + /** + * Gets the value of the unique property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUnique() { + return unique; + } + + /** + * Sets the value of the unique property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUnique(Boolean value) { + this.unique = value; + } + + /** + * Gets the value of the primaryKey property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isPrimaryKey() { + return primaryKey; + } + + /** + * Sets the value of the primaryKey property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setPrimaryKey(Boolean value) { + this.primaryKey = value; + } + + /** + * Gets the value of the identity property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isIdentity() { + return identity; + } + + /** + * Sets the value of the identity property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setIdentity(Boolean value) { + this.identity = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + + /** + * Gets the value of the deleteCascade property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeleteCascade() { + return deleteCascade; + } + + /** + * Sets the value of the deleteCascade property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeleteCascade(Boolean value) { + this.deleteCascade = value; + } + + /** + * Gets the value of the deferrable property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeferrable() { + return deferrable; + } + + /** + * Sets the value of the deferrable property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeferrable(Boolean value) { + this.deferrable = value; + } + + /** + * Gets the value of the initiallyDeferred property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isInitiallyDeferred() { + return initiallyDeferred; + } + + /** + * Sets the value of the initiallyDeferred property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setInitiallyDeferred(Boolean value) { + this.initiallyDeferred = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java new file mode 100644 index 000000000..54c2c3162 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java @@ -0,0 +1,64 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}defaultTablespace"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "defaultTablespace" +}) +@XmlRootElement(name = "configuration") +public class Configuration { + + @XmlElement(required = true) + protected DefaultTablespace defaultTablespace; + + /** + * Gets the value of the defaultTablespace property. + * + * @return + * possible object is + * {@link DefaultTablespace } + * + */ + public DefaultTablespace getDefaultTablespace() { + return defaultTablespace; + } + + /** + * Sets the value of the defaultTablespace property. + * + * @param value + * allowed object is + * {@link DefaultTablespace } + * + */ + public void setDefaultTablespace(DefaultTablespace value) { + this.defaultTablespace = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java new file mode 100644 index 000000000..c3cd8aa8f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "createDropHistoryTable", namespace = "http://ebean-orm.github.io/xml/ns/dbmigration") +public class CreateDropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java new file mode 100644 index 000000000..f3e779cff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "addHistoryTable") +public class CreateHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java new file mode 100644 index 000000000..1546a9b49 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java @@ -0,0 +1,237 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column", + "uniqueConstraint" +}) +@XmlRootElement(name = "createTable") +public class CreateTable { + + @XmlElement(required = true) + protected List column; + protected List uniqueConstraint; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "withHistory") + protected Boolean withHistory; + @XmlAttribute(name = "tablespace") + protected String tablespace; + @XmlAttribute(name = "indexTablespace") + protected String indexTablespace; + @XmlAttribute(name = "remarks") + protected String remarks; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the uniqueConstraint property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + * + * For example, to add a new item, do as follows: + * + * getUniqueConstraint().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * For example clob -> text for postgres. + *
Java class for anonymous complex type. + * + *
The following schema fragment specifies the expected content contained within this class. + * + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * </sequence> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + *
set
+ * For example, to add a new item, do as follows: + *
+ * getColumn().add(newItem); + *
+ * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java new file mode 100644 index 000000000..6a9aac565 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/AddForeignKey.java @@ -0,0 +1,143 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="columns" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="references" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "addForeignKey") +public class AddForeignKey { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "columns", required = true) + protected String columns; + @XmlAttribute(name = "references", required = true) + protected String references; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the columns property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumns() { + return columns; + } + + /** + * Sets the value of the columns property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumns(String value) { + this.columns = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java new file mode 100644 index 000000000..31b8a130c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Application.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="resourcePath" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "application") +public class Application { + + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "resourcePath", required = true) + protected String resourcePath; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the resourcePath property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getResourcePath() { + return resourcePath; + } + + /** + * Sets the value of the resourcePath property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setResourcePath(String value) { + this.resourcePath = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java new file mode 100644 index 000000000..950b038dd --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Applications.java @@ -0,0 +1,69 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}application" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "application" +}) +@XmlRootElement(name = "applications") +public class Applications { + + protected List application; + + /** + * Gets the value of the application property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the application property. + * + * + * For example, to add a new item, do as follows: + * + * getApplication().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Application } + * + * + */ + public List getApplication() { + if (application == null) { + application = new ArrayList(); + } + return this.application; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java new file mode 100644 index 000000000..3ca1a3fca --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "apply") +public class Apply { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java new file mode 100644 index 000000000..502f7875f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java @@ -0,0 +1,160 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <choice> + * <group ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetChildren" maxOccurs="unbounded" minOccurs="0"/> + * </choice> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /> + * <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSetChildren" +}) +@XmlRootElement(name = "changeSet") +public class ChangeSet { + + @XmlElements({ + @XmlElement(name = "configuration", type = Configuration.class), + @XmlElement(name = "sql", type = Sql.class), + @XmlElement(name = "createTable", type = CreateTable.class), + @XmlElement(name = "dropTable", type = DropTable.class), + @XmlElement(name = "renameTable", type = RenameTable.class), + @XmlElement(name = "addHistoryTable", type = CreateHistoryTable.class), + @XmlElement(name = "createView", type = CreateView.class), + @XmlElement(name = "dropView", type = DropView.class), + @XmlElement(name = "renameView", type = RenameView.class), + @XmlElement(name = "addColumn", type = AddColumn.class), + @XmlElement(name = "dropColumn", type = DropColumn.class), + @XmlElement(name = "renameColumn", type = RenameColumn.class), + @XmlElement(name = "addForeignKey", type = AddForeignKey.class), + @XmlElement(name = "dropForeignKey", type = DropForeignKey.class) + }) + protected List changeSetChildren; + @XmlAttribute(name = "id", required = true) + @XmlSchemaType(name = "positiveInteger") + protected BigInteger id; + @XmlAttribute(name = "comment") + protected String comment; + + /** + * Gets the value of the changeSetChildren property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSetChildren property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSetChildren().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Configuration } + * {@link Sql } + * {@link CreateTable } + * {@link DropTable } + * {@link RenameTable } + * {@link CreateHistoryTable } + * {@link CreateView } + * {@link DropView } + * {@link RenameView } + * {@link AddColumn } + * {@link DropColumn } + * {@link RenameColumn } + * {@link AddForeignKey } + * {@link DropForeignKey } + * + * + */ + public List getChangeSetChildren() { + if (changeSetChildren == null) { + changeSetChildren = new ArrayList(); + } + return this.changeSetChildren; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link BigInteger } + * + */ + public BigInteger getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link BigInteger } + * + */ + public void setId(BigInteger value) { + this.id = value; + } + + /** + * Gets the value of the comment property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getComment() { + return comment; + } + + /** + * Sets the value of the comment property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setComment(String value) { + this.comment = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java new file mode 100644 index 000000000..5abc4568c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java @@ -0,0 +1,402 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column"/> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}columnAttributes"/> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "column") +public class Column { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "type", required = true) + protected String type; + @XmlAttribute(name = "defaultValue") + protected String defaultValue; + @XmlAttribute(name = "remarks") + protected String remarks; + @XmlAttribute(name = "notnull") + protected Boolean notnull; + @XmlAttribute(name = "checkConstraint") + protected String checkConstraint; + @XmlAttribute(name = "unique") + protected Boolean unique; + @XmlAttribute(name = "primaryKey") + protected Boolean primaryKey; + @XmlAttribute(name = "identity") + protected Boolean identity; + @XmlAttribute(name = "references") + protected String references; + @XmlAttribute(name = "deleteCascade") + protected Boolean deleteCascade; + @XmlAttribute(name = "deferrable") + protected Boolean deferrable; + @XmlAttribute(name = "initiallyDeferred") + protected Boolean initiallyDeferred; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the defaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * Sets the value of the defaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDefaultValue(String value) { + this.defaultValue = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + + /** + * Gets the value of the notnull property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNotnull() { + return notnull; + } + + /** + * Sets the value of the notnull property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNotnull(Boolean value) { + this.notnull = value; + } + + /** + * Gets the value of the checkConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCheckConstraint() { + return checkConstraint; + } + + /** + * Sets the value of the checkConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCheckConstraint(String value) { + this.checkConstraint = value; + } + + /** + * Gets the value of the unique property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUnique() { + return unique; + } + + /** + * Sets the value of the unique property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUnique(Boolean value) { + this.unique = value; + } + + /** + * Gets the value of the primaryKey property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isPrimaryKey() { + return primaryKey; + } + + /** + * Sets the value of the primaryKey property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setPrimaryKey(Boolean value) { + this.primaryKey = value; + } + + /** + * Gets the value of the identity property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isIdentity() { + return identity; + } + + /** + * Sets the value of the identity property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setIdentity(Boolean value) { + this.identity = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + + /** + * Gets the value of the deleteCascade property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeleteCascade() { + return deleteCascade; + } + + /** + * Sets the value of the deleteCascade property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeleteCascade(Boolean value) { + this.deleteCascade = value; + } + + /** + * Gets the value of the deferrable property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeferrable() { + return deferrable; + } + + /** + * Sets the value of the deferrable property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeferrable(Boolean value) { + this.deferrable = value; + } + + /** + * Gets the value of the initiallyDeferred property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isInitiallyDeferred() { + return initiallyDeferred; + } + + /** + * Sets the value of the initiallyDeferred property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setInitiallyDeferred(Boolean value) { + this.initiallyDeferred = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java new file mode 100644 index 000000000..54c2c3162 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java @@ -0,0 +1,64 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}defaultTablespace"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "defaultTablespace" +}) +@XmlRootElement(name = "configuration") +public class Configuration { + + @XmlElement(required = true) + protected DefaultTablespace defaultTablespace; + + /** + * Gets the value of the defaultTablespace property. + * + * @return + * possible object is + * {@link DefaultTablespace } + * + */ + public DefaultTablespace getDefaultTablespace() { + return defaultTablespace; + } + + /** + * Sets the value of the defaultTablespace property. + * + * @param value + * allowed object is + * {@link DefaultTablespace } + * + */ + public void setDefaultTablespace(DefaultTablespace value) { + this.defaultTablespace = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java new file mode 100644 index 000000000..c3cd8aa8f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "createDropHistoryTable", namespace = "http://ebean-orm.github.io/xml/ns/dbmigration") +public class CreateDropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java new file mode 100644 index 000000000..f3e779cff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "addHistoryTable") +public class CreateHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java new file mode 100644 index 000000000..1546a9b49 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java @@ -0,0 +1,237 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column", + "uniqueConstraint" +}) +@XmlRootElement(name = "createTable") +public class CreateTable { + + @XmlElement(required = true) + protected List column; + protected List uniqueConstraint; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "withHistory") + protected Boolean withHistory; + @XmlAttribute(name = "tablespace") + protected String tablespace; + @XmlAttribute(name = "indexTablespace") + protected String indexTablespace; + @XmlAttribute(name = "remarks") + protected String remarks; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the uniqueConstraint property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + * + * For example, to add a new item, do as follows: + * + * getUniqueConstraint().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="columns" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="references" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </extension> + * </simpleContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="resourcePath" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}application" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + *
+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the application property. + * + *
+ * getApplication().add(newItem); + *
+ * Objects of the following type(s) are allowed in the list + * {@link Application } + * + * + */ + public List getApplication() { + if (application == null) { + application = new ArrayList(); + } + return this.application; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java new file mode 100644 index 000000000..3ca1a3fca --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Apply.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "apply") +public class Apply { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java new file mode 100644 index 000000000..502f7875f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java @@ -0,0 +1,160 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <choice> + * <group ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetChildren" maxOccurs="unbounded" minOccurs="0"/> + * </choice> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /> + * <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSetChildren" +}) +@XmlRootElement(name = "changeSet") +public class ChangeSet { + + @XmlElements({ + @XmlElement(name = "configuration", type = Configuration.class), + @XmlElement(name = "sql", type = Sql.class), + @XmlElement(name = "createTable", type = CreateTable.class), + @XmlElement(name = "dropTable", type = DropTable.class), + @XmlElement(name = "renameTable", type = RenameTable.class), + @XmlElement(name = "addHistoryTable", type = CreateHistoryTable.class), + @XmlElement(name = "createView", type = CreateView.class), + @XmlElement(name = "dropView", type = DropView.class), + @XmlElement(name = "renameView", type = RenameView.class), + @XmlElement(name = "addColumn", type = AddColumn.class), + @XmlElement(name = "dropColumn", type = DropColumn.class), + @XmlElement(name = "renameColumn", type = RenameColumn.class), + @XmlElement(name = "addForeignKey", type = AddForeignKey.class), + @XmlElement(name = "dropForeignKey", type = DropForeignKey.class) + }) + protected List changeSetChildren; + @XmlAttribute(name = "id", required = true) + @XmlSchemaType(name = "positiveInteger") + protected BigInteger id; + @XmlAttribute(name = "comment") + protected String comment; + + /** + * Gets the value of the changeSetChildren property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSetChildren property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSetChildren().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Configuration } + * {@link Sql } + * {@link CreateTable } + * {@link DropTable } + * {@link RenameTable } + * {@link CreateHistoryTable } + * {@link CreateView } + * {@link DropView } + * {@link RenameView } + * {@link AddColumn } + * {@link DropColumn } + * {@link RenameColumn } + * {@link AddForeignKey } + * {@link DropForeignKey } + * + * + */ + public List getChangeSetChildren() { + if (changeSetChildren == null) { + changeSetChildren = new ArrayList(); + } + return this.changeSetChildren; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link BigInteger } + * + */ + public BigInteger getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link BigInteger } + * + */ + public void setId(BigInteger value) { + this.id = value; + } + + /** + * Gets the value of the comment property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getComment() { + return comment; + } + + /** + * Sets the value of the comment property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setComment(String value) { + this.comment = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java new file mode 100644 index 000000000..5abc4568c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java @@ -0,0 +1,402 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column"/> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}columnAttributes"/> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "column") +public class Column { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "type", required = true) + protected String type; + @XmlAttribute(name = "defaultValue") + protected String defaultValue; + @XmlAttribute(name = "remarks") + protected String remarks; + @XmlAttribute(name = "notnull") + protected Boolean notnull; + @XmlAttribute(name = "checkConstraint") + protected String checkConstraint; + @XmlAttribute(name = "unique") + protected Boolean unique; + @XmlAttribute(name = "primaryKey") + protected Boolean primaryKey; + @XmlAttribute(name = "identity") + protected Boolean identity; + @XmlAttribute(name = "references") + protected String references; + @XmlAttribute(name = "deleteCascade") + protected Boolean deleteCascade; + @XmlAttribute(name = "deferrable") + protected Boolean deferrable; + @XmlAttribute(name = "initiallyDeferred") + protected Boolean initiallyDeferred; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the defaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * Sets the value of the defaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDefaultValue(String value) { + this.defaultValue = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + + /** + * Gets the value of the notnull property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNotnull() { + return notnull; + } + + /** + * Sets the value of the notnull property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNotnull(Boolean value) { + this.notnull = value; + } + + /** + * Gets the value of the checkConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCheckConstraint() { + return checkConstraint; + } + + /** + * Sets the value of the checkConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCheckConstraint(String value) { + this.checkConstraint = value; + } + + /** + * Gets the value of the unique property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUnique() { + return unique; + } + + /** + * Sets the value of the unique property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUnique(Boolean value) { + this.unique = value; + } + + /** + * Gets the value of the primaryKey property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isPrimaryKey() { + return primaryKey; + } + + /** + * Sets the value of the primaryKey property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setPrimaryKey(Boolean value) { + this.primaryKey = value; + } + + /** + * Gets the value of the identity property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isIdentity() { + return identity; + } + + /** + * Sets the value of the identity property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setIdentity(Boolean value) { + this.identity = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + + /** + * Gets the value of the deleteCascade property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeleteCascade() { + return deleteCascade; + } + + /** + * Sets the value of the deleteCascade property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeleteCascade(Boolean value) { + this.deleteCascade = value; + } + + /** + * Gets the value of the deferrable property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeferrable() { + return deferrable; + } + + /** + * Sets the value of the deferrable property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeferrable(Boolean value) { + this.deferrable = value; + } + + /** + * Gets the value of the initiallyDeferred property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isInitiallyDeferred() { + return initiallyDeferred; + } + + /** + * Sets the value of the initiallyDeferred property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setInitiallyDeferred(Boolean value) { + this.initiallyDeferred = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java new file mode 100644 index 000000000..54c2c3162 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java @@ -0,0 +1,64 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}defaultTablespace"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "defaultTablespace" +}) +@XmlRootElement(name = "configuration") +public class Configuration { + + @XmlElement(required = true) + protected DefaultTablespace defaultTablespace; + + /** + * Gets the value of the defaultTablespace property. + * + * @return + * possible object is + * {@link DefaultTablespace } + * + */ + public DefaultTablespace getDefaultTablespace() { + return defaultTablespace; + } + + /** + * Sets the value of the defaultTablespace property. + * + * @param value + * allowed object is + * {@link DefaultTablespace } + * + */ + public void setDefaultTablespace(DefaultTablespace value) { + this.defaultTablespace = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java new file mode 100644 index 000000000..c3cd8aa8f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "createDropHistoryTable", namespace = "http://ebean-orm.github.io/xml/ns/dbmigration") +public class CreateDropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java new file mode 100644 index 000000000..f3e779cff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "addHistoryTable") +public class CreateHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java new file mode 100644 index 000000000..1546a9b49 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java @@ -0,0 +1,237 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column", + "uniqueConstraint" +}) +@XmlRootElement(name = "createTable") +public class CreateTable { + + @XmlElement(required = true) + protected List column; + protected List uniqueConstraint; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "withHistory") + protected Boolean withHistory; + @XmlAttribute(name = "tablespace") + protected String tablespace; + @XmlAttribute(name = "indexTablespace") + protected String indexTablespace; + @XmlAttribute(name = "remarks") + protected String remarks; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the uniqueConstraint property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + * + * For example, to add a new item, do as follows: + * + * getUniqueConstraint().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <choice> + * <group ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetChildren" maxOccurs="unbounded" minOccurs="0"/> + * </choice> + * </sequence> + * <attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /> + * <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSetChildren property. + * + *
+ * getChangeSetChildren().add(newItem); + *
+ * Objects of the following type(s) are allowed in the list + * {@link Configuration } + * {@link Sql } + * {@link CreateTable } + * {@link DropTable } + * {@link RenameTable } + * {@link CreateHistoryTable } + * {@link CreateView } + * {@link DropView } + * {@link RenameView } + * {@link AddColumn } + * {@link DropColumn } + * {@link RenameColumn } + * {@link AddForeignKey } + * {@link DropForeignKey } + * + * + */ + public List getChangeSetChildren() { + if (changeSetChildren == null) { + changeSetChildren = new ArrayList(); + } + return this.changeSetChildren; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link BigInteger } + * + */ + public BigInteger getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link BigInteger } + * + */ + public void setId(BigInteger value) { + this.id = value; + } + + /** + * Gets the value of the comment property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getComment() { + return comment; + } + + /** + * Sets the value of the comment property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setComment(String value) { + this.comment = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java new file mode 100644 index 000000000..5abc4568c --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java @@ -0,0 +1,402 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column"/> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}columnAttributes"/> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "column") +public class Column { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "type", required = true) + protected String type; + @XmlAttribute(name = "defaultValue") + protected String defaultValue; + @XmlAttribute(name = "remarks") + protected String remarks; + @XmlAttribute(name = "notnull") + protected Boolean notnull; + @XmlAttribute(name = "checkConstraint") + protected String checkConstraint; + @XmlAttribute(name = "unique") + protected Boolean unique; + @XmlAttribute(name = "primaryKey") + protected Boolean primaryKey; + @XmlAttribute(name = "identity") + protected Boolean identity; + @XmlAttribute(name = "references") + protected String references; + @XmlAttribute(name = "deleteCascade") + protected Boolean deleteCascade; + @XmlAttribute(name = "deferrable") + protected Boolean deferrable; + @XmlAttribute(name = "initiallyDeferred") + protected Boolean initiallyDeferred; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the defaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * Sets the value of the defaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDefaultValue(String value) { + this.defaultValue = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + + /** + * Gets the value of the notnull property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNotnull() { + return notnull; + } + + /** + * Sets the value of the notnull property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNotnull(Boolean value) { + this.notnull = value; + } + + /** + * Gets the value of the checkConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCheckConstraint() { + return checkConstraint; + } + + /** + * Sets the value of the checkConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCheckConstraint(String value) { + this.checkConstraint = value; + } + + /** + * Gets the value of the unique property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUnique() { + return unique; + } + + /** + * Sets the value of the unique property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUnique(Boolean value) { + this.unique = value; + } + + /** + * Gets the value of the primaryKey property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isPrimaryKey() { + return primaryKey; + } + + /** + * Sets the value of the primaryKey property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setPrimaryKey(Boolean value) { + this.primaryKey = value; + } + + /** + * Gets the value of the identity property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isIdentity() { + return identity; + } + + /** + * Sets the value of the identity property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setIdentity(Boolean value) { + this.identity = value; + } + + /** + * Gets the value of the references property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getReferences() { + return references; + } + + /** + * Sets the value of the references property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setReferences(String value) { + this.references = value; + } + + /** + * Gets the value of the deleteCascade property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeleteCascade() { + return deleteCascade; + } + + /** + * Sets the value of the deleteCascade property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeleteCascade(Boolean value) { + this.deleteCascade = value; + } + + /** + * Gets the value of the deferrable property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isDeferrable() { + return deferrable; + } + + /** + * Sets the value of the deferrable property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setDeferrable(Boolean value) { + this.deferrable = value; + } + + /** + * Gets the value of the initiallyDeferred property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isInitiallyDeferred() { + return initiallyDeferred; + } + + /** + * Sets the value of the initiallyDeferred property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setInitiallyDeferred(Boolean value) { + this.initiallyDeferred = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java new file mode 100644 index 000000000..54c2c3162 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Configuration.java @@ -0,0 +1,64 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}defaultTablespace"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "defaultTablespace" +}) +@XmlRootElement(name = "configuration") +public class Configuration { + + @XmlElement(required = true) + protected DefaultTablespace defaultTablespace; + + /** + * Gets the value of the defaultTablespace property. + * + * @return + * possible object is + * {@link DefaultTablespace } + * + */ + public DefaultTablespace getDefaultTablespace() { + return defaultTablespace; + } + + /** + * Sets the value of the defaultTablespace property. + * + * @param value + * allowed object is + * {@link DefaultTablespace } + * + */ + public void setDefaultTablespace(DefaultTablespace value) { + this.defaultTablespace = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java new file mode 100644 index 000000000..c3cd8aa8f --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateDropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "createDropHistoryTable", namespace = "http://ebean-orm.github.io/xml/ns/dbmigration") +public class CreateDropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java new file mode 100644 index 000000000..f3e779cff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "addHistoryTable") +public class CreateHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java new file mode 100644 index 000000000..1546a9b49 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateTable.java @@ -0,0 +1,237 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "column", + "uniqueConstraint" +}) +@XmlRootElement(name = "createTable") +public class CreateTable { + + @XmlElement(required = true) + protected List column; + protected List uniqueConstraint; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "withHistory") + protected Boolean withHistory; + @XmlAttribute(name = "tablespace") + protected String tablespace; + @XmlAttribute(name = "indexTablespace") + protected String indexTablespace; + @XmlAttribute(name = "remarks") + protected String remarks; + + /** + * Gets the value of the column property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the column property. + * + * + * For example, to add a new item, do as follows: + * + * getColumn().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the uniqueConstraint property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + * + * For example, to add a new item, do as follows: + * + * getUniqueConstraint().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column"/> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}columnAttributes"/> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}defaultTablespace"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/> + * </sequence> + * <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * Objects of the following type(s) are allowed in the list + * {@link Column } + * + * + */ + public List getColumn() { + if (column == null) { + column = new ArrayList(); + } + return this.column; + } + + /** + * Gets the value of the uniqueConstraint property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + * + * For example, to add a new item, do as follows: + * + * getUniqueConstraint().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the uniqueConstraint property. + * + *
+ * getUniqueConstraint().add(newItem); + *
+ * Objects of the following type(s) are allowed in the list + * {@link UniqueConstraint } + * + * + */ + public List getUniqueConstraint() { + if (uniqueConstraint == null) { + uniqueConstraint = new ArrayList(); + } + return this.uniqueConstraint; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the withHistory property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isWithHistory() { + return withHistory; + } + + /** + * Sets the value of the withHistory property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setWithHistory(Boolean value) { + this.withHistory = value; + } + + /** + * Gets the value of the tablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTablespace() { + return tablespace; + } + + /** + * Sets the value of the tablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTablespace(String value) { + this.tablespace = value; + } + + /** + * Gets the value of the indexTablespace property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Sets the value of the indexTablespace property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexTablespace(String value) { + this.indexTablespace = value; + } + + /** + * Gets the value of the remarks property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getRemarks() { + return remarks; + } + + /** + * Sets the value of the remarks property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setRemarks(String value) { + this.remarks = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java new file mode 100644 index 000000000..0eccb5849 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/CreateView.java @@ -0,0 +1,116 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "createView") +public class CreateView { + + @XmlValue + protected String value; + @XmlAttribute(name = "name", required = true) + protected String name; + @XmlAttribute(name = "replaceIfExists") + protected Boolean replaceIfExists; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + + /** + * Gets the value of the replaceIfExists property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isReplaceIfExists() { + return replaceIfExists; + } + + /** + * Sets the value of the replaceIfExists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setReplaceIfExists(Boolean value) { + this.replaceIfExists = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java new file mode 100644 index 000000000..5beaf7555 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DefaultTablespace.java @@ -0,0 +1,114 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "defaultTablespace") +public class DefaultTablespace { + + @XmlAttribute(name = "tables") + protected String tables; + @XmlAttribute(name = "indexes") + protected String indexes; + @XmlAttribute(name = "history") + protected String history; + + /** + * Gets the value of the tables property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTables() { + return tables; + } + + /** + * Sets the value of the tables property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTables(String value) { + this.tables = value; + } + + /** + * Gets the value of the indexes property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getIndexes() { + return indexes; + } + + /** + * Sets the value of the indexes property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setIndexes(String value) { + this.indexes = value; + } + + /** + * Gets the value of the history property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getHistory() { + return history; + } + + /** + * Sets the value of the history property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setHistory(String value) { + this.history = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java new file mode 100644 index 000000000..06c3fe6db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropColumn.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropColumn") +public class DropColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java new file mode 100644 index 000000000..a38b15e76 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropForeignKey.java @@ -0,0 +1,89 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "content" +}) +@XmlRootElement(name = "dropForeignKey") +public class DropForeignKey { + + @XmlValue + protected String content; + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the content property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getContent() { + return content; + } + + /** + * Sets the value of the content property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setContent(String value) { + this.content = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java new file mode 100644 index 000000000..e2ad3f4b8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropHistoryTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="baseTable" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropHistoryTable") +public class DropHistoryTable { + + @XmlAttribute(name = "baseTable", required = true) + protected String baseTable; + + /** + * Gets the value of the baseTable property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Sets the value of the baseTable property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBaseTable(String value) { + this.baseTable = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java new file mode 100644 index 000000000..fe24e36af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropTable.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropTable") +public class DropTable { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java new file mode 100644 index 000000000..6fd5ad032 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/DropView.java @@ -0,0 +1,60 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "dropView") +public class DropView { + + @XmlAttribute(name = "name", required = true) + protected String name; + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setName(String value) { + this.name = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java new file mode 100644 index 000000000..b4ec46043 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Migration.java @@ -0,0 +1,71 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "changeSet" +}) +@XmlRootElement(name = "migration") +public class Migration { + + @XmlElement(required = true) + protected List changeSet; + + /** + * Gets the value of the changeSet property. + * + * + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + * + * For example, to add a new item, do as follows: + * + * getChangeSet().add(newItem); + * + * + * + * + * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
+ * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="replaceIfExists" type="{http://www.w3.org/2001/XMLSchema}boolean" /> + * </extension> + * </simpleContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="tables" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="indexes" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="history" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSet" maxOccurs="unbounded"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + *
+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the changeSet property. + * + *
+ * getChangeSet().add(newItem); + *
+ * Objects of the following type(s) are allowed in the list + * {@link ChangeSet } + * + * + */ + public List getChangeSet() { + if (changeSet == null) { + changeSet = new ArrayList(); + } + return this.changeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java new file mode 100644 index 000000000..c0614a695 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -0,0 +1,224 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.avaje.ebean.dbmigration.migration package. + * An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameColumn") +public class RenameColumn { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "dataType") + protected String dataType; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the dataType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDataType() { + return dataType; + } + + /** + * Sets the value of the dataType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDataType(String value) { + this.dataType = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java new file mode 100644 index 000000000..aca4f7f88 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameTable.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameTable") +public class RenameTable { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java new file mode 100644 index 000000000..7623ba705 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameView.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "renameView") +public class RenameView { + + @XmlAttribute(name = "oldName", required = true) + protected String oldName; + @XmlAttribute(name = "newName", required = true) + protected String newName; + + /** + * Gets the value of the oldName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldName() { + return oldName; + } + + /** + * Sets the value of the oldName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldName(String value) { + this.oldName = value; + } + + /** + * Gets the value of the newName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewName() { + return newName; + } + + /** + * Sets the value of the newName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewName(String value) { + this.newName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java new file mode 100644 index 000000000..9779712a9 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Rollback.java @@ -0,0 +1,61 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <simpleContent> + * <extension base="<http://www.w3.org/2001/XMLSchema>string"> + * </extension> + * </simpleContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "value" +}) +@XmlRootElement(name = "rollback") +public class Rollback { + + @XmlValue + protected String value; + + /** + * Gets the value of the value property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getValue() { + return value; + } + + /** + * Sets the value of the value property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java new file mode 100644 index 000000000..534536ddc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Sql.java @@ -0,0 +1,92 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "apply", + "rollback" +}) +@XmlRootElement(name = "sql") +public class Sql { + + @XmlElement(required = true) + protected Apply apply; + @XmlElement(required = true) + protected Rollback rollback; + + /** + * Gets the value of the apply property. + * + * @return + * possible object is + * {@link Apply } + * + */ + public Apply getApply() { + return apply; + } + + /** + * Sets the value of the apply property. + * + * @param value + * allowed object is + * {@link Apply } + * + */ + public void setApply(Apply value) { + this.apply = value; + } + + /** + * Gets the value of the rollback property. + * + * @return + * possible object is + * {@link Rollback } + * + */ + public Rollback getRollback() { + return rollback; + } + + /** + * Sets the value of the rollback property. + * + * @param value + * allowed object is + * {@link Rollback } + * + */ + public void setRollback(Rollback value) { + this.rollback = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java new file mode 100644 index 000000000..42a3ed359 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/UniqueConstraint.java @@ -0,0 +1,87 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * Java class for anonymous complex type. + * + * The following schema fragment specifies the expected content contained within this class. + * + * + * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + * + * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "uniqueConstraint") +public class UniqueConstraint { + + @XmlAttribute(name = "columnNames", required = true) + protected String columnNames; + @XmlAttribute(name = "constraintName") + protected String constraintName; + + /** + * Gets the value of the columnNames property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnNames() { + return columnNames; + } + + /** + * Sets the value of the columnNames property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnNames(String value) { + this.columnNames = value; + } + + /** + * Gets the value of the constraintName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getConstraintName() { + return constraintName; + } + + /** + * Sets the value of the constraintName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setConstraintName(String value) { + this.constraintName = value; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java new file mode 100644 index 000000000..3002dd9ff --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/dbmigration", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebean.dbmigration.migration; diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java new file mode 100644 index 000000000..5e3eb421b --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -0,0 +1,46 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; + +/** + * Reads a migration xml document returning the Migration. + */ +public class MigrationXmlReader { + + private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration read(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + throw new IllegalArgumentException("No resource found for path [" + resourcePath + "]"); + } + + return INSTANCE.read(is); + } + + /** + * Read and return a Migration from an xml document. + */ + public Migration read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Migration) unmarshaller.unmarshal(is); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java new file mode 100644 index 000000000..a6c6b3bfc --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration.migrationreader; + + +import com.avaje.ebean.dbmigration.migration.Migration; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the Migration/ChangeSet as an XML document. + */ +public class MigrationXmlWriter { + + /** + * Write a Migration to a file as an xml document to the file. + */ + public void write(Migration migration, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Migration.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(migration, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java new file mode 100644 index 000000000..ca78b7893 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -0,0 +1,130 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; +import com.avaje.ebean.dbmigration.model.build.ModelBuildBeanVisitor; +import com.avaje.ebean.dbmigration.model.build.ModelBuildContext; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Reads EbeanServer bean descriptors to build the current model. + */ +public class CurrentModel { + + private final SpiEbeanServer server; + + private DdlNamingConvention namingConvention; + + private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + + public CurrentModel(SpiEbeanServer server) { + this.server = server; + this.namingConvention = new DdlNamingConvention(); + } + + public ModelContainer read() { + if (model == null) { + model = new ModelContainer(); + ModelBuildContext context = new ModelBuildContext(model); + ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); + VisitAllUsing visit = new VisitAllUsing(visitor, server); + visit.visitAllBeans(); + } + return model; + } + + public ChangeSet getChangeSet() { + read(); + if (changeSet == null) { + changeSet = asChangeSet(); + } + return changeSet; + } + + public void writeMigration(File file) { + + ChangeSet changeSet = getChangeSet(); + Migration migration = new Migration(); + migration.getChangeSet().add(changeSet); + + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, file); + } + + public String getCreateDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.apply().getBuffer()); + ddl.append(write.applyForeignKeys().getBuffer()); + ddl.append(write.applyHistory().getBuffer()); + + return ddl.toString(); + } + + public String getDropDdl() throws IOException { + + createDdl(); + + StringBuilder ddl = new StringBuilder(2000); + ddl.append(write.rollbackForeignKeys().getBuffer()); + ddl.append(write.rollback().getBuffer()); + + return ddl.toString(); + } + + private void createDdl() throws IOException { + + if (write == null) { + ChangeSet createChangeSet = getChangeSet(); + + write = new DdlWrite(); + + BaseDdlHandler handler = handler(); + handler.generate(write, createChangeSet); + } + } + + private BaseDdlHandler handler() { + + DatabasePlatform databasePlatform = server.getDatabasePlatform(); + PlatformDdl platformDdl = databasePlatform.getPlatformDdl(); + + return new BaseDdlHandler(namingConvention, platformDdl); + } + + /** + * Convert the model into a ChangeSet. + */ + private ChangeSet asChangeSet() { + + // empty diff so changes will effectively all be create + ModelDiff diff = new ModelDiff(); + diff.compareTo(model); + + List createChanges = diff.getCreateChanges(); + + // put the changes into a ChangeSet + ChangeSet createChangeSet = new ChangeSet(); + createChangeSet.getChangeSetChildren().addAll(createChanges); + return createChangeSet; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java new file mode 100644 index 000000000..f24dfd1db --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -0,0 +1,125 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Column; + +/** + * A column in the logical model. + */ +public class MColumn { + + private final String name; + private final String type; + private String checkConstraint; + private String defaultValue; + private String references; + private boolean notnull; + private boolean primaryKey; + private boolean identity; + private boolean unique; + + public MColumn(Column column) { + this.name = column.getName(); + this.type = column.getType(); + this.checkConstraint = column.getCheckConstraint(); + this.defaultValue = column.getDefaultValue(); + this.references = column.getReferences(); + this.notnull = Boolean.TRUE.equals(column.isNotnull()); + this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); + this.identity = Boolean.TRUE.equals(column.isIdentity()); + this.unique = Boolean.TRUE.equals(column.isUnique()); + } + + public MColumn(String name, String type) { + this.name = name; + this.type = type; + } + + public MColumn(String name, String type, boolean notnull) { + this.name = name; + this.type = type; + this.notnull = notnull; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isIdentity() { + return identity; + } + + public void setIdentity(boolean identity) { + this.identity = identity; + } + + public String getCheckConstraint() { + return checkConstraint; + } + + public void setCheckConstraint(String checkConstraint) { + this.checkConstraint = checkConstraint; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getReferences() { + return references; + } + + public void setReferences(String references) { + this.references = references; + } + + public boolean isNotnull() { + return notnull; + } + + public void setNotnull(boolean notnull) { + this.notnull = notnull; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isUnique() { + return unique; + } + + public Column createColumn() { + + Column c = new Column(); + c.setName(name); + c.setType(type); + c.setNotnull(notnull); + c.setCheckConstraint(checkConstraint); + c.setUnique(unique); + c.setPrimaryKey(primaryKey); + c.setIdentity(identity); + c.setReferences(references); + c.setDefaultValue(defaultValue); + + //c.setDeleteCascade(); + //c.setDeferrable(deferrable); + + return c; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java new file mode 100644 index 000000000..84472f97e --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundForeignKey.java @@ -0,0 +1,27 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundForeignKey { + + private final String referenceTable; + private final List columns = new ArrayList(); + private final List referenceColumns = new ArrayList(); + + public MCompoundForeignKey(String referenceTable) { + this.referenceTable = referenceTable; + } + + public void addColumnPair(String dbCol, String refColumn) { + columns.add(dbCol); + referenceColumns.add(refColumn); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java new file mode 100644 index 000000000..ff9f9b9af --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MCompoundUniqueConstraint.java @@ -0,0 +1,24 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * A unique constraint for multiple columns. + * + * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + * + */ +public class MCompoundUniqueConstraint { + + /** + * The columns combined to be unique. + */ + private final String[] columns; + + public MCompoundUniqueConstraint(String[] columns) { + this.columns = columns; + } + + public String[] getColumns() { + return columns; + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java new file mode 100644 index 000000000..f6165a670 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MConfiguration.java @@ -0,0 +1,77 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Configuration; +import com.avaje.ebean.dbmigration.migration.DefaultTablespace; + +/** + * Holds configuration such as the default tablespaces to use for tables, + * indexes, history tables etc. + */ +public class MConfiguration { + + /** + * Default tablespace for tables. + */ + protected String tableTablespace; + + /** + * Default tablespace for indexes. + */ + protected String indexTablespace; + + /** + * Default tablespace for history tables. + */ + protected String historyTablespace; + + /** + * Apply the migration configuration. + * + * It is expected that these are applied in the correct chronological order + * from earliest to latest. + * + */ + public void apply(Configuration configuration) { + + DefaultTablespace defaultTablespace = configuration.getDefaultTablespace(); + if (defaultTablespace != null) { + String tables = defaultTablespace.getTables(); + if (isNotEmpty(tables)) { + this.tableTablespace = tables; + } + String indexes = defaultTablespace.getIndexes(); + if (isNotEmpty(indexes)) { + this.indexTablespace = indexes; + } + String history = defaultTablespace.getHistory(); + if (isNotEmpty(history)) { + this.historyTablespace = history; + } + } + } + + /** + * Return the default tablespace to use for tables. + */ + public String getTableTablespace() { + return tableTablespace; + } + + /** + * Return the default tablespace to use for indexes. + */ + public String getIndexTablespace() { + return indexTablespace; + } + + /** + * Return the default tablespace to use for history tables. + */ + public String getHistoryTablespace() { + return historyTablespace; + } + + protected boolean isNotEmpty(String tables) { + return tables != null && !tables.trim().isEmpty(); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java new file mode 100644 index 000000000..5b3810771 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -0,0 +1,7 @@ +package com.avaje.ebean.dbmigration.model; + +/** + * Index as part of the logical model. + */ +public class MIndex { +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java new file mode 100644 index 000000000..753fdadb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -0,0 +1,186 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds the logical model for a given Table and everything associated to it. + * + * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + * + * + * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + * + * + * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + * + */ +public class MTable { + + /** + * Flag set to indicate + */ + private boolean matched; + + private final String name; + + private String remarks; + + private String tablespace; + + private String indexTablespace; + + private Boolean withHistory; + + private Map columns = new LinkedHashMap(); + + private List compoundUniqueConstraints = new ArrayList(); + + private List compoundKeys = new ArrayList(); + /** + * Construct for migration. + */ + public MTable(CreateTable createTable) { + this.name = createTable.getName(); + this.remarks = createTable.getRemarks(); + this.tablespace = createTable.getTablespace(); + this.indexTablespace = createTable.getIndexTablespace(); + this.withHistory = createTable.isWithHistory(); + List cols = createTable.getColumn(); + for (Column column : cols) { + addColumn(column); + } + } + + /** + * Construct typically from EbeanServer meta data. + */ + public MTable(String name) { + this.name = name; + } + + public CreateTable createTable() { + + CreateTable createTable = new CreateTable(); + createTable.setName(name); + createTable.setRemarks(remarks); + createTable.setTablespace(tablespace); + createTable.setIndexTablespace(indexTablespace); + createTable.setWithHistory(withHistory); + + for (MColumn column : this.columns.values()) { + createTable.getColumn().add(column.createColumn()); + } + return createTable; + } + + public boolean isMatched() { + return matched; + } + + public void setMatched(boolean matched) { + this.matched = matched; + } + + /** + * Apply AddColumn migration. + */ + public void apply(AddColumn addColumn) { + checkTableName(addColumn.getTableName()); + for (Column column : addColumn.getColumn()) { + addColumn(column); + } + } + + /** + * Apply DropColumn migration. + */ + public void apply(DropColumn dropColumn) { + checkTableName(dropColumn.getTableName()); + columns.remove(dropColumn.getColumnName()); + } + + public String getName() { + return name; + } + + public String getRemarks() { + return remarks; + } + + public String getTablespace() { + return tablespace; + } + + public String getIndexTablespace() { + return indexTablespace; + } + + public Boolean getWithHistory() { + return withHistory; + } + + public Map getColumns() { + return columns; + } + + public List getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + public List getCompoundKeys() { + return compoundKeys; + } + + private void checkTableName(String tableName) { + if (!name.equals(tableName)) { + throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]"); + } + } + + /** + * Add a column via migration data. + */ + private void addColumn(Column column) { + columns.put(column.getName(), new MColumn(column)); + } + + /** + * Add a model column (typically from EbeanServer meta data). + */ + public void addColumn(MColumn column) { + columns.put(column.getName(), column); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(String[] columns) { + compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns)); + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(List columns) { + String[] cols = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + cols[i] = columns.get(i).getName(); + } + addCompoundUniqueConstraint(cols); + } + + public void addForeignKey(MCompoundForeignKey compoundKey) { + compoundKeys.add(compoundKey); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java new file mode 100644 index 000000000..7eed356cf --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Holds all the tables, views, indexes etc that represent the model. + * + * Migration changeSets can be applied to the model. + * + */ +public class ModelContainer { + + /** + * All the tables in the model. + */ + private Map tables = new LinkedHashMap(); + + + /** + * Return the map of all the tables. + */ + public Map getTables() { + return tables; + } + + /** + * Return the table by name. + */ + public MTable getTable(String tableName) { + return tables.get(tableName); + } + + + /** + * Apply a migration with associated changeSets to the model. + */ + public void apply(Migration migration) { + + List changeSets = migration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + applyChangeSet(changeSet); + } + } + + /** + * Apply a changeSet to the model. + */ + protected void applyChangeSet(ChangeSet changeSet) { + + List changeSetChildren = changeSet.getChangeSetChildren(); + for (Object change : changeSetChildren) { + if (change instanceof CreateTable) { + applyChange((CreateTable) change); + } else if (change instanceof AddColumn) { + applyChange((AddColumn) change); + } else if (change instanceof DropColumn) { + applyChange((DropColumn) change); + } + } + } + + /** + * Apply a CreateTable change to the model. + */ + protected void applyChange(CreateTable createTable) { + String tableName = createTable.getName(); + if (tables.containsKey(tableName)) { + throw new IllegalStateException("Table [" + tableName + "] already exists?"); + } + MTable table = new MTable(createTable); + tables.put(tableName, table); + } + + /** + * Apply a AddColumn change to the model. + */ + protected void applyChange(AddColumn addColumn) { + MTable table = tables.get(addColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + } + table.apply(addColumn); + } + + /** + * Apply a DropColumn change to the model. + */ + protected void applyChange(DropColumn dropColumn) { + MTable table = tables.get(dropColumn.getTableName()); + if (table == null) { + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + } + table.apply(dropColumn); + } + + /** + * Add a table (typically from reading EbeanServer meta data). + */ + public void addTable(MTable table) { + tables.put(table.getName(), table); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java new file mode 100644 index 000000000..23be84b02 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -0,0 +1,98 @@ +package com.avaje.ebean.dbmigration.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used to prepare a diff in terms of changes required to migrate from + * the base model to the newer model. + */ +public class ModelDiff { + + /** + * The base model to which we compare the newer model. + */ + protected final ModelContainer baseModel; + + /** + * List of 'create' type changes. + */ + protected final List createChanges = new ArrayList(); + + /** + * List of 'drop' type changes. Potential for putting into a separate changeSet. + */ + protected final List dropChanges = new ArrayList(); + + /** + * Construct with a base model. + */ + public ModelDiff(ModelContainer baseModel) { + this.baseModel = baseModel; + } + + /** + * Construct with a base model. + */ + public ModelDiff() { + this.baseModel = new ModelContainer(); + } + + /** + * Return the list of 'create' changes. + */ + public List getCreateChanges() { + return createChanges; + } + + /** + * Return the list of 'drop' changes. + */ + public List getDropChanges() { + return dropChanges; + } + + /** + * Compare to a 'newer' model and collect the differences. + */ + public void compareTo(ModelContainer newModel) { + + for (MTable newTable : newModel.getTables().values()) { + + MTable currentTable = baseModel.getTable(newTable.getName()); + if (currentTable == null) { + addNewTable(newTable); + } else { + compareTables(currentTable, newTable); + } + } + + //TODO: other parts of the model? views, indexes etc + + } + + /** + * Add CreateTable to the 'creation' changes. + */ + protected void addNewTable(MTable newTable) { + + createChanges.add(newTable.createTable()); +// createChanges.add(newTable.createForeignKeys()); + } + + /** + * Compare tables looking for add/drop/modify columns etc. + */ + protected void compareTables(MTable currentTable, MTable newTable) { + + //TODO: compareTables() + // changed columns + // find additional columns + // find removed columns + // changes to indexes? + // changes to primary key + // changes to foreign key + // changes to unique constraints? + + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java new file mode 100644 index 000000000..a0af0d6d1 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -0,0 +1,61 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor; +import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor; + +/** + * Used to build the Model objects MTable etc. + */ +public class ModelBuildBeanVisitor implements BeanVisitor { + + private final ModelBuildContext ctx; + + public ModelBuildBeanVisitor(ModelBuildContext ctx) { + this.ctx = ctx; + } + + /** + * Return the PropertyVisitor used to read all the property meta data + * and in this case add MColumn objects to the model. + * + * This creates an MTable and adds it to the model. + * + */ + public BeanPropertyVisitor visitBean(BeanDescriptor> descriptor) { + + if (!descriptor.isInheritanceRoot()) { + return null; + } + + MTable table = new MTable(descriptor.getBaseTable()); + + // add the table to the model + ctx.addTable(table); + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add the discriminator column + String discColumn = inheritInfo.getDiscriminatorColumn(); + DbType dbType = ctx.getDbTypeMap().get(inheritInfo.getDiscriminatorType()); + String discDbType = dbType.renderType(inheritInfo.getDiscriminatorLength(), 0); + + table.addColumn(new MColumn(discColumn, discDbType, true)); + } + + CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints(); + if (compoundUniqueConstraints != null) { + for (int i = 0; i < compoundUniqueConstraints.length; i++) { + table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns()); + } + } + + return new ModelBuildPropertyVisitor(ctx, table); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java new file mode 100644 index 000000000..fbf6dfd23 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -0,0 +1,64 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; + +/** + * The context used during DDL generation. + */ +public class ModelBuildContext { + + /** + * Use platform agnostic logical types. These types are converted to + * platform specific types in the DDL generation. + */ + private final DbTypeMap dbTypeMap = DbTypeMap.logicalTypes(); + + private final ModelContainer model; + + public ModelBuildContext(ModelContainer model) { + this.model = model; + } + + public void addTable(MTable table) { + model.addTable(table); + } + + /** + * Return the map used to determine the DB specific type + * for a given bean property. + */ + public DbTypeMap getDbTypeMap() { + return dbTypeMap; + } + + + public String getColumnDefn(BeanProperty p) { + DbType dbType = getDbType(p); + if (dbType == null) { + throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName()); + } + return p.renderDbType(dbType); + } + + private DbType getDbType(BeanProperty p) { + + if (p.isDbEncrypted()) { + return dbTypeMap.get(p.getDbEncryptedType()); + } + + int dbType = p.getDbType(); + if (dbType == 0) { +// ScalarType scalarType = p.getScalarType(); +// if (scalarType == null) { + throw new RuntimeException("No scalarType for " + p.getFullBeanName()); +// } +// dbType = scalarType.getJdbcType(); + } + return dbTypeMap.get(dbType); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java new file mode 100644 index 000000000..49f153277 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildIntersectionTable.java @@ -0,0 +1,124 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; + + +/** + * Add the intersection table to the model. + */ +public class ModelBuildIntersectionTable { + + private final ModelBuildContext ctx; + + private final BeanPropertyAssocMany> manyProp; + private final TableJoin intersectionTableJoin; + private final TableJoin tableJoin; + + public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany> manyProp) { + this.ctx = ctx; + this.manyProp = manyProp; + this.intersectionTableJoin = manyProp.getIntersectionTableJoin(); + this.tableJoin = manyProp.getTableJoin(); + } + + public void build() { + + MTable table = createTable(); + ctx.addTable(table); + + //buildFkConstraints(); + } + +// private void buildFkConstraints(MTable table) { +// +// BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); +// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true); +// ctx.addIntersectionTableFk(fk1); +// +// BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); +// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false); +// ctx.addIntersectionTableFk(fk2); +// } + + +// private String buildFkConstraints(BeanDescriptor> desc, TableJoinColumn[] columns, boolean direction) { +// +// +// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable(); +// +// fkName = getFkNameWithSuffix(fkName); +// +// fkBuf.append("alter table "); +// fkBuf.append(intersectionTableJoin.getTable()); +// fkBuf.append(" add constraint ").append(fkName); +// +// fkBuf.append(" foreign key ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(") references ").append(desc.getBaseTable()).append(" ("); +// +// for (int i = 0; i < columns.length; i++) { +// if (i > 0) { +// fkBuf.append(", "); +// } +// String col = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn(); +// fkBuf.append(col); +// } +// fkBuf.append(")"); +// +// String fkeySuffix = ctx.getDdlSyntax().getForeignKeySuffix(); +// if (fkeySuffix != null){ +// fkBuf.append(" "); +// fkBuf.append(fkeySuffix); +// } +// fkBuf.append(";").append(NEW_LINE); +// +// return fkBuf.toString(); +// } + + private MTable createTable() { + + BeanDescriptor> localDesc = manyProp.getBeanDescriptor(); + BeanDescriptor> targetDesc = manyProp.getTargetDescriptor(); + + MTable table = new MTable(intersectionTableJoin.getTable()); + + TableJoinColumn[] columns = intersectionTableJoin.columns(); + for (int i = 0; i < columns.length; i++) { + addColumn(table, localDesc, columns[i].getForeignDbColumn(), columns[i].getLocalDbColumn()); + } + + TableJoinColumn[] otherColumns = tableJoin.columns(); + for (int i = 0; i < otherColumns.length; i++) { + addColumn(table, targetDesc, otherColumns[i].getLocalDbColumn(), otherColumns[i].getForeignDbColumn()); + } + + return table; + } + + private void addColumn(MTable table, BeanDescriptor> desc, String column, String findPropColumn) { + + BeanProperty p = desc.getIdBinder().findBeanProperty(findPropColumn); + if (p == null) { + throw new RuntimeException("Could not find id property for " + findPropColumn); + } + + MColumn col = new MColumn(column, ctx.getColumnDefn(p), true); + col.setPrimaryKey(true); + + table.addColumn(col); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java new file mode 100644 index 000000000..444611e29 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -0,0 +1,147 @@ +package com.avaje.ebean.dbmigration.model.build; + +import com.avaje.ebean.dbmigration.model.MCompoundForeignKey; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebean.dbmigration.model.MColumn; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Used as part of ModelBuildBeanVisitor and generally adds the MColumn to the associated + * MTable model objects. + */ +public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { + + private final ModelBuildContext ctx; + + private final MTable table; + + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) { + this.ctx = ctx; + this.table = table; + } + + @Override + public void visitMany(BeanPropertyAssocMany> p) { + if (p.isManyToMany()) { + if (p.getMappedBy() == null) { + // only create on other 'owning' side + + //TableJoin intersectionTableJoin = p.getIntersectionTableJoin(); + // check if the intersection table has already been created + + // build the create table and fkey constraints + // putting the DDL into ctx for later output as we are + // in the middle of rendering the create table DDL + new ModelBuildIntersectionTable(ctx, p).build(); + } + } + } + + @Override + public void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p) { + visitScalar(p); + } + + @Override + public void visitCompound(BeanPropertyCompound p) { + // do nothing + } + + @Override + public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded) { + + //this.embedded = embedded; + visitScalar(p); + } + + @Override + public void visitOneImported(BeanPropertyAssocOne> p) { + + TableJoinColumn[] columns = p.getTableJoin().columns(); + if (columns.length == 0) { + String msg = "No join columns for " + p.getFullBeanName(); + throw new RuntimeException(msg); + } + + ImportedId importedId = p.getImportedId(); + + List modelColumns = new ArrayList(columns.length); + + MCompoundForeignKey compoundKey = null; + if (columns.length > 1) { + // compound foreign key + String refTable = p.getTargetDescriptor().getBaseTable(); + compoundKey = new MCompoundForeignKey(refTable); + table.addForeignKey(compoundKey); + } + + for (int i = 0; i < columns.length; i++) { + + String dbCol = columns[i].getLocalDbColumn(); + BeanProperty importedProperty = importedId.findMatchImport(dbCol); + if (importedProperty == null) { + throw new RuntimeException("Imported BeanProperty not found?"); + } + String columnDefn = ctx.getColumnDefn(importedProperty); + String refColumn = importedProperty.getDbColumn(); + + MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable()); + if (columns.length == 1) { + // single references column (put it on the column) + String refTable = importedProperty.getBeanDescriptor().getBaseTable(); + col.setReferences(refTable + "." + refColumn); + } else { + compoundKey.addColumnPair(dbCol, refColumn); + } + modelColumns.add(col); + table.addColumn(col); + } + + + + if (p.isOneToOne()) { + // Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne + if (modelColumns.size() == 1) { + modelColumns.get(0).setUnique(true); + } else { + table.addCompoundUniqueConstraint(modelColumns); + } + } + } + + @Override + public void visitScalar(BeanProperty p) { + + if (p.isSecondaryTable()) { + return; + } + + MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + + if (p.isId()){ + col.setPrimaryKey(true); + if (p.getBeanDescriptor().isUseIdGenerator()) { + col.setIdentity(true); + } + } else if (!p.isNullable() || p.isDDLNotNull()) { + col.setNotnull(true); + } + + if (p.isUnique() && !p.isId()) { + col.setUnique(true); + } + col.setCheckConstraint(p.getDbConstraintExpression()); + + table.addColumn(col); + } + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java new file mode 100644 index 000000000..c2cc4d7f0 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BaseTablePropertyVisitor.java @@ -0,0 +1,59 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to help mark PropertyVisitor methods that need to be implemented + * to visit base table properties. + */ +public abstract class BaseTablePropertyVisitor implements BeanPropertyVisitor { + + /** + * Not required in that you can use the visitEmbeddedScalar. + */ + public void visitEmbedded(BeanPropertyAssocOne> p) { + } + + /** + * Override this method. + */ + public abstract void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Not part of base table. + */ + public void visitMany(BeanPropertyAssocMany> p) { + } + + /** + * Not part of base table. + */ + public void visitOneExported(BeanPropertyAssocOne> p) { + } + + /** + * Override this method for the foreign key. + */ + public abstract void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Override this method for normal scalar property. + */ + public abstract void visitScalar(BeanProperty p); + + /** + * Not required in that the scalar properties map to the columns. + */ + public void visitCompound(BeanPropertyCompound p) { + } + + /** + * Override this method for scalar property inside a Immutable Compound Value object. + */ + public abstract void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java new file mode 100644 index 000000000..f536d1086 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanPropertyVisitor.java @@ -0,0 +1,53 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; + +/** + * Used to visit a BeanProperty given the type of bean property it is. + */ +public interface BeanPropertyVisitor { + + /** + * Visit a OneToMany or ManyToMany property. + */ + void visitMany(BeanPropertyAssocMany> p); + + /** + * Visit the imported side of a OneToOne property. + */ + void visitOneImported(BeanPropertyAssocOne> p); + + /** + * Visit the exported side of a OneToOne property. + */ + void visitOneExported(BeanPropertyAssocOne> p); + + /** + * Visit an embedded property. + */ + void visitEmbedded(BeanPropertyAssocOne> p); + + /** + * Visit the scalar property of an embedded bean. + */ + void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne> embedded); + + /** + * Visit a scalar property. + */ + void visitScalar(BeanProperty p); + + /** + * Visit a compound value object. + */ + void visitCompound(BeanPropertyCompound p); + + /** + * Visit the scalar value inside a compound value object. + */ + void visitCompoundScalar(BeanPropertyCompound compound, BeanProperty p); + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java new file mode 100644 index 000000000..b140878d6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/BeanVisitor.java @@ -0,0 +1,16 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Visitor pattern for visiting a BeanDescriptor and potentially all its bean + * properties. + */ +public interface BeanVisitor { + + /** + * Visit a BeanDescriptor and return a PropertyVisitor to use to visit each + * property on the entity bean (return null to skip visiting this bean). + */ + BeanPropertyVisitor visitBean(BeanDescriptor> descriptor); +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java new file mode 100644 index 000000000..61f9e3009 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/visitor/VisitAllUsing.java @@ -0,0 +1,158 @@ +package com.avaje.ebean.dbmigration.model.visitor; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor; + +import java.util.List; + +/** + * Makes use of BeanVisitor and PropertyVisitor to navigate BeanDescriptors + * and their properties. + */ +public class VisitAllUsing { + + protected final BeanVisitor visitor; + + protected final List> descriptors; + + /** + * Visit all the descriptors for a given server. + */ + public VisitAllUsing(BeanVisitor visitor, SpiEbeanServer server) { + + this(visitor, server.getBeanDescriptors()); + } + + /** + * Visit all the descriptors in the list. + */ + public VisitAllUsing(BeanVisitor visitor, List> descriptors) { + this.visitor = visitor; + this.descriptors = descriptors; + } + + public void visitAllBeans() { + for (BeanDescriptor> desc : descriptors) { + if (desc.getBaseTable() != null) { + visitBean(desc, visitor); + } + } + } + + /** + * Visit the bean using a visitor. + */ + protected void visitBean(BeanDescriptor> desc, BeanVisitor visitor) { + + BeanPropertyVisitor propertyVisitor = visitor.visitBean(desc); + if (propertyVisitor != null) { + + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(propertyVisitor, idProp); + } + + BeanPropertyAssocOne> unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(propertyVisitor, unidirectional); + } + + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(propertyVisitor, p); + } + } + + visitInheritanceProperties(desc, propertyVisitor); + } + } + + /** + * Visit the property. + */ + protected void visit(BeanPropertyVisitor pv, BeanProperty p) { + + if (p instanceof BeanPropertyAssocMany>) { + // oneToMany or manyToMany + pv.visitMany((BeanPropertyAssocMany>) p); + + } else if (p instanceof BeanPropertyAssocOne>) { + BeanPropertyAssocOne> assocOne = (BeanPropertyAssocOne>) p; + if (assocOne.isEmbedded()) { + // Embedded bean + pv.visitEmbedded(assocOne); + BeanProperty[] embProps = assocOne.getProperties(); + for (int i = 0; i < embProps.length; i++) { + pv.visitEmbeddedScalar(embProps[i], assocOne); + } + + } else if (assocOne.isOneToOneExported()) { + // associated one exported + pv.visitOneExported(assocOne); + + } else { + // associated one imported + pv.visitOneImported(assocOne); + } + + } else if (p instanceof BeanPropertyCompound) { + // compound type + BeanPropertyCompound compound = (BeanPropertyCompound) p; + pv.visitCompound(compound); + + BeanProperty[] properties = compound.getScalarProperties(); + for (int i = 0; i < properties.length; i++) { + pv.visitCompoundScalar(compound, properties[i]); + } + + } else { + // simple scalar type + pv.visitScalar(p); + } + } + + + /** + * Visit all the other inheritance properties that are not on the root. + */ + protected void visitInheritanceProperties(BeanDescriptor> descriptor, BeanPropertyVisitor pv) { + + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && inheritInfo.isRoot()) { + // add all properties on the children objects + InheritChildVisitor childVisitor = new InheritChildVisitor(this, pv); + inheritInfo.visitChildren(childVisitor); + } + } + + + /** + * Helper used to visit all the inheritInfo/BeanDescriptor in + * the inheritance hierarchy (to add their 'local' properties). + */ + protected class InheritChildVisitor implements InheritInfoVisitor { + + private final VisitAllUsing owner; + private final BeanPropertyVisitor pv; + + protected InheritChildVisitor(VisitAllUsing owner, BeanPropertyVisitor pv) { + this.owner = owner; + this.pv = pv; + } + + public void visit(InheritInfo inheritInfo) { + BeanProperty[] propertiesLocal = inheritInfo.getBeanDescriptor().propertiesLocal(); + for (int i = 0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java new file mode 100644 index 000000000..2dc801c77 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl; +import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseDdlHandlerTest extends BaseTestCase { + + + private BaseDdlHandler h2Handler() { + DbTypeMap types = new H2Platform().getDbTypeMap(); + return new BaseDdlHandler(new DdlNamingConvention(), new H2Ddl(types)); + } + + private BaseDdlHandler postgresHandler() { + DbTypeMap pgTypes = new PostgresPlatform().getDbTypeMap(); + PostgresDdl pgDdl = new PostgresDdl(pgTypes); + return new BaseDdlHandler(new DdlNamingConvention(), pgDdl); + } + + @Test + public void addColumn_nullable_noConstraint() throws Exception { + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, Helper.getAddColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add column added_to_foo varchar(20);\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo("alter table foo drop column added_to_foo;\n\n"); + } + + @Test + public void dropColumn() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getDropColumn()); + + assertThat(write.apply().getBuffer()).isEqualTo("alter table foo drop column col2;\n\n"); + assertThat(write.rollback().getBuffer()).isEqualTo(""); + } + + + @Test + public void createTable() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getCreateTable()); + + String createTableDDL = Helper.asText(this, "/assert/create-table.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(createTableDDL); + assertThat(write.rollback().getBuffer()).isEqualTo("drop table foo;\n\n"); + } + + @Test + public void generateChangeSet() throws Exception { + + DdlWrite write = new DdlWrite(); + BaseDdlHandler handler = h2Handler(); + + handler.generate(write, Helper.getChangeSet()); + + String apply = Helper.asText(this, "/assert/BaseDdlHandlerTest/apply.sql"); + String rollbackLast = Helper.asText(this, "/assert/BaseDdlHandlerTest/rollback.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + + @Test + public void generateChangeSetFromModel() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = h2Handler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-apply.txt"); + String rollbackLast = Helper.asText(this, "/assert/changeset-rollback.txt"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + + @Test + public void generateChangeSetFromModel_given_postgresTypes() throws Exception { + + SpiEbeanServer defaultServer = (SpiEbeanServer) Ebean.getDefaultServer(); + + + ChangeSet createChangeSet = new CurrentModel(defaultServer).getChangeSet(); + + DdlWrite write = new DdlWrite(); + + BaseDdlHandler handler = postgresHandler(); + handler.generate(write, createChangeSet); + + String apply = Helper.asText(this, "/assert/changeset-pg-apply.sql"); + String applyLast = Helper.asText(this, "/assert/changeset-pg-applyLast.sql"); + String rollbackFirst = Helper.asText(this, "/assert/changeset-pg-rollbackFirst.sql"); + String rollbackLast = Helper.asText(this, "/assert/changeset-pg-rollbackLast.sql"); + + assertThat(write.apply().getBuffer()).isEqualTo(apply); + assertThat(write.applyForeignKeys().getBuffer()).isEqualTo(applyLast); + assertThat(write.rollbackForeignKeys().getBuffer()).isEqualTo(rollbackFirst); + assertThat(write.rollback().getBuffer()).isEqualTo(rollbackLast); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java new file mode 100644 index 000000000..f1616c2ea --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/Helper.java @@ -0,0 +1,76 @@ +package com.avaje.ebean.dbmigration.ddlgeneration; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.List; + +/** + * Helper for testing to return some basic migration objects. + */ +public class Helper { + + static Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); + + static ChangeSet changeSet; + + static List changeSetChildren; + + static { + List changeSets = migration.getChangeSet(); + changeSet = changeSets.get(0); + changeSetChildren = changeSet.getChangeSetChildren(); + } + + public static ChangeSet getChangeSet() { + return changeSet; + } + + public static CreateTable getCreateTable() { + return (CreateTable) changeSetChildren.get(0); + } + + public static AddColumn getAddColumn() { + return (AddColumn) changeSetChildren.get(1); + } + + public static DropColumn getDropColumn() { + return (DropColumn) changeSetChildren.get(2); + } + + public static String asText(Object instance, String relativePath) throws IOException { + InputStream is = instance.getClass().getResourceAsStream(relativePath); + if (is == null) { + throw new IllegalArgumentException("resource "+relativePath+" not found"); + } + return asText(is); + } + + public static String asText(InputStream in) throws IOException { + + try { + InputStreamReader reader = new InputStreamReader(in); + + LineNumberReader lineNumberReader = new LineNumberReader(reader); + + StringBuilder builder = new StringBuilder(400); + String line; + while ((line = lineNumberReader.readLine()) != null) { + builder.append(line).append("\n"); + } + return builder.toString(); + + } finally { + in.close(); + } + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java new file mode 100644 index 000000000..f0220f62b --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdlTest.java @@ -0,0 +1,70 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.ddlgeneration.Helper; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class BaseTableDdlTest { + + @Test + public void testGenerate() throws Exception { + + BaseTableDdl ddlGen = new BaseTableDdl(new DdlNamingConvention(), new H2Ddl(new DbTypeMap())); + + DdlWrite write = new DdlWrite(); + + + ddlGen.generate(write, createTable()); + String apply = write.apply().getBuffer(); + String applyLast = write.applyForeignKeys().getBuffer(); + + String rollbackFirst = write.rollbackForeignKeys().getBuffer(); + String rollbackLast = write.rollback().getBuffer(); + + assertThat(apply).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-apply.txt")); + assertThat(applyLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-applyLast.txt")); + assertThat(rollbackFirst).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollbackFirst.txt")); + assertThat(rollbackLast).isEqualTo( Helper.asText(this, "/assert/BaseTableDdlTest/createTable-rollback.txt")); + + + } + + private CreateTable createTable() { + CreateTable createTable = new CreateTable(); + createTable.setName("mytable"); + List columns = createTable.getColumn(); + Column col = new Column(); + col.setName("id"); + col.setType("integer"); + col.setPrimaryKey(true); + + columns.add(col); + + Column col2 = new Column(); + col2.setName("status"); + col2.setType("varchar(1)"); + col2.setNotnull(true); + col2.setCheckConstraint("check (status in ('A','B'))"); + + columns.add(col2); + + Column col3 = new Column(); + col3.setName("order_id"); + col3.setType("integer"); + col3.setNotnull(true); + col3.setReferences("orders.id"); + + columns.add(col3); + + return createTable; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java new file mode 100644 index 000000000..fbcd78832 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbNameNormaliseTest.java @@ -0,0 +1,29 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class DbNameNormaliseTest { + + DbNameNormalise normalise = new DbNameNormalise(); + + @Test + public void testNormalise() throws Exception { + + assertThat(normalise.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(normalise.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + + @Test + public void testTrimQuotes() throws Exception { + + assertThat(normalise.trimQuotes("[foo]")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("'foo'")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("\"foo\"")).isEqualTo("foo"); + assertThat(normalise.trimQuotes("`foo`")).isEqualTo("foo"); + + assertThat(normalise.trimQuotes("`fo_o`")).isEqualTo("fo_o"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java new file mode 100644 index 000000000..a9f8a7b59 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DdlNamingConventionTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DdlNamingConventionTest { + + DdlNamingConvention defaultNaming = new DdlNamingConvention(); + + @Test + public void testPrimaryKeyName() throws Exception { + + List cols = new ArrayList(); + cols.add("[jim]"); + cols.add("`jack`"); + assertThat(defaultNaming.primaryKeyName("[cat].[sce].[foo_bar]", cols)).isEqualTo("pk_foo_bar"); + } + + @Test + public void testUniqueConstraintName() throws Exception { + + assertThat(defaultNaming.uniqueConstraintName("[foo_bar]", "[jim]")).isEqualTo("uq_foo_bar_jim"); + } + + @Test + public void testCheckConstraintName() throws Exception { + + assertThat(defaultNaming.checkConstraintName("[foo_bar]", "[jim]")).isEqualTo("ck_foo_bar_jim"); + } + + @Test + public void testNormalise() throws Exception { + + assertThat(defaultNaming.normalise("cat.sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("sch.foo_bar]")).isEqualTo("foo_bar"); + assertThat(defaultNaming.normalise("foo_bar]")).isEqualTo("foo_bar"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java new file mode 100644 index 000000000..efbd47c4f --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformTypeConverterTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebean.dbmigration.ddlgeneration.platform; + +import com.avaje.ebean.config.dbplatform.DbTypeMap; +import com.avaje.ebean.config.dbplatform.H2Platform; +import com.avaje.ebean.config.dbplatform.PostgresPlatform; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PlatformTypeConverterTest { + + @Test + public void testConvert_given_postgres() throws Exception { + + PostgresPlatform pg = new PostgresPlatform(); + DbTypeMap dbTypeMap = pg.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("text"); + assertThat(converter.convert("blob")).isEqualTo("bytea"); + assertThat(converter.convert("tinyint")).isEqualTo("smallint"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + assertThat(converter.convert("integer(8)")).isEqualTo("integer"); // removes scale + + assertThat(converter.convert("funky(")).isEqualTo("funky("); + assertThat(converter.convert("funky()")).isEqualTo("funky()"); + assertThat(converter.convert("funky)")).isEqualTo("funky)"); + } + + @Test + public void testConvert_given_h2() throws Exception { + + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("varchar(10)")).isEqualTo("varchar(10)"); + assertThat(converter.convert("decimal(10,2)")).isEqualTo("decimal(10,2)"); + assertThat(converter.convert("clob")).isEqualTo("clob"); + assertThat(converter.convert("blob")).isEqualTo("blob"); + assertThat(converter.convert("tinyint")).isEqualTo("tinyint"); + assertThat(converter.convert("integer(8)")).isEqualTo("integer(8)"); + assertThat(converter.convert("funky")).isEqualTo("funky"); // unknown + } + + @Test + public void testConvertJsonTypes_given_postgres() { + + PostgresPlatform platform = new PostgresPlatform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("bytea"); + assertThat(converter.convert("jsonclob")).isEqualTo("text"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("json"); + assertThat(converter.convert("jsonb")).isEqualTo("jsonb"); + } + + @Test + public void testConvertJsonTypes_given_h2() { + + H2Platform platform = new H2Platform(); + DbTypeMap dbTypeMap = platform.getDbTypeMap(); + + PlatformTypeConverter converter = new PlatformTypeConverter(dbTypeMap); + + assertThat(converter.convert("jsonblob")).isEqualTo("blob"); + assertThat(converter.convert("jsonclob")).isEqualTo("clob"); + assertThat(converter.convert("jsonvarchar(200)")).isEqualTo("varchar(200)"); + assertThat(converter.convert("json")).isEqualTo("clob"); + assertThat(converter.convert("jsonb")).isEqualTo("clob"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java new file mode 100644 index 000000000..9ce0538d5 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -0,0 +1,26 @@ +package com.avaje.ebean.dbmigration.migrationreader; + +import com.avaje.ebean.dbmigration.migration.Migration; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class MigrationXmlWriterTest { + + Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); + + @Test + public void testWrite() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + File temp = File.createTempFile("migrationWrite",".xml"); + MigrationXmlWriter writer = new MigrationXmlWriter(); + writer.write(migration, temp); + + logger.info("wrote migration file: "+temp.getAbsolutePath()); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java new file mode 100644 index 000000000..c15a7db50 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -0,0 +1,41 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.CreateTable; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelContainerApplyTest { + + @Test + public void testApply() throws Exception { + + Migration migration = MigrationXmlReader.read("/test/container/test-create-table.xml"); + + List changeSets = migration.getChangeSet(); + ChangeSet changeSet = changeSets.get(0); + + List changeSetChildren = changeSet.getChangeSetChildren(); + assertThat(changeSetChildren).hasSize(3); + assertThat(changeSetChildren.get(0)).isInstanceOf(CreateTable.class); + assertThat(changeSetChildren.get(1)).isInstanceOf(AddColumn.class); + assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); + + ModelContainer model = new ModelContainer(); + model.apply(migration); + + MTable foo = model.getTable("foo"); + assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getTablespace()).isEqualTo("fooSpace"); + assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); + assertThat(foo.getWithHistory()).isEqualTo(true); + assertThat(foo.getColumns()).containsKeys("col1", "col3", "added_to_foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java new file mode 100644 index 000000000..96a5305de --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitorTest.java @@ -0,0 +1,33 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebean.dbmigration.model.visitor.VisitAllUsing; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuildBeanVisitorTest extends BaseTestCase { + + @Test + public void test() { + + SpiEbeanServer defaultServer = (SpiEbeanServer)Ebean.getDefaultServer(); + + ModelContainer model = new ModelContainer(); + + ModelBuildContext ctx = new ModelBuildContext(model); + ModelBuildBeanVisitor addTable = new ModelBuildBeanVisitor(ctx); + + new VisitAllUsing(addTable, defaultServer).visitAllBeans(); + + MTable customer = model.getTable("o_customer"); + + assertThat(customer).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java new file mode 100644 index 000000000..ad63ef2bc --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/build/ModelBuild_compoundKeyTest.java @@ -0,0 +1,51 @@ +package com.avaje.ebean.dbmigration.model.build; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.dbmigration.model.ModelContainer; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.CKeyAssoc; +import com.avaje.tests.model.basic.CKeyDetail; +import com.avaje.tests.model.basic.CKeyParent; +import com.avaje.tests.model.basic.CKeyParentId; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ModelBuild_compoundKeyTest extends BaseTestCase { + + @Test + public void test() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("h2other"); + config.setDdlGenerate(false); + config.setDdlRun(false); + config.setDefaultServer(false); + config.setRegister(false); + + config.addClass(CKeyDetail.class); + config.addClass(CKeyParent.class); + config.addClass(CKeyAssoc.class); + config.addClass(CKeyParentId.class); + + + SpiEbeanServer ebeanServer = (SpiEbeanServer)EbeanServerFactory.create(config); + + CurrentModel currentModel = new CurrentModel(ebeanServer); + ModelContainer model = currentModel.read(); + + MTable parent = model.getTable("ckey_parent"); + MTable detail = model.getTable("ckey_detail"); + + assertThat(parent).isNotNull(); + assertThat(detail).isNotNull(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java index 951058f21..d1b2d9518 100644 --- a/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java +++ b/src/test/java/com/avaje/tests/model/basic/CKeyAssoc.java @@ -4,7 +4,7 @@ import javax.persistence.Entity; import javax.persistence.Id; @Entity -public class CKeyAssoc { +public class CKeyAssoc { @Id Integer id; diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt new file mode 100644 index 000000000..549c29a6a --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-apply.txt @@ -0,0 +1,8 @@ +create table mytable ( + id integer not null, + status varchar(1) not null, + order_id integer not null, + constraint ck_mytable_status check (status in ('A','B')), + constraint pk_mytable primary key (id) +); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt new file mode 100644 index 000000000..1f68497c3 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-applyLast.txt @@ -0,0 +1,3 @@ +alter table mytable add constraint fk_mytable_order_id foreign key (order_id) references orders (id) on delete restrict on update restrict; +create index ix_mytable_order_id on mytable (order_id); + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt new file mode 100644 index 000000000..3be9f6ed5 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollback.txt @@ -0,0 +1,2 @@ +drop table mytable; + diff --git a/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt new file mode 100644 index 000000000..a6c9768a1 --- /dev/null +++ b/src/test/resources/assert/BaseTableDdlTest/createTable-rollbackFirst.txt @@ -0,0 +1,3 @@ +drop index ix_mytable_order_id; +alter table mytable drop constraint fk_mytable_order_id; + diff --git a/src/test/resources/assert/changeset-apply.txt b/src/test/resources/assert/changeset-apply.txt new file mode 100644 index 000000000..a5439ec18 --- /dev/null +++ b/src/test/resources/assert/changeset-apply.txt @@ -0,0 +1,182 @@ +create table o_address ( + id bigint not null, + line1 varchar(100), + line2 varchar(100), + city varchar(100), + country_code varchar(2), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_address primary key (id) +); + +create table blob_holder ( + id bigint not null, + text_content clob, + binary_content blob, + file_content blob, + version bigint not null, + constraint pk_blob_holder primary key (id) +); + +create table car_accessory ( + id bigint not null, + name varchar(255), + car_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_car_accessory primary key (id) +); + +create table be_contact ( + id bigint not null, + first_name varchar(50), + last_name varchar(50), + email varchar(200), + phone varchar(20), + customer_id bigint not null, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_be_contact primary key (id) +); + +create table contact_note ( + id bigint not null, + contact_id bigint not null, + title varchar(255), + note clob, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_contact_note primary key (id) +); + +create table o_country ( + code varchar(2) not null, + name varchar(60), + constraint pk_o_country primary key (code) +); + +create table be_customer ( + id bigint not null, + inactive boolean, + name varchar(100), + registered timestamp, + comments varchar(1000), + billing_address_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint uq_be_customer_name unique (name), + constraint pk_be_customer primary key (id) +); + +create table document ( + id bigint not null, + title varchar(255), + author varchar(255), + content clob, + version bigint not null, + constraint pk_document primary key (id) +); + +create table mrole ( + id integer not null, + role_name varchar(255), + constraint pk_mrole primary key (id) +); + +create table mrole_muser ( + mrole_id integer not null, + muser_id integer not null, + constraint pk_mrole_muser primary key (mrole_id,muser_id) +); + +create table muser ( + id integer not null, + user_name varchar(255), + user_type_id integer, + constraint pk_muser primary key (id) +); + +create table muser_type ( + id integer not null, + name varchar(255), + constraint pk_muser_type primary key (id) +); + +create table o_booking ( + id bigint not null, + booking_date date, + invoice_id bigint, + constraint uq_o_booking_invoice_id unique (invoice_id), + constraint pk_o_booking primary key (id) +); + +create table o_invoice ( + id bigint not null, + invoice_date date, + constraint pk_o_invoice primary key (id) +); + +create table o_order ( + id bigint not null, + status varchar(1), + order_date date, + ship_date date, + customer_id bigint, + shipping_address_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint ck_o_order_status check (status in ('N','C','A','S')), + constraint pk_o_order primary key (id) +); + +create table o_order_detail ( + id bigint not null, + order_id bigint, + order_qty integer, + ship_qty integer, + unit_price double, + product_id bigint, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_order_detail primary key (id) +); + +create table o_product ( + id bigint not null, + sku varchar(20), + name varchar(255), + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + constraint pk_o_product primary key (id) +); + +create table truck_ref ( + id integer not null, + something varchar(255), + constraint pk_truck_ref primary key (id) +); + +create table vehicle ( + dtype varchar(3) not null, + id bigint not null, + license_number varchar(255), + registration_date timestamp, + version bigint not null, + when_created timestamp not null, + when_modified timestamp not null, + truck_ref_id integer, + capacity double, + driver varchar(255), + car_ref_id integer, + constraint pk_vehicle primary key (id) +); + diff --git a/src/test/resources/assert/changeset-rollback.txt b/src/test/resources/assert/changeset-rollback.txt new file mode 100644 index 000000000..42b1af137 --- /dev/null +++ b/src/test/resources/assert/changeset-rollback.txt @@ -0,0 +1,38 @@ +drop table o_address; + +drop table blob_holder; + +drop table car_accessory; + +drop table be_contact; + +drop table contact_note; + +drop table o_country; + +drop table be_customer; + +drop table document; + +drop table mrole; + +drop table mrole_muser; + +drop table muser; + +drop table muser_type; + +drop table o_booking; + +drop table o_invoice; + +drop table o_order; + +drop table o_order_detail; + +drop table o_product; + +drop table truck_ref; + +drop table vehicle; + diff --git a/src/test/resources/assert/create-table.txt b/src/test/resources/assert/create-table.txt new file mode 100644 index 000000000..4c59155ef --- /dev/null +++ b/src/test/resources/assert/create-table.txt @@ -0,0 +1,7 @@ +create table foo ( + col1 varchar(4) not null, + col2 varchar(30) not null, + col3 varchar(30) not null, + constraint pk_foo primary key (col1) +); + diff --git a/src/test/resources/container/test-compoundkey.xml b/src/test/resources/container/test-compoundkey.xml new file mode 100644 index 000000000..c424ba941 --- /dev/null +++ b/src/test/resources/container/test-compoundkey.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/container/test-create-table.xml b/src/test/resources/container/test-create-table.xml new file mode 100644 index 000000000..d1f2a57ee --- /dev/null +++ b/src/test/resources/container/test-create-table.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-1.xml b/src/test/resources/dbmigration/app1/migration-1.xml new file mode 100644 index 000000000..dab87837e --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-1.xml @@ -0,0 +1,55 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/migration-2.xml b/src/test/resources/dbmigration/app1/migration-2.xml new file mode 100644 index 000000000..e5c9d91c5 --- /dev/null +++ b/src/test/resources/dbmigration/app1/migration-2.xml @@ -0,0 +1,51 @@ + + + + + + + + create table foo; + + + drop table if exists foo; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml new file mode 100644 index 000000000..9c01f1b8f --- /dev/null +++ b/src/test/resources/dbmigration/applications.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file
An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.avaje.ebean.dbmigration.migration + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link Rollback } + * + */ + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); + } + + /** + * Create an instance of {@link Column } + * + */ + public Column createColumn() { + return new Column(); + } + + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + + /** + * Create an instance of {@link UniqueConstraint } + * + */ + public UniqueConstraint createUniqueConstraint() { + return new UniqueConstraint(); + } + + /** + * Create an instance of {@link DropForeignKey } + * + */ + public DropForeignKey createDropForeignKey() { + return new DropForeignKey(); + } + + /** + * Create an instance of {@link Apply } + * + */ + public Apply createApply() { + return new Apply(); + } + + /** + * Create an instance of {@link Configuration } + * + */ + public Configuration createConfiguration() { + return new Configuration(); + } + + /** + * Create an instance of {@link DefaultTablespace } + * + */ + public DefaultTablespace createDefaultTablespace() { + return new DefaultTablespace(); + } + + /** + * Create an instance of {@link RenameTable } + * + */ + public RenameTable createRenameTable() { + return new RenameTable(); + } + + /** + * Create an instance of {@link DropHistoryTable } + * + */ + public DropHistoryTable createDropHistoryTable() { + return new DropHistoryTable(); + } + + /** + * Create an instance of {@link RenameView } + * + */ + public RenameView createRenameView() { + return new RenameView(); + } + + /** + * Create an instance of {@link AddForeignKey } + * + */ + public AddForeignKey createAddForeignKey() { + return new AddForeignKey(); + } + + /** + * Create an instance of {@link DropColumn } + * + */ + public DropColumn createDropColumn() { + return new DropColumn(); + } + + /** + * Create an instance of {@link DropView } + * + */ + public DropView createDropView() { + return new DropView(); + } + + /** + * Create an instance of {@link ChangeSet } + * + */ + public ChangeSet createChangeSet() { + return new ChangeSet(); + } + + /** + * Create an instance of {@link Sql } + * + */ + public Sql createSql() { + return new Sql(); + } + + /** + * Create an instance of {@link DropTable } + * + */ + public DropTable createDropTable() { + return new DropTable(); + } + + /** + * Create an instance of {@link CreateHistoryTable } + * + */ + public CreateHistoryTable createCreateHistoryTable() { + return new CreateHistoryTable(); + } + + /** + * Create an instance of {@link CreateView } + * + */ + public CreateView createCreateView() { + return new CreateView(); + } + + /** + * Create an instance of {@link RenameColumn } + * + */ + public RenameColumn createRenameColumn() { + return new RenameColumn(); + } + + /** + * Create an instance of {@link Application } + * + */ + public Application createApplication() { + return new Application(); + } + + /** + * Create an instance of {@link Migration } + * + */ + public Migration createMigration() { + return new Migration(); + } + + /** + * Create an instance of {@link Applications } + * + */ + public Applications createApplications() { + return new Applications(); + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java new file mode 100644 index 000000000..00e1b30f4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/RenameColumn.java @@ -0,0 +1,141 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="dataType" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="oldName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="newName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <sequence> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}apply"/> + * <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}rollback"/> + * </sequence> + * </restriction> + * </complexContent> + * </complexType> + *
+ * <complexType> + * <complexContent> + * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" /> + * </restriction> + * </complexContent> + * </complexType> + *
+ * Note that unique constraint on a single column is instead + * a boolean flag on the associated MColumn. + *
+ * It is expected that these are applied in the correct chronological order + * from earliest to latest. + *
+ * This effectively represents a table, its columns and all associated + * constraints, foreign keys and indexes. + *
+ * Migrations can be applied to this such that it represents the state + * of a given table after various migrations have been applied. + *
+ * This table model can also be derived from the EbeanServer bean descriptor + * and associated properties. + *
+ * Migration changeSets can be applied to the model. + *
+ * This creates an MTable and adds it to the model. + *