DbMigration update - constraints up front

This commit is contained in:
Robin Bygrave
2015-08-11 08:25:29 +12:00
parent 845faf74ff
commit 851f40e7b0
72 changed files with 4045 additions and 3347 deletions
@@ -1,12 +1,10 @@
package com.avaje.ebean.config;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides some base implementation for NamingConventions.
*
@@ -14,28 +12,39 @@ import org.slf4j.LoggerFactory;
*/
public abstract class AbstractNamingConvention implements NamingConvention {
/** The Constant logger. */
private static final Logger logger = LoggerFactory.getLogger(AbstractNamingConvention.class);
/** The Constant DEFAULT_SEQ_FORMAT. */
/**
* The Constant DEFAULT_SEQ_FORMAT.
*/
public static final String DEFAULT_SEQ_FORMAT = "{table}_seq";
/** Sequence Format that includes the Primary Key column */
/**
* Sequence Format that includes the Primary Key column
*/
public static final String TABLE_PKCOLUMN_SEQ_FORMAT = "{table}_{column}_seq";
/** The catalog. */
/**
* The catalog.
*/
private String catalog;
/** The schema. */
/**
* The schema.
*/
private String schema;
/** The sequence format. */
/**
* The sequence format.
*/
private String sequenceFormat;
/** The database platform. */
/**
* The database platform.
*/
protected DatabasePlatform databasePlatform;
/** Used to trim off extra prefix for M2M. */
/**
* Used to trim off extra prefix for M2M.
*/
protected int rhsPrefixLength = 3;
protected boolean useForeignKeyPrefix;
@@ -59,8 +68,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
}
/**
* Construct with the default sequence format ("{table}_seq") and
* useForeignKeyPrefix as true.
* Construct with the default sequence format ("{table}_seq") and useForeignKeyPrefix as true.
*/
public AbstractNamingConvention() {
this(DEFAULT_SEQ_FORMAT);
@@ -233,7 +241,7 @@ public abstract class AbstractNamingConvention implements NamingConvention {
if (t != null && !isEmpty(t.name())) {
// Note: empty catalog and schema are converted to null
// Only need to convert quoted identifiers from annotations
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
return new TableName(quoteIdentifiers(t.catalog()), quoteIdentifiers(t.schema()), quoteIdentifiers(t.name()));
}
// No annotation
@@ -0,0 +1,136 @@
package com.avaje.ebean.config;
/**
* Naming convention used for constraint names.
* <p>
* Note that these constraint names are trimmed in the PlatformDdl which can be overridden
* but provides a decent default implementation.
* </p>
*/
public class DbConstraintNaming {
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 boolean lowerCaseNames = true;
protected DbConstraintNormalise normalise = new DbConstraintNormalise();
public DbConstraintNaming() {
}
/**
* Return the primary key constraint name.
*/
public String primaryKeyName(String tableName) {
return pkPrefix + normaliseTable(tableName) + pkSuffix;
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
public String foreignKeyConstraintName(String tableName, String columnName) {
return fkPrefix + normaliseTable(tableName) + fkMiddle + normaliseColumn(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 colPart = joinColumnNames(columns);
return fkIndexPrefix + normaliseTable(tableName) + fkIndexMiddle + colPart + fkIndexSuffix;
}
public String foreignKeyIndexName(String tableName, String column) {
String colPart = normaliseTable(column);
return fkIndexPrefix + normaliseTable(tableName) + fkIndexMiddle + colPart + fkIndexSuffix;
}
/**
* Join the column names together with underscores.
*/
protected String joinColumnNames(String[] columns) {
if (columns.length == 1) {
return normaliseColumn(columns[0]);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append("_");
}
sb.append(normaliseColumn(columns[i]));
}
return sb.toString();
}
/**
* Return the unique constraint name.
*/
public String uniqueConstraintName(String tableName, String columnName) {
return uqPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + uqSuffix;
}
/**
* Return the unique constraint name.
*/
public String uniqueConstraintName(String tableName, String[] columns) {
String colPart = joinColumnNames(columns);
return uqPrefix + normaliseTable(tableName) + "_" + colPart + uqSuffix;
}
/**
* Return the check constraint name.
*/
public String checkConstraintName(String tableName, String columnName) {
return ckPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + ckSuffix;
}
/**
* Normalise the table name by trimming catalog and schema and removing any
* quoted identifier characters (",',[,] etc).
*/
public String normaliseTable(String tableName) {
return normalise.normaliseTable(tableName);
}
/**
* Normalise the column name by removing any quoted identifier characters (",',[,] etc).
*/
public String normaliseColumn(String tableName) {
return normalise.normaliseColumn(tableName);
}
/**
* Lower case the table or column name checking for quoted identifiers.
*/
public String lowerName(String tableName) {
if (lowerCaseNames && normalise.notQuoted(tableName)) {
return tableName.toLowerCase();
}
return tableName;
}
}
@@ -1,24 +1,19 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.DbQuotes;
package com.avaje.ebean.config;
/**
* Used to normalise table and column names which means stripping out
* quoted identifier characters and any catalog or schema prefix.
*/
public class DdlNameNormalise {
public class DbConstraintNormalise {
protected final String[] quotedIdentifiers;
protected boolean lowerCaseTables = true;
protected boolean lowerCaseColumns = true;
protected DbQuotes quotes = new DbQuotes();
public DdlNameNormalise() {
}
public boolean notQuoted(String tableName) {
return quotes.notQuoted(tableName);
public DbConstraintNormalise() {
this.quotedIdentifiers = new String[]{"\"", "'", "[", "]", "`"};
}
/**
@@ -54,9 +49,31 @@ public class DdlNameNormalise {
/**
* Trim off the platform quoted identifier quotes like [ ' and ".
*/
protected String trimQuotes(String tableName) {
public boolean notQuoted(String tableName) {
return quotes.trimQuotes(tableName);
// remove quoted identifier characters
for (int i = 0; i < quotedIdentifiers.length; i++) {
if (tableName.contains(quotedIdentifiers[i])) {
return false;
}
}
return true;
}
/**
* Trim off the platform quoted identifier quotes like [ ' and ".
*/
public String trimQuotes(String tableName) {
if (tableName == null) {
return "";
}
// remove quoted identifier characters
for (int i = 0; i < quotedIdentifiers.length; i++) {
tableName = tableName.replace(quotedIdentifiers[i], "");
}
return tableName;
}
}
@@ -82,8 +82,7 @@ public interface NamingConvention {
String getPropertyFromColumn(Class<?> beanClass, String dbColumnName);
/**
* Return the sequence name given the table name (for DB's that use
* sequences).
* Return the sequence name given the table name (for DB's that use sequences).
* <p>
* Typically you might append "_seq" to the table name as an example.
* </p>
@@ -99,14 +98,14 @@ public interface NamingConvention {
* Return true if a prefix should be used building a foreign key name.
* <p>
* This by default is true and this works well when the primary key column
* names are simply "ID". In this case a prefix (such as "ORDER" and
* "CUSTOMER" etc) is added to the foreign key column producing "ORDER_ID" and
* "CUSTOMER_ID".
* names are simply "id". In this case a prefix (such as "order" and
* "customer" etc) is added to the foreign key column producing "order_id" and
* "customer_id".
* </p>
* <p>
* This should return false when your primary key columns are the same as the
* foreign key columns. For example, when the primary key columns are
* "ORDER_ID", "CUST_ID" etc ... and they are the same as the foreign key
* "order_id", "cust_id" etc ... and they are the same as the foreign key
* column names.
* </p>
*/
@@ -239,6 +239,11 @@ public class ServerConfig {
*/
private NamingConvention namingConvention = new UnderscoreNamingConvention();
/**
* Naming convention used in DDL generation for primary keys, foreign keys etc.
*/
private DbConstraintNaming constraintNaming = new DbConstraintNaming();
/**
* Behaviour of update to include on the change properties.
*/
@@ -975,6 +980,20 @@ public class ServerConfig {
this.namingConvention = namingConvention;
}
/**
* Return the constraint naming convention used in DDL generation.
*/
public DbConstraintNaming getConstraintNaming() {
return constraintNaming;
}
/**
* Set the constraint naming convention used in DDL generation.
*/
public void setConstraintNaming(DbConstraintNaming constraintNaming) {
this.constraintNaming = constraintNaming;
}
/**
* Return the configuration for the Autofetch feature.
*/
@@ -3,23 +3,26 @@ package com.avaje.ebean.config;
/**
* Converts between Camel Case and Underscore based names for both table and
* column names (and is the default naming convention in Ebean).
*
*
* @author emcgreal
* @author rbygrave
*/
public class UnderscoreNamingConvention extends AbstractNamingConvention {
/** Force toUnderscore to return in upper case. */
/**
* Force toUnderscore to return in upper case.
*/
private boolean forceUpperCase = false;
/** The digits compressed. */
/**
* The digits compressed.
*/
private boolean digitsCompressed = true;
/**
* Create with a given sequence format.
*
* @param sequenceFormat
* the sequence format
*
* @param sequenceFormat the sequence format
*/
public UnderscoreNamingConvention(String sequenceFormat) {
super(sequenceFormat);
@@ -34,39 +37,30 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
/**
* Returns the last part of the class name.
*
* @param beanClass
* the bean class
*
*
* @param beanClass the bean class
* @return the table name from class
*/
public TableName getTableNameByConvention(Class<?> beanClass) {
return new TableName(getCatalog(),
getSchema(),
toUnderscoreFromCamel(beanClass.getSimpleName()));
return new TableName(getCatalog(), getSchema(), toUnderscoreFromCamel(beanClass.getSimpleName()));
}
/**
* Converts Camel case property name to underscore based column name.
*
*
* @return the column from property
*/
public String getColumnFromProperty(Class<?> beanClass, String propertyName) {// Field
// field)
// {
public String getColumnFromProperty(Class<?> beanClass, String propertyName) {
return toUnderscoreFromCamel(propertyName);
}
/**
* Converts underscore based column name to Camel case property name.
*
* @param beanClass
* the bean class
* @param dbColumnName
* the db column name
*
*
* @param beanClass the bean class
* @param dbColumnName the db column name
* @return the property from column
*/
public String getPropertyFromColumn(Class<?> beanClass, String dbColumnName) {
@@ -110,7 +104,7 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
protected String toUnderscoreFromCamel(String camelCase) {
int lastUpper = -1;
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder(camelCase.length()+4);
for (int i = 0; i < camelCase.length(); i++) {
char c = camelCase.charAt(i);
@@ -144,16 +138,11 @@ public class UnderscoreNamingConvention extends AbstractNamingConvention {
}
/**
* To camel from underscore.
*
* @param underscore
* the underscore
*
* @return the string
* Convert and return the from string from underscore to camel case.
*/
protected String toCamelFromUnderscore(String underscore) {
StringBuilder result = new StringBuilder();
StringBuilder result = new StringBuilder(underscore.length());
String[] vals = underscore.split("_");
for (int i = 0; i < vals.length; i++) {
@@ -15,6 +15,7 @@ public class DB2Platform extends DatabasePlatform {
public DB2Platform() {
super();
this.name = "db2";
this.maxTableNameLength = 18;
this.sqlLimiter = new Db2SqlLimiter();
this.platformDdl = new DB2Ddl(dbTypeMap, dbIdentity);
@@ -7,6 +7,7 @@ import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import org.slf4j.Logger;
@@ -145,6 +146,12 @@ public class DatabasePlatform {
protected PlatformDdl platformDdl;
/**
* The maximum length of table names - used specifically when derived
* default table names for intersection tables.
*/
protected int maxTableNameLength = 60;
/**
* Instantiates a new database platform.
*/
@@ -169,7 +176,7 @@ public class DatabasePlatform {
* </p>
*/
public int getMaxTableNameLength() {
return platformDdl.getMaxTableNameLength();
return maxTableNameLength;
}
/**
@@ -182,8 +189,8 @@ public class DatabasePlatform {
/**
* Create and return a DDL handler for generating DDL scripts.
*/
public DdlHandler createDdlHandler() {
return platformDdl.createDdlHandler();
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
return platformDdl.createDdlHandler(serverConfig);
}
/**
@@ -14,6 +14,7 @@ public class Oracle10Platform extends DatabasePlatform {
public Oracle10Platform() {
super();
this.name = "oracle";
this.maxIntersectionTableName = 30;
// OnQueryOnly.CLOSE as a performance optimisation on Oracle
this.onQueryOnly = OnQueryOnly.CLOSE;
this.dbEncrypt = new Oracle10DbEncrypt();
@@ -1,6 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
@@ -66,8 +67,8 @@ public class PostgresPlatform extends DatabasePlatform {
/**
* Return a DdlHandler instance for generating DDL for the specific platform.
*/
public DdlHandler createDdlHandler() {
return this.platformDdl.createDdlHandler();
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
return this.platformDdl.createDdlHandler(serverConfig);
}
/**
@@ -30,8 +30,6 @@ public class DbMigration {
this.migrationConfig = server.getServerConfig().getMigrationConfig();
}
public void writeCurrent() {
CurrentModel currentModel = new CurrentModel(server);
@@ -97,10 +97,8 @@ public class DdlGenerator implements SpiEbeanPlugin {
try {
String c = generateDropDdl();
writeFile(dropFile, c);
} catch (IOException e) {
String msg = "Error generating Drop DDL";
throw new PersistenceException(msg, e);
throw new PersistenceException("Error generating Drop DDL", e);
}
}
@@ -109,10 +107,8 @@ public class DdlGenerator implements SpiEbeanPlugin {
try {
String c = generateCreateDdl();
writeFile(createFile, c);
} catch (IOException e) {
String msg = "Error generating Create DDL";
throw new PersistenceException(msg, e);
throw new PersistenceException("Error generating Create DDL", e);
}
}
@@ -213,8 +209,7 @@ public class DdlGenerator implements SpiEbeanPlugin {
t.commit();
} catch (Exception e) {
String msg = "Error: " + e.getMessage();
throw new PersistenceException(msg, e);
throw new PersistenceException("Error: " + e.getMessage(), e);
} finally {
t.end();
}
@@ -1,8 +1,9 @@
package com.avaje.ebean.dbmigration.ddlgeneration;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.config.NamingConvention;
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.AlterColumn;
@@ -22,8 +23,8 @@ public class BaseDdlHandler implements DdlHandler {
protected final TableDdl tableDdl;
public BaseDdlHandler(DdlNamingConvention namingConvention, PlatformDdl platformDdl) {
this.tableDdl = new BaseTableDdl(namingConvention, platformDdl);
public BaseDdlHandler(NamingConvention namingConvention, DbConstraintNaming naming, PlatformDdl platformDdl) {
this.tableDdl = new BaseTableDdl(namingConvention, naming, platformDdl);
this.columnDdl = new BaseColumnDdl(platformDdl);
}
@@ -55,23 +55,22 @@ public class BaseColumnDdl implements ColumnDdl {
historyIncludeColumn(writer, alterColumn);
}
if (hasValue(alterColumn.getOldReferences())) {
if (hasValue(alterColumn.getDropForeignKey())) {
dropForeignKey(writer, alterColumn);
}
if (hasValue(alterColumn.getNewReferences())) {
if (hasValue(alterColumn.getReferences())) {
addForeignKey(writer, alterColumn);
}
if (isTrue(alterColumn.isUnique())) {
addUniqueConstraint(writer, alterColumn);
} else if (isFalse(alterColumn.isUnique())) {
if (hasValue(alterColumn.getDropUnique())) {
dropUniqueConstraint(writer, alterColumn);
}
if (hasValue(alterColumn.getUnique())) {
addUniqueConstraint(writer, alterColumn);
}
if (isTrue(alterColumn.isUniqueOneToOne())) {
if (hasValue(alterColumn.getUniqueOneToOne())) {
addUniqueOneToOneConstraint(writer, alterColumn);
} else if (isFalse(alterColumn.isUniqueOneToOne())) {
dropUniqueOneToOneConstraint(writer, alterColumn);
}
}
@@ -82,34 +81,41 @@ public class BaseColumnDdl implements ColumnDdl {
}
protected void dropForeignKey(DdlWrite writer, AlterColumn alterColumn) {
protected void dropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
String tableName = alter.getTableName();
String fkName = alter.getDropForeignKey();
writer.apply()
.append(platformDdl.alterTableDropForeignKey(tableName, fkName))
.endOfStatement();
}
protected void dropUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alterColumn) {
}
protected void addUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alterColumn) {
}
protected void dropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String uqName = platformDdl.namingConvention.uniqueConstraintName(tableName, columnName, 50);
String uqName = alter.getDropUnique();
writer.apply()
.append(platformDdl.dropIndex(uqName, tableName))
.endOfStatement();
}
protected void addUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
addUniqueConstraint(writer, alter, alter.getUniqueOneToOne());
}
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException {
addUniqueConstraint(writer, alter, alter.getUnique());
}
protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String uqName = platformDdl.namingConvention.uniqueConstraintName(tableName, columnName, 50);
String[] cols = {columnName};
writer.apply()
@@ -1,9 +1,13 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.IdType;
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.ddlgeneration.platform.util.IndexSet;
import com.avaje.ebean.dbmigration.migration.Column;
import com.avaje.ebean.dbmigration.migration.CreateTable;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
@@ -19,7 +23,9 @@ import java.util.List;
*/
public class BaseTableDdl implements TableDdl {
protected final DdlNamingConvention namingConvention;
protected final DbConstraintNaming naming;
protected final NamingConvention namingConvention;
protected final PlatformDdl platformDdl;
@@ -32,7 +38,7 @@ public class BaseTableDdl implements TableDdl {
/**
* Used when unique constraints specifically for OneToOne can't be created normally (MsSqlServer).
*/
protected IndexSet externalUnique = new IndexSet();
protected List<Column> externalUnique = new ArrayList<Column>();
// counters used when constraint names are truncated due to maximum length
// and these counters are used to keep the constraint name unique
@@ -41,11 +47,16 @@ public class BaseTableDdl implements TableDdl {
protected int countForeignKey;
protected int countIndex;
public BaseTableDdl(ServerConfig serverConfig, PlatformDdl platformDdl) {
this(serverConfig.getNamingConvention(), serverConfig.getConstraintNaming(), platformDdl);
}
/**
* Construct with a naming convention and platform specific DDL.
*/
public BaseTableDdl(DdlNamingConvention namingConvention, PlatformDdl platformDdl) {
public BaseTableDdl(NamingConvention namingConvention, DbConstraintNaming naming, PlatformDdl platformDdl) {
this.namingConvention = namingConvention;
this.naming = naming;
this.platformDdl = platformDdl;
}
@@ -74,7 +85,7 @@ public class BaseTableDdl implements TableDdl {
List<Column> columns = createTable.getColumn();
List<Column> pk = determinePrimaryKeyColumns(columns);
boolean singleColumnPrimaryKey = pk.size() == 1;
boolean singleColumnPrimaryKey = (pk.size() == 1);
boolean useIdentity = false;
boolean useSequence = false;
@@ -99,7 +110,7 @@ public class BaseTableDdl implements TableDdl {
writeCompoundUniqueConstraints(apply, createTable);
if (!pk.isEmpty()) {
// defined on the columns
writePrimaryKeyConstraint(apply, tableName, toColumnNames(pk));
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
}
apply.newLine().append(")").endOfStatement();
@@ -111,7 +122,8 @@ public class BaseTableDdl implements TableDdl {
dropTable(writer.rollback(), tableName);
if (useSequence) {
writeSequence(writer, createTable);
String pkCol = singleColumnPrimaryKey ? pk.get(0).getName() : null;
writeSequence(writer, createTable, pkCol);
}
// add blank line for a bit of whitespace between tables
@@ -133,30 +145,31 @@ public class BaseTableDdl implements TableDdl {
private void writeUniqueOneToOneConstraints(DdlWrite write, CreateTable createTable) throws IOException {
String tableName = createTable.getName();
for (IndexColumns index : externalUnique.indexes) {
String uqName = determineUniqueConstraintName(tableName, index.joinedNames());
for (Column col : externalUnique) {
String uqName = col.getUniqueOneToOne();
String[] columnNames = {col.getName()};
write.apply()
.append(platformDdl.createExternalUniqueForOneToOne(uqName, tableName, index.columnsArray()))
.append(platformDdl.createExternalUniqueForOneToOne(uqName, tableName, columnNames))
.endOfStatement();
// register it so we check against effective duplication
// when creating the foreign key indexes
indexSet.add(index);
write.rollbackForeignKeys()
.append(platformDdl.dropIndex(uqName, tableName))
.endOfStatement();
}
}
private void writeSequence(DdlWrite writer, CreateTable createTable) throws IOException {
private void writeSequence(DdlWrite writer, CreateTable createTable, String pk) throws IOException {
// explicit sequence use or platform decides
String explicitSequenceName = createTable.getSequenceName();
int initial = toInt(createTable.getSequenceInitial());
int allocate = toInt(createTable.getSequenceAllocate());
String seqName = namingConvention.sequenceName(createTable.getName(), explicitSequenceName);
String seqName = explicitSequenceName;
if (seqName == null) {
seqName = namingConvention.getSequenceName(createTable.getName(), pk);
}
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
if (createSeq != null) {
writer.apply().append(createSeq).newLine();
@@ -177,7 +190,7 @@ public class BaseTableDdl implements TableDdl {
for (Column column : columns) {
String references = column.getReferences();
if (hasValue(references)) {
writeForeignKey(write, tableName, column.getName(), references);
writeForeignKey(write, tableName, column);
}
}
@@ -192,19 +205,19 @@ public class BaseTableDdl implements TableDdl {
for (ForeignKey key : foreignKey) {
String refTableName = key.getRefTableName();
String fkName = determineForeignKeyConstraintName(tableName, refTableName);
String fkName = key.getName();
String[] cols = toColumnNamesSplit(key.getColumnNames());
String[] refColumns = toColumnNamesSplit(key.getRefColumnNames());
writeForeignKey(write, fkName, tableName, cols, refTableName, refColumns);
writeForeignKey(write, fkName, tableName, cols, refTableName, refColumns, key.getIndexName());
}
}
protected void writeForeignKey(DdlWrite write, String tableName, String columnName, String references) throws IOException {
String fkName = determineForeignKeyConstraintName(tableName, columnName);
protected void writeForeignKey(DdlWrite write, String tableName, Column column) throws IOException {
String fkName = column.getForeignKeyName();
String references = column.getReferences();
int pos = references.lastIndexOf('.');
if (pos == -1) {
throw new IllegalStateException("Expecting period '.' character for table.column split but not found in [" + references + "]");
@@ -212,13 +225,13 @@ public class BaseTableDdl implements TableDdl {
String refTableName = references.substring(0, pos);
String refColumnName = references.substring(pos + 1);
String[] cols = {columnName};
String[] cols = {column.getName()};
String[] refCols = {refColumnName};
writeForeignKey(write, fkName, tableName, cols, refTableName, refCols);
writeForeignKey(write, fkName, tableName, cols, refTableName, refCols, column.getForeignKeyIndex());
}
protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns) throws IOException {
protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns, String indexName) throws IOException {
tableName = lowerName(tableName);
DdlBuffer fkeyBuffer = write.applyForeignKeys();
@@ -234,10 +247,7 @@ public class BaseTableDdl implements TableDdl {
fkeyBuffer.appendWithSpace(platformDdl.getForeignKeyRestrict())
.endOfStatement();
String indexName = determineForeignKeyIndexName(tableName, columns);
boolean addIndex = indexSet.add(columns);
if (addIndex) {
if (indexName != null) {
// no matching unique constraint so add the index
fkeyBuffer.append("create index ").append(indexName).append(" on ").append(tableName);
appendColumns(columns, fkeyBuffer);
@@ -250,7 +260,7 @@ public class BaseTableDdl implements TableDdl {
.append(platformDdl.alterTableDropForeignKey(tableName, fkName))
.endOfStatement();
if (addIndex) {
if (indexName != null) {
write.rollbackForeignKeys()
.append(platformDdl.dropIndex(indexName, tableName))
.endOfStatement();
@@ -289,7 +299,7 @@ public class BaseTableDdl implements TableDdl {
for (Column column : columns) {
String checkConstraint = column.getCheckConstraint();
if (hasValue(checkConstraint)) {
writeCheckConstraint(apply, createTable.getName(), column, checkConstraint);
writeCheckConstraint(apply, column, checkConstraint);
}
}
}
@@ -297,9 +307,9 @@ public class BaseTableDdl implements TableDdl {
/**
* Write a check constraint.
*/
protected void writeCheckConstraint(DdlBuffer buffer, String tableName, Column column, String checkConstraint) throws IOException {
protected void writeCheckConstraint(DdlBuffer buffer, Column column, String checkConstraint) throws IOException {
String ckName = determineCheckConstraintName(tableName, column.getName());
String ckName = column.getCheckConstraintName();
buffer.append(",").newLine();
buffer.append(" constraint ").append(ckName);
@@ -319,11 +329,11 @@ public class BaseTableDdl implements TableDdl {
List<Column> columns = createTable.getColumn();
for (Column column : columns) {
if (isTrue(column.isUnique()) || (inlineUniqueOneToOne && isTrue(column.isUniqueOneToOne()))) {
if (hasValue(column.getUnique()) || (inlineUniqueOneToOne && hasValue(column.getUniqueOneToOne()))) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, createTable.getName(), column);
indexSet.add(column);
} else if (!inlineUniqueOneToOne && isTrue(column.isUniqueOneToOne())) {
inlineUniqueConstraintSingle(apply, column);
} else if (!inlineUniqueOneToOne && hasValue(column.getUniqueOneToOne())) {
// MsSqlServer specific mechanism for adding unique constraints (that allow nulls)
externalUnique.add(column);
}
@@ -333,9 +343,12 @@ public class BaseTableDdl implements TableDdl {
/**
* Write the unique constraint inline with the create table statement.
*/
protected void inlineUniqueConstraintSingle(DdlBuffer buffer, String tableName, Column column) throws IOException {
protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) throws IOException {
String uqName = determineUniqueConstraintName(tableName, column.getName());
String uqName = column.getUnique();
if (uqName == null) {
uqName = column.getUniqueOneToOne();
}
buffer.append(",").newLine();
buffer.append(" constraint ").append(uqName).append(" unique ");
@@ -347,9 +360,7 @@ public class BaseTableDdl implements TableDdl {
/**
* Write the primary key constraint inline with the create table statement.
*/
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String tableName, String[] pkColumns) throws IOException {
String pkName = determinePrimaryKeyName(tableName);
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String pkName, String[] pkColumns) throws IOException {
buffer.append(",").newLine();
buffer.append(" constraint ").append(pkName).append(" primary key");
@@ -383,7 +394,7 @@ public class BaseTableDdl implements TableDdl {
* choice for column/table names.
*/
protected String lowerName(String name) {
return platformDdl.lowerName(name);
return naming.lowerName(name);
}
/**
@@ -415,46 +426,6 @@ public class BaseTableDdl implements TableDdl {
return platformDdl.convert(type, identity);
}
/**
* Return the primary key constraint name.
*/
protected String determinePrimaryKeyName(String tableName) {
return namingConvention.primaryKeyName(tableName);
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
protected String determineForeignKeyConstraintName(String tableName, String columnName) {
return namingConvention.foreignKeyConstraintName(tableName, columnName, ++countForeignKey);
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
protected String determineForeignKeyIndexName(String tableName, String[] columns) {
return namingConvention.foreignKeyIndexName(tableName, columns, ++countIndex);
}
/**
* Return the unique constraint name.
*/
protected String determineUniqueConstraintName(String tableName, String columnName) {
return namingConvention.uniqueConstraintName(tableName, columnName, ++countUnique);
}
/**
* Return the constraint name.
*/
protected String determineCheckConstraintName(String tableName, String columnName) {
return namingConvention.checkConstraintName(tableName, columnName, ++countCheck);
}
/**
* Return the list of columns that make the primary key.
*/
@@ -489,118 +460,4 @@ public class BaseTableDdl implements TableDdl {
return (value == null) ? 0 : value.intValue();
}
/**
* The indexes held on the table.
* <p>
* Used to detect when we don't need to add an index on the foreign key columns
* when there is an existing unique constraint with the same columns.
*/
protected static class IndexSet {
private List<IndexColumns> indexes = new ArrayList<IndexColumns>();
/**
* Clear the indexes (for each table).
*/
public void clear() {
indexes.clear();
}
/**
* Add an index for the given column.
*/
public void add(Column column) {
indexes.add(new IndexColumns(column));
}
/**
* Return true if an index should be added for the given columns.
* <p>
* Returning false indicates there is an existing index (unique constraint) with these columns
* and that an extra index should not be added.
* </p>
*/
public boolean add(String[] columns) {
IndexColumns newIndex = new IndexColumns(columns);
for (int i = 0; i < indexes.size(); i++) {
if (indexes.get(i).isMatch(newIndex)) {
return false;
}
}
indexes.add(newIndex);
return true;
}
/**
* Add the externally created unique constraint here so that we check later if foreign key indexes
* don't need to be created (as the columns match this unique constraint).
*/
public void add(IndexColumns index) {
indexes.add(index);
}
}
/**
* Set of columns making up a particular index (column order is important).
*/
protected static class IndexColumns {
List<String> columns = new ArrayList<String>(4);
/**
* Construct representing as a single column index.
*/
public IndexColumns(Column column) {
columns.add(column.getName());
}
/**
* Construct representing index.
*/
public IndexColumns(String[] columnNames) {
for (int i = 0; i < columnNames.length; i++) {
columns.add(columnNames[i]);
}
}
/**
* Return true if there this index match (same columns same order).
*/
public boolean isMatch(IndexColumns other) {
return columns.equals(other.columns);
}
/**
* Add a unique index based on the single column.
*/
protected void add(String column) {
columns.add(column);
}
/**
* Return the columns as a string array.
*/
public String[] columnsArray() {
return columns.toArray(new String[columns.size()]);
}
/**
* Return the column names all joined with underscore.
*/
public String joinedNames() {
if (columns.size() == 1) {
return columns.get(0);
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
sb.append("_");
}
sb.append(columns.get(i));
}
return sb.toString();
}
}
}
}
@@ -11,6 +11,7 @@ public class DB2Ddl extends PlatformDdl {
public DB2Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.identitySuffix = " generated by default as identity";
this.maxConstraintNameLength = 18;
}
}
@@ -1,145 +0,0 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
/**
* 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 int maxConstraintNameLength = 32;
protected boolean lowerCaseNames = true;
protected DdlNameNormalise normalise = new DdlNameNormalise();
public DdlNamingConvention() {
}
/**
* Return the primary key constraint name.
*/
public String primaryKeyName(String tableName) {
return maxLength(pkPrefix + normaliseTable(tableName) + pkSuffix, 0);
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
public String foreignKeyConstraintName(String tableName, String columnName, int foreignKeyCount) {
return maxLength(fkPrefix + normaliseTable(tableName) + fkMiddle + normaliseColumn(columnName) + fkSuffix, foreignKeyCount);
}
/**
* Return the index name associated with a foreign key constraint given a single column foreign key.
*/
public String foreignKeyIndexName(String tableName, String[] columns, int indexCount) {
String colPart;
if (columns.length == 1) {
colPart = normaliseColumn(columns[0]);
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append("_");
}
sb.append(normaliseColumn(columns[i]));
}
colPart = sb.toString();
}
return maxLength(fkIndexPrefix + normaliseTable(tableName) + fkIndexMiddle + colPart + fkIndexSuffix, indexCount);
}
/**
* Return the unique constraint name.
*/
public String uniqueConstraintName(String tableName, String columnName, int indexCount) {
return maxLength(uqPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + uqSuffix, indexCount);
}
/**
* Return the check constraint name.
*/
public String checkConstraintName(String tableName, String columnName, int checkCount) {
return maxLength(ckPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + ckSuffix, checkCount);
}
/**
* Return the sequence name. If it is explicitly provided return that but
* otherwise derive the sequence name from the table name.
*
* @param tableName the table the sequence relates to
* @param sequenceName an explicitly provided sequence name (typically null)
* @return the sequence name to use
*/
public String sequenceName(String tableName, String sequenceName) {
return (sequenceName != null) ? lowerName(sequenceName) : normaliseTable(tableName) + "_seq";
}
/**
* Return the maximum table name length.
* <p>
* This is used when deriving names of intersection tables.
* </p>
*/
public int getMaxTableNameLength() {
return maxConstraintNameLength;
}
/**
* Apply a maximum length to the constraint name.
*/
protected String maxLength(String constraintName, int count) {
if (constraintName.length() < maxConstraintNameLength) {
return constraintName;
}
// add the count to ensure the constraint name is unique
// (relying on the prefix having the table name to be globally unique)
return constraintName.substring(0, maxConstraintNameLength - 3) + "_" + count;
}
/**
* Normalise the table name by trimming catalog and schema and removing any
* quoted identifier characters (",',[,] etc).
*/
protected String normaliseTable(String tableName) {
return normalise.normaliseTable(tableName);
}
/**
* Normalise the column name by removing any quoted identifier characters (",',[,] etc).
*/
protected String normaliseColumn(String tableName) {
return normalise.normaliseColumn(tableName);
}
public String lowerName(String tableName) {
if (lowerCaseNames && normalise.notQuoted(tableName)) {
return tableName.toLowerCase();
}
return tableName;
}
}
@@ -10,7 +10,6 @@ public class H2Ddl extends PlatformDdl {
public H2Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.historyDdl = new H2HistoryDdl();
}
}
@@ -1,17 +0,0 @@
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
}
}
@@ -13,7 +13,6 @@ public class MsSqlServerDdl extends PlatformDdl {
this.identitySuffix = " identity(1,1)";
this.foreignKeyRestrict = "";
this.inlineUniqueOneToOne = false;
this.namingConvention.maxConstraintNameLength = 62; //Actually 128
}
@Override
@@ -10,7 +10,6 @@ public class MySqlDdl extends PlatformDdl {
public MySqlDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.namingConvention.maxConstraintNameLength = 64;
}
/**
@@ -24,6 +23,7 @@ public class MySqlDdl extends PlatformDdl {
/**
* Return the drop foreign key clause.
*/
@Override
public String alterTableDropForeignKey(String tableName, String fkName) {
return "alter table " + tableName + " drop foreign key " + fkName;
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.model.MTable;
@@ -12,8 +13,12 @@ import java.io.IOException;
public class NoHistorySupportDdl implements PlatformHistoryDdl {
@Override
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
public void configure(ServerConfig serverConfig) {
// does nothing
}
@Override
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
// does nothing
}
}
@@ -10,11 +10,10 @@ public class Oracle10Ddl extends PlatformDdl {
public Oracle10Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.historyDdl = new H2HistoryDdl();
this.dropTableIfExists = "drop table ";
this.dropSequenceIfExists = "drop sequence ";
this.dropTableCascade = " cascade constraints purge";
this.namingConvention.maxConstraintNameLength = 30;
this.maxConstraintNameLength = 30;
this.foreignKeyRestrict = "";
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.IdType;
@@ -7,6 +8,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.VowelRemover;
import com.avaje.ebean.dbmigration.migration.AlterColumn;
import com.avaje.ebean.dbmigration.migration.IdentityType;
import com.avaje.ebean.dbmigration.model.MTable;
@@ -20,8 +22,6 @@ public class PlatformDdl {
protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
protected DdlNamingConvention namingConvention = new DdlNamingConvention();
/**
* Converter for logical/standard types to platform specific types. (eg. clob -> text)
*/
@@ -48,18 +48,50 @@ public class PlatformDdl {
protected String identitySuffix = " auto_increment";
protected String dropConstraintIfExists = "drop constraint if exists";
protected String dropIndexIfExists = "drop index if exists ";
/**
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
*/
protected boolean inlineUniqueOneToOne = true;
/**
* A value of 60 is a reasonable default for all databases except
* Oracle (limited to 30) and DB2 (limited to 18).
*/
protected int maxConstraintNameLength = 60;
public PlatformDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
this.dbIdentity = dbIdentity;
this.typeConverter = new PlatformTypeConverter(platformTypes);
}
public DdlHandler createDdlHandler() {
return new BaseDdlHandler(namingConvention, this);
public DdlHandler createDdlHandler(ServerConfig serverConfig) {
historyDdl.configure(serverConfig);
return new BaseDdlHandler(serverConfig.getNamingConvention(), serverConfig.getConstraintNaming(), this);
}
/**
* Apply a maximum length to the constraint name.
* <p>
* This implementation should work well apart from perhaps DB2 where the limit is 18.
*/
public String maxLength(String constraintName, int count) {
if (constraintName.length() < maxConstraintNameLength) {
return constraintName;
}
if (maxConstraintNameLength < 60) {
// trim out vowels for Oracle / DB2 with short max lengths
constraintName = VowelRemover.trim(constraintName, 4);
if (constraintName.length() < maxConstraintNameLength) {
return constraintName;
}
}
// add the count to ensure the constraint name is unique
// (relying on the prefix having the table name to be globally unique)
return constraintName.substring(0, maxConstraintNameLength - 3) + "_" + count;
}
public IdType useIdentityType(IdentityType modelIdentityType) {
@@ -85,7 +117,7 @@ public class PlatformDdl {
* Return the drop foreign key clause.
*/
public String alterTableDropForeignKey(String tableName, String fkName) {
return "alter table " + tableName + " drop constraint " + fkName;
return "alter table " + tableName + " " + dropConstraintIfExists + " " + fkName;
}
/**
@@ -140,28 +172,9 @@ public class PlatformDdl {
* Return the drop index statement.
*/
public String dropIndex(String indexName, String tableName) {
return "drop index "+indexName;
return dropIndexIfExists + indexName;
}
/**
* Support lower naming tables and columns in DDL generation.
* Tables/Columns with quoted identifiers are exempt.
*/
public String lowerName(String name) {
return namingConvention.lowerName(name);
}
/**
* Return the maximum table name length.
* <p>
* This is used when deriving names of intersection tables.
* </p>
*/
public int getMaxTableNameLength() {
return namingConvention.getMaxTableNameLength();
}
/**
* Return true if unique constraints for OneToOne can be inlined as normal.
* Returns false for MsSqlServer due to it's null handling for unique constraints.
@@ -1,5 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.model.MTable;
@@ -10,8 +11,15 @@ import java.io.IOException;
*/
public interface PlatformHistoryDdl {
/**
* Configure typically reading the
* @param serverConfig
*/
void configure(ServerConfig serverConfig);
/**
* Add history support to the table using platform specific mechanism.
*/
void createWithHistory(DdlWrite writer, MTable table) throws IOException;
}
@@ -10,9 +10,8 @@ public class PostgresDdl extends PlatformDdl {
public PostgresDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.historyDdl = new PostgresHistoryDdl(this.namingConvention.normalise);
this.historyDdl = new PostgresHistoryDdl();
this.dropTableCascade = " cascade";
this.namingConvention.maxConstraintNameLength = 62;
}
/**
@@ -1,5 +1,7 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.model.MColumn;
@@ -9,18 +11,38 @@ import java.io.IOException;
import java.util.Collection;
/**
*
* Uses DB triggers to maintain a history table.
*/
public class PostgresHistoryDdl implements PlatformHistoryDdl {
private final DdlNameNormalise normalise;
private DbConstraintNaming constraintNaming;
public PostgresHistoryDdl(DdlNameNormalise normalise) {
this.normalise = normalise;
private String sysPeriod;
private String viewSuffix;
private String historySuffix = "_history";
public PostgresHistoryDdl() {
}
@Override
public void configure(ServerConfig serverConfig) {
this.sysPeriod = serverConfig.getAsOfSysPeriod();
this.viewSuffix = serverConfig.getAsOfViewSuffix();
this.constraintNaming = serverConfig.getConstraintNaming();
}
@Override
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
addHistoryTable(writer, table);
addStoredFunction(writer, table);
addTrigger(writer, table);
}
protected String historyTableName(String baseTableName) {
return baseTableName + "_history";
return baseTableName + historySuffix;
}
protected String procedureName(String baseTableName) {
@@ -31,42 +53,33 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
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.normaliseTable(table.getName());
String baseTableName = constraintNaming.normaliseTable(table.getName());
DdlBuffer buffer = writer.applyHistory();
buffer
.append("alter table ").append(baseTableName)
.append(" add column sys_period tstzrange not null")
.append(" add column ").append(sysPeriod).append(" tstzrange not null")
.endOfStatement().end();
buffer
.append("create table ").append(baseTableName).append("_history")
.append("create table ").append(baseTableName).append(historySuffix)
.append(" (like ").append(baseTableName).append(")")
.endOfStatement().end();
buffer
.append("create view ").append(baseTableName).append("_with_history")
.append("create view ").append(baseTableName).append(viewSuffix)
.append(" as select * from").append(baseTableName)
.append(" union all select * from").append(baseTableName).append("_history")
.append(" union all select * from").append(baseTableName).append(historySuffix)
.endOfStatement().end();
}
public void addTrigger(DdlWrite writer, MTable table) throws IOException {
String baseTableName = this.normalise.normaliseTable(table.getName());
String baseTableName = constraintNaming.normaliseTable(table.getName());
String procedureName = procedureName(baseTableName);
String triggerName = triggerName(baseTableName);
@@ -81,7 +94,7 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
public void addStoredFunction(DdlWrite writer, MTable table) throws IOException {
String baseTableName = this.normalise.normaliseTable(table.getName());
String baseTableName = constraintNaming.normaliseTable(table.getName());
String procedureName = procedureName(baseTableName);
DdlBuffer buffer = writer.applyHistory();
@@ -90,13 +103,13 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
.append("begin").newLine();
buffer
.append(" if (TG_OP = 'INSERT') then").newLine()
.append(" NEW.sys_period = tstzrange(CURRENT_TIMESTAMP,null);").newLine()
.append(" NEW.").append(sysPeriod).append(" = 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(" NEW.").append(sysPeriod).append(" = tstzrange(CURRENT_TIMESTAMP,null);").newLine()
.append(" return new;").newLine().newLine();
buffer
.append(" elsif (TG_OP = 'DELETE') then").newLine();
@@ -116,9 +129,9 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
String historyTable = historyTableName(table.getName());
buffer.append(" insert into ").append(historyTable).append(" (sys_period,");
buffer.append(" insert into ").append(historyTable).append(" (").append(sysPeriod).append(",");
appendColumnNames(buffer, table, "");
buffer.append(") values (tstzrange(lower(OLD.sys_period), CURRENT_TIMESTAMP), ");
buffer.append(") values (tstzrange(lower(OLD.").append(sysPeriod).append("), CURRENT_TIMESTAMP), ");
appendColumnNames(buffer, table, "OLD.");
buffer.append(");").newLine();
}
@@ -0,0 +1,89 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform.util;
import java.util.ArrayList;
import java.util.List;
/**
* Set of columns making up a particular index (column order is important).
*/
public class IndexColumns {
List<String> columns = new ArrayList<String>(4);
/**
* Construct representing as a single column index.
*/
public IndexColumns(String column) {
columns.add(column);
}
/**
* Construct representing index.
*/
public IndexColumns(String[] columnNames) {
for (int i = 0; i < columnNames.length; i++) {
columns.add(columnNames[i]);
}
}
/**
* Return true if this index matches (same single column).
*/
public boolean isMatch(String singleColumn) {
return columns.size() == 1 && columns.get(0).equals(singleColumn);
}
/**
* Return true if this index matches (same single column).
*/
public boolean isMatch(List<String> columnNames) {
if (columns.size() != columnNames.size()) {
return false;
}
for (int i = 0; i <columns.size() ; i++) {
if (!columns.get(i).equals(columnNames.get(i))) {
return false;
}
}
return true;
}
/**
* Return true if this index matches (same columns same order).
*/
public boolean isMatch(IndexColumns other) {
return columns.equals(other.columns);
}
/**
* Add a unique index based on the single column.
*/
protected void add(String column) {
columns.add(column);
}
/**
* Return the columns as a string array.
*/
public String[] columnsArray() {
return columns.toArray(new String[columns.size()]);
}
/**
* Return the column names all joined with underscore.
*/
public String joinedNames() {
if (columns.size() == 1) {
return columns.get(0);
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
sb.append("_");
}
sb.append(columns.get(i));
}
return sb.toString();
}
}
}
@@ -0,0 +1,85 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform.util;
import com.avaje.ebean.dbmigration.migration.Column;
import java.util.ArrayList;
import java.util.List;
/**
* The indexes held on the table.
* <p>
* Used to detect when we don't need to add an index on the foreign key columns
* when there is an existing unique constraint with the same columns.
*/
public class IndexSet {
private List<IndexColumns> indexes = new ArrayList<IndexColumns>();
/**
* Clear the indexes (for each table).
*/
public void clear() {
indexes.clear();
}
/**
* Add an index for the given column.
*/
public void add(String column) {
indexes.add(new IndexColumns(column));
}
/**
* Return true if an index should be added for the given columns.
* <p>
* Returning false indicates there is an existing index (unique constraint) with these columns
* and that an extra index should not be added.
* </p>
*/
public boolean add(String[] columns) {
IndexColumns newIndex = new IndexColumns(columns);
for (int i = 0; i < indexes.size(); i++) {
if (indexes.get(i).isMatch(newIndex)) {
return false;
}
}
indexes.add(newIndex);
return true;
}
/**
* Add the externally created unique constraint here so that we check later if foreign key indexes
* don't need to be created (as the columns match this unique constraint).
*/
public void add(IndexColumns index) {
indexes.add(index);
}
public boolean contains(String column) {
for (IndexColumns index : indexes) {
if (index.isMatch(column)) {
return true;
}
}
return false;
}
public boolean contains(List<String> columns) {
for (IndexColumns index : indexes) {
if (index.isMatch(columns)) {
return true;
}
}
return false;
}
public List<IndexColumns> getIndexes() {
return indexes;
}
public void addIndex(Column column) {
}
}
@@ -0,0 +1,33 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform.util;
/**
* Utility to remove vowels (from constraint names primarily for Oracle and DB2).
*/
public class VowelRemover {
/**
* Trim a word by removing vowels skipping some initial characters.
*/
public static String trim(String word, int skipChars) {
if (word.length() < skipChars) {
return word;
}
StringBuilder res = new StringBuilder();
res.append(word.substring(0, skipChars));
for (int i = skipChars; i < word.length(); i++) {
char ch = word.charAt(i);
if (!isVowel(ch)) {
res.append(ch);
}
}
return res.toString();
}
private static boolean isVowel(char ch) {
ch = Character.toLowerCase(ch);
return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u';
}
}
@@ -20,16 +20,20 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="defaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="notnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="historyExclude" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="uniqueOneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="oldDefaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="newDefaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="oldCheckConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="newCheckConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="oldReferences" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="newReferences" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="checkConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="checkConstraintName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="dropCheckConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="uniqueOneToOne" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="dropUnique" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="references" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="dropForeignKey" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="dropForeignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -48,26 +52,34 @@ public class AlterColumn {
protected String tableName;
@XmlAttribute(name = "type")
protected String type;
@XmlAttribute(name = "defaultValue")
protected String defaultValue;
@XmlAttribute(name = "notnull")
protected Boolean notnull;
@XmlAttribute(name = "historyExclude")
protected Boolean historyExclude;
@XmlAttribute(name = "checkConstraint")
protected String checkConstraint;
@XmlAttribute(name = "checkConstraintName")
protected String checkConstraintName;
@XmlAttribute(name = "dropCheckConstraint")
protected String dropCheckConstraint;
@XmlAttribute(name = "unique")
protected Boolean unique;
protected String unique;
@XmlAttribute(name = "uniqueOneToOne")
protected Boolean uniqueOneToOne;
@XmlAttribute(name = "oldDefaultValue")
protected String oldDefaultValue;
@XmlAttribute(name = "newDefaultValue")
protected String newDefaultValue;
@XmlAttribute(name = "oldCheckConstraint")
protected String oldCheckConstraint;
@XmlAttribute(name = "newCheckConstraint")
protected String newCheckConstraint;
@XmlAttribute(name = "oldReferences")
protected String oldReferences;
@XmlAttribute(name = "newReferences")
protected String newReferences;
protected String uniqueOneToOne;
@XmlAttribute(name = "dropUnique")
protected String dropUnique;
@XmlAttribute(name = "references")
protected String references;
@XmlAttribute(name = "foreignKeyName")
protected String foreignKeyName;
@XmlAttribute(name = "foreignKeyIndex")
protected String foreignKeyIndex;
@XmlAttribute(name = "dropForeignKey")
protected String dropForeignKey;
@XmlAttribute(name = "dropForeignKeyIndex")
protected String dropForeignKeyIndex;
/**
* Gets the value of the columnName property.
@@ -141,6 +153,30 @@ public class AlterColumn {
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 notnull property.
*
@@ -189,15 +225,87 @@ public class AlterColumn {
this.historyExclude = 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 checkConstraintName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getCheckConstraintName() {
return checkConstraintName;
}
/**
* Sets the value of the checkConstraintName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setCheckConstraintName(String value) {
this.checkConstraintName = value;
}
/**
* Gets the value of the dropCheckConstraint property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getDropCheckConstraint() {
return dropCheckConstraint;
}
/**
* Sets the value of the dropCheckConstraint property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setDropCheckConstraint(String value) {
this.dropCheckConstraint = value;
}
/**
* Gets the value of the unique property.
*
* @return
* possible object is
* {@link Boolean }
* {@link String }
*
*/
public Boolean isUnique() {
public String getUnique() {
return unique;
}
@@ -206,10 +314,10 @@ public class AlterColumn {
*
* @param value
* allowed object is
* {@link Boolean }
* {@link String }
*
*/
public void setUnique(Boolean value) {
public void setUnique(String value) {
this.unique = value;
}
@@ -218,10 +326,10 @@ public class AlterColumn {
*
* @return
* possible object is
* {@link Boolean }
* {@link String }
*
*/
public Boolean isUniqueOneToOne() {
public String getUniqueOneToOne() {
return uniqueOneToOne;
}
@@ -230,155 +338,155 @@ public class AlterColumn {
*
* @param value
* allowed object is
* {@link Boolean }
* {@link String }
*
*/
public void setUniqueOneToOne(Boolean value) {
public void setUniqueOneToOne(String value) {
this.uniqueOneToOne = value;
}
/**
* Gets the value of the oldDefaultValue property.
* Gets the value of the dropUnique property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getOldDefaultValue() {
return oldDefaultValue;
public String getDropUnique() {
return dropUnique;
}
/**
* Sets the value of the oldDefaultValue property.
* Sets the value of the dropUnique property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setOldDefaultValue(String value) {
this.oldDefaultValue = value;
public void setDropUnique(String value) {
this.dropUnique = value;
}
/**
* Gets the value of the newDefaultValue property.
* Gets the value of the references property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getNewDefaultValue() {
return newDefaultValue;
public String getReferences() {
return references;
}
/**
* Sets the value of the newDefaultValue property.
* Sets the value of the references property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setNewDefaultValue(String value) {
this.newDefaultValue = value;
public void setReferences(String value) {
this.references = value;
}
/**
* Gets the value of the oldCheckConstraint property.
* Gets the value of the foreignKeyName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getOldCheckConstraint() {
return oldCheckConstraint;
public String getForeignKeyName() {
return foreignKeyName;
}
/**
* Sets the value of the oldCheckConstraint property.
* Sets the value of the foreignKeyName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setOldCheckConstraint(String value) {
this.oldCheckConstraint = value;
public void setForeignKeyName(String value) {
this.foreignKeyName = value;
}
/**
* Gets the value of the newCheckConstraint property.
* Gets the value of the foreignKeyIndex property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getNewCheckConstraint() {
return newCheckConstraint;
public String getForeignKeyIndex() {
return foreignKeyIndex;
}
/**
* Sets the value of the newCheckConstraint property.
* Sets the value of the foreignKeyIndex property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setNewCheckConstraint(String value) {
this.newCheckConstraint = value;
public void setForeignKeyIndex(String value) {
this.foreignKeyIndex = value;
}
/**
* Gets the value of the oldReferences property.
* Gets the value of the dropForeignKey property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getOldReferences() {
return oldReferences;
public String getDropForeignKey() {
return dropForeignKey;
}
/**
* Sets the value of the oldReferences property.
* Sets the value of the dropForeignKey property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setOldReferences(String value) {
this.oldReferences = value;
public void setDropForeignKey(String value) {
this.dropForeignKey = value;
}
/**
* Gets the value of the newReferences property.
* Gets the value of the dropForeignKeyIndex property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getNewReferences() {
return newReferences;
public String getDropForeignKeyIndex() {
return dropForeignKeyIndex;
}
/**
* Sets the value of the newReferences property.
* Sets the value of the dropForeignKeyIndex property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setNewReferences(String value) {
this.newReferences = value;
public void setDropForeignKeyIndex(String value) {
this.dropForeignKeyIndex = value;
}
}
@@ -22,13 +22,16 @@ import javax.xml.bind.annotation.XmlValue;
* &lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="defaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="notnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="checkConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="historyExclude" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="uniqueOneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="primaryKey" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="identity" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="checkConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="checkConstraintName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="uniqueOneToOne" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="references" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="foreignKeyIndex" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
@@ -54,20 +57,26 @@ public class Column {
protected String defaultValue;
@XmlAttribute(name = "notnull")
protected Boolean notnull;
@XmlAttribute(name = "checkConstraint")
protected String checkConstraint;
@XmlAttribute(name = "historyExclude")
protected Boolean historyExclude;
@XmlAttribute(name = "unique")
protected Boolean unique;
@XmlAttribute(name = "uniqueOneToOne")
protected Boolean uniqueOneToOne;
@XmlAttribute(name = "primaryKey")
protected Boolean primaryKey;
@XmlAttribute(name = "identity")
protected Boolean identity;
@XmlAttribute(name = "checkConstraint")
protected String checkConstraint;
@XmlAttribute(name = "checkConstraintName")
protected String checkConstraintName;
@XmlAttribute(name = "unique")
protected String unique;
@XmlAttribute(name = "uniqueOneToOne")
protected String uniqueOneToOne;
@XmlAttribute(name = "references")
protected String references;
@XmlAttribute(name = "foreignKeyName")
protected String foreignKeyName;
@XmlAttribute(name = "foreignKeyIndex")
protected String foreignKeyIndex;
@XmlAttribute(name = "comment")
protected String comment;
@@ -191,30 +200,6 @@ public class Column {
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 historyExclude property.
*
@@ -239,54 +224,6 @@ public class Column {
this.historyExclude = 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 uniqueOneToOne property.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isUniqueOneToOne() {
return uniqueOneToOne;
}
/**
* Sets the value of the uniqueOneToOne property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setUniqueOneToOne(Boolean value) {
this.uniqueOneToOne = value;
}
/**
* Gets the value of the primaryKey property.
*
@@ -335,6 +272,102 @@ public class Column {
this.identity = 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 checkConstraintName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getCheckConstraintName() {
return checkConstraintName;
}
/**
* Sets the value of the checkConstraintName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setCheckConstraintName(String value) {
this.checkConstraintName = value;
}
/**
* Gets the value of the unique property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getUnique() {
return unique;
}
/**
* Sets the value of the unique property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setUnique(String value) {
this.unique = value;
}
/**
* Gets the value of the uniqueOneToOne property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getUniqueOneToOne() {
return uniqueOneToOne;
}
/**
* Sets the value of the uniqueOneToOne property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setUniqueOneToOne(String value) {
this.uniqueOneToOne = value;
}
/**
* Gets the value of the references property.
*
@@ -359,6 +392,54 @@ public class Column {
this.references = value;
}
/**
* Gets the value of the foreignKeyName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getForeignKeyName() {
return foreignKeyName;
}
/**
* Sets the value of the foreignKeyName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setForeignKeyName(String value) {
this.foreignKeyName = value;
}
/**
* Gets the value of the foreignKeyIndex property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getForeignKeyIndex() {
return foreignKeyIndex;
}
/**
* Sets the value of the foreignKeyIndex property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setForeignKeyIndex(String value) {
this.foreignKeyIndex = value;
}
/**
* Gets the value of the comment property.
*
@@ -34,6 +34,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceInitial" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="sequenceAllocate" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="pkName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -68,6 +69,8 @@ public class CreateTable {
@XmlAttribute(name = "sequenceAllocate")
@XmlSchemaType(name = "positiveInteger")
protected BigInteger sequenceAllocate;
@XmlAttribute(name = "pkName")
protected String pkName;
@XmlAttribute(name = "tablespace")
protected String tablespace;
@XmlAttribute(name = "indexTablespace")
@@ -306,6 +309,30 @@ public class CreateTable {
this.sequenceAllocate = value;
}
/**
* Gets the value of the pkName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getPkName() {
return pkName;
}
/**
* Sets the value of the pkName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setPkName(String value) {
this.pkName = value;
}
/**
* Gets the value of the tablespace property.
*
@@ -17,9 +17,11 @@ import javax.xml.bind.annotation.XmlType;
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refColumnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refTableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="indexName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -32,12 +34,40 @@ import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "foreignKey")
public class ForeignKey {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "refColumnNames", required = true)
protected String refColumnNames;
@XmlAttribute(name = "refTableName", required = true)
protected String refTableName;
@XmlAttribute(name = "indexName")
protected String indexName;
/**
* 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 columnNames property.
@@ -111,4 +141,28 @@ public class ForeignKey {
this.refTableName = value;
}
/**
* Gets the value of the indexName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getIndexName() {
return indexName;
}
/**
* Sets the value of the indexName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setIndexName(String value) {
this.indexName = value;
}
}
@@ -17,8 +17,8 @@ import javax.xml.bind.annotation.XmlType;
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="constraintName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -31,10 +31,34 @@ import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "uniqueConstraint")
public class UniqueConstraint {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "constraintName")
protected String constraintName;
/**
* 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 columnNames property.
@@ -60,28 +84,4 @@ public class UniqueConstraint {
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;
}
}
@@ -2,6 +2,8 @@ package com.avaje.ebean.dbmigration.model;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.Migration;
import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter;
@@ -21,6 +23,10 @@ public class CurrentModel {
private final SpiEbeanServer server;
private final DbConstraintNaming constraintNaming;
private final PlatformDdl platformDdl;
private ModelContainer model;
private ChangeSet changeSet;
@@ -32,6 +38,8 @@ public class CurrentModel {
*/
public CurrentModel(SpiEbeanServer server) {
this.server = server;
this.platformDdl = server.getDatabasePlatform().getPlatformDdl();
this.constraintNaming = server.getServerConfig().getConstraintNaming();
}
/**
@@ -40,7 +48,7 @@ public class CurrentModel {
public ModelContainer read() {
if (model == null) {
model = new ModelContainer();
ModelBuildContext context = new ModelBuildContext(model);
ModelBuildContext context = new ModelBuildContext(model, constraintNaming, platformDdl);
ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context);
VisitAllUsing visit = new VisitAllUsing(visitor, server);
visit.visitAllBeans();
@@ -124,7 +132,7 @@ public class CurrentModel {
* Return the platform specific DdlHandler (to generate DDL).
*/
private DdlHandler handler() {
return server.getDatabasePlatform().createDdlHandler();
return server.getDatabasePlatform().createDdlHandler(server.getServerConfig());
}
/**
@@ -11,31 +11,39 @@ public class MColumn {
private final String name;
private final String type;
private String checkConstraint;
private String checkConstraintName;
private String defaultValue;
private String references;
private String foreignKeyName;
private String foreignKeyIndex;
private boolean historyExclude;
private boolean notnull;
private boolean primaryKey;
private boolean identity;
private boolean unique;
private String unique;
/**
* Special unique for OneToOne as we need to handle that different
* specifically for MsSqlServer.
*/
private boolean uniqueOneToOne;
private String uniqueOneToOne;
public MColumn(Column column) {
this.name = column.getName();
this.type = column.getType();
this.checkConstraint = column.getCheckConstraint();
this.checkConstraintName = column.getCheckConstraintName();
this.defaultValue = column.getDefaultValue();
this.references = column.getReferences();
this.foreignKeyName = column.getForeignKeyName();
this.foreignKeyIndex = column.getForeignKeyIndex();
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());
this.unique = column.getUnique();
this.uniqueOneToOne = column.getUniqueOneToOne();
this.historyExclude = Boolean.TRUE.equals(column.isHistoryExclude());
}
@@ -82,6 +90,30 @@ public class MColumn {
this.checkConstraint = checkConstraint;
}
public String getCheckConstraintName() {
return checkConstraintName;
}
public void setCheckConstraintName(String checkConstraintName) {
this.checkConstraintName = checkConstraintName;
}
public String getForeignKeyName() {
return foreignKeyName;
}
public void setForeignKeyName(String foreignKeyName) {
this.foreignKeyName = foreignKeyName;
}
public String getForeignKeyIndex() {
return foreignKeyIndex;
}
public void setForeignKeyIndex(String foreignKeyIndex) {
this.foreignKeyIndex = foreignKeyIndex;
}
public String getDefaultValue() {
return defaultValue;
}
@@ -114,11 +146,11 @@ public class MColumn {
this.historyExclude = historyExclude;
}
public void setUnique(boolean unique) {
public void setUnique(String unique) {
this.unique = unique;
}
public boolean isUnique() {
public String getUnique() {
return unique;
}
@@ -126,14 +158,14 @@ public class MColumn {
* Set unique specifically for OneToOne mapping.
* We need special DDL for this case for MsSqlServer.
*/
public void setUniqueOneToOne(boolean uniqueOneToOne) {
public void setUniqueOneToOne(String uniqueOneToOne) {
this.uniqueOneToOne = uniqueOneToOne;
}
/**
* Return true if this is unique for a OneToOne.
*/
public boolean isUniqueOneToOne() {
public String getUniqueOneToOne() {
return uniqueOneToOne;
}
@@ -142,16 +174,20 @@ public class MColumn {
Column c = new Column();
c.setName(name);
c.setType(type);
if (notnull) c.setNotnull(true);
if (unique) c.setUnique(true);
if (uniqueOneToOne) c.setUniqueOneToOne(true);
if (primaryKey) c.setPrimaryKey(true);
if (identity) c.setIdentity(true);
if (historyExclude) c.setHistoryExclude(true);
c.setCheckConstraint(checkConstraint);
c.setCheckConstraintName(checkConstraintName);
c.setReferences(references);
c.setForeignKeyName(foreignKeyName);
c.setForeignKeyIndex(foreignKeyIndex);
c.setDefaultValue(defaultValue);
c.setUnique(unique);
c.setUniqueOneToOne(uniqueOneToOne);
return c;
}
@@ -160,6 +196,10 @@ public class MColumn {
return (val1 == null) ? val2 != null : !val1.equals(val2);
}
private boolean hasValue(String val) {
return val != null && !val.isEmpty();
}
AlterColumn alterColumn;
private AlterColumn getAlterColumn(String tableName) {
@@ -175,6 +215,7 @@ public class MColumn {
String tableName = table.getName();
// set to null and check at the end
this.alterColumn = null;
if (different(type, newColumn.type)) {
@@ -188,27 +229,53 @@ public class MColumn {
}
if (different(defaultValue, newColumn.defaultValue)) {
AlterColumn alter = getAlterColumn(tableName);
alter.setOldDefaultValue(defaultValue);
alter.setNewDefaultValue(newColumn.defaultValue);
}
if (different(checkConstraint, newColumn.checkConstraint)) {
AlterColumn alter = getAlterColumn(tableName);
alter.setOldCheckConstraint(checkConstraint);
alter.setNewCheckConstraint(newColumn.checkConstraint);
}
if (different(references, newColumn.references)) {
AlterColumn alter = getAlterColumn(tableName);
alter.setOldReferences(references);
alter.setNewReferences(newColumn.references);
alter.setDefaultValue(newColumn.defaultValue);
}
if (unique != newColumn.unique) {
if (different(checkConstraint, newColumn.checkConstraint)) {
AlterColumn alter = getAlterColumn(tableName);
alter.setUnique(newColumn.unique);
if (hasValue(checkConstraint)) {
alter.setDropCheckConstraint(checkConstraintName);
}
if (hasValue(newColumn.checkConstraint)) {
alter.setCheckConstraintName(newColumn.checkConstraintName);
alter.setCheckConstraint(newColumn.checkConstraint);
}
}
if (uniqueOneToOne != newColumn.uniqueOneToOne) {
if (different(references, newColumn.references)) {
// foreign key change
AlterColumn alter = getAlterColumn(tableName);
alter.setUniqueOneToOne(newColumn.uniqueOneToOne);
if (hasValue(foreignKeyName)) {
alter.setDropForeignKey(foreignKeyName);
}
if (hasValue(foreignKeyIndex)) {
alter.setDropForeignKeyIndex(foreignKeyIndex);
}
if (hasValue(newColumn.references)) {
// add new foreign key constraint
alter.setReferences(newColumn.references);
alter.setForeignKeyName(newColumn.foreignKeyName);
alter.setForeignKeyIndex(newColumn.foreignKeyIndex);
}
}
if (different(unique, newColumn.unique)) {
AlterColumn alter = getAlterColumn(tableName);
if (hasValue(unique)) {
alter.setDropUnique(unique);
}
if (hasValue(newColumn.unique)) {
alter.setUnique(newColumn.unique);
}
}
if (different(uniqueOneToOne, newColumn.uniqueOneToOne)) {
AlterColumn alter = getAlterColumn(tableName);
if (hasValue(uniqueOneToOne)) {
alter.setDropUnique(uniqueOneToOne);
}
if (hasValue(newColumn.uniqueOneToOne)) {
alter.setUniqueOneToOne(newColumn.uniqueOneToOne);
}
}
if (alterColumn != null) {
@@ -1,6 +1,5 @@
package com.avaje.ebean.dbmigration.model;
import com.avaje.ebean.dbmigration.migration.Column;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
import java.util.ArrayList;
@@ -9,33 +8,60 @@ import java.util.List;
/**
* A unique constraint for multiple columns.
* <p>
* Note that unique constraint on a single column is instead
* a boolean flag on the associated MColumn.
* Note that unique constraint on a single column is instead
* a boolean flag on the associated MColumn.
* </p>
*/
public class MCompoundForeignKey {
private final String name;
private final String referenceTable;
private final List<String> columns = new ArrayList<String>();
private final List<String> referenceColumns = new ArrayList<String>();
private String indexName;
public MCompoundForeignKey(String referenceTable) {
public MCompoundForeignKey(String name, String referenceTable, String indexName) {
this.name = name;
this.referenceTable = referenceTable;
this.indexName = indexName;
}
/**
* Add a column pair of local and referenced column.
*/
public void addColumnPair(String dbCol, String refColumn) {
columns.add(dbCol);
referenceColumns.add(refColumn);
}
/**
* Create and return an ForeignKey migration element.
*/
public ForeignKey createForeignKey() {
ForeignKey fk = new ForeignKey();
fk.setName(name);
fk.setIndexName(indexName);
fk.setColumnNames(toColumnNames(columns));
fk.setRefColumnNames(toColumnNames(referenceColumns));
fk.setRefTableName(referenceTable);
return fk;
}
/**
* Return the columns making up the foreign key in order.
*/
public List<String> getColumns() {
return columns;
}
/**
* Set the associated index name. Note that setting to null has the effect
* of indicating an associated index should not be created for this foreign key.
*/
public void setIndexName(String indexName) {
this.indexName = indexName;
}
/**
* Return as an array of string column names.
*/
@@ -9,6 +9,8 @@ package com.avaje.ebean.dbmigration.model;
*/
public class MCompoundUniqueConstraint {
private final String name;
/**
* Flag if true indicates this was specifically created for a OneToOne mapping.
*/
@@ -19,7 +21,8 @@ public class MCompoundUniqueConstraint {
*/
private final String[] columns;
public MCompoundUniqueConstraint(String[] columns, boolean oneToOne) {
public MCompoundUniqueConstraint(String[] columns, boolean oneToOne, String name) {
this.name = name;
this.columns = columns;
this.oneToOne = oneToOne;
}
@@ -38,4 +41,10 @@ public class MCompoundUniqueConstraint {
return oneToOne;
}
/**
* Return the constraint name.
*/
public String getName() {
return name;
}
}
@@ -34,6 +34,8 @@ public class MTable {
private final String name;
private String pkName;
private String comment;
private String tablespace;
@@ -65,6 +67,7 @@ public class MTable {
*/
public MTable(CreateTable createTable) {
this.name = createTable.getName();
this.pkName = createTable.getPkName();
this.comment = createTable.getComment();
this.tablespace = createTable.getTablespace();
this.indexTablespace = createTable.getIndexTablespace();
@@ -90,6 +93,7 @@ public class MTable {
CreateTable createTable = new CreateTable();
createTable.setName(name);
createTable.setPkName(pkName);
createTable.setComment(comment);
createTable.setTablespace(tablespace);
createTable.setIndexTablespace(indexTablespace);
@@ -165,6 +169,14 @@ public class MTable {
return name;
}
public String getPkName() {
return pkName;
}
public void setPkName(String pkName) {
this.pkName = pkName;
}
public String getComment() {
return comment;
}
@@ -273,19 +285,19 @@ public class MTable {
/**
* Add a compound unique constraint.
*/
public void addCompoundUniqueConstraint(String[] columns, boolean oneToOne) {
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns, oneToOne));
public void addCompoundUniqueConstraint(String[] columns, boolean oneToOne, String constraintName) {
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns, oneToOne, constraintName));
}
/**
* Add a compound unique constraint.
*/
public void addCompoundUniqueConstraint(List<MColumn> columns, boolean oneToOne) {
public void addCompoundUniqueConstraint(List<MColumn> columns, boolean oneToOne, String constraintName) {
String[] cols = new String[columns.size()];
for (int i = 0; i < columns.size(); i++) {
cols[i] = columns.get(i).getName();
}
addCompoundUniqueConstraint(cols, oneToOne);
addCompoundUniqueConstraint(cols, oneToOne, constraintName);
}
public void addForeignKey(MCompoundForeignKey compoundKey) {
@@ -8,7 +8,6 @@ import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor;
import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
/**
@@ -52,14 +51,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
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(), false);
}
}
return new ModelBuildPropertyVisitor(ctx, table);
return new ModelBuildPropertyVisitor(ctx, table, descriptor.getCompoundUniqueConstraints());
}
private void setIdentity(BeanDescriptor<?> descriptor, MTable table) {
@@ -2,6 +2,8 @@ package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.DbConstraintNaming;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.ModelContainer;
@@ -20,14 +22,52 @@ public class ModelBuildContext {
private final ModelContainer model;
public ModelBuildContext(ModelContainer model) {
private final DbConstraintNaming constraintNaming;
private final PlatformDdl platformDdl;
public ModelBuildContext(ModelContainer model, DbConstraintNaming constraintNaming, PlatformDdl platformDdl) {
this.model = model;
this.constraintNaming = constraintNaming;
this.platformDdl = platformDdl;
}
public String primaryKeyName(String tableName) {
return maxLength(constraintNaming.primaryKeyName(tableName), 0);
}
public String foreignKeyConstraintName(String tableName, String columnName, int foreignKeyCount) {
return maxLength(constraintNaming.foreignKeyConstraintName(tableName, columnName), foreignKeyCount);
}
public String foreignKeyIndexName(String tableName, String[] columns, int indexCount) {
return maxLength(constraintNaming.foreignKeyIndexName(tableName, columns), indexCount);
}
public String foreignKeyIndexName(String tableName, String column, int indexCount) {
return maxLength(constraintNaming.foreignKeyIndexName(tableName, column), indexCount);
}
public String uniqueConstraintName(String tableName, String columnName, int indexCount) {
return maxLength(constraintNaming.uniqueConstraintName(tableName, columnName), indexCount);
}
public String uniqueConstraintName(String tableName, String[] columnNames, int indexCount) {
return maxLength(constraintNaming.uniqueConstraintName(tableName, columnNames), indexCount);
}
public String checkConstraintName(String tableName, String columnName, int checkCount) {
return maxLength(constraintNaming.checkConstraintName(tableName, columnName), checkCount);
}
public void addTable(MTable table) {
model.addTable(table);
}
private String maxLength(String constraintName, int indexCount) {
return platformDdl.maxLength(constraintName, indexCount);
}
/**
* Return the map used to determine the DB specific type
* for a given bean property.
@@ -57,12 +97,6 @@ public class ModelBuildContext {
return dbTypeMap.get(jdbcType);
}
// ScalarType<Object> scalarType = p.getScalarType();
// if (scalarType == null) {
// throw new RuntimeException("No scalarType for " + p.getFullBeanName());
// }
// return dbTypeMap.get(scalarType.getJdbcType());
// can be the logical JSON types (JSON, JSONB, JSONClob, JSONBlob, JSONVarchar)
int dbType = p.getDbType();
if (dbType == 0) {
@@ -23,6 +23,8 @@ public class ModelBuildIntersectionTable {
private MTable intersectionTable;
private int countForeignKey;
public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
this.ctx = ctx;
this.manyProp = manyProp;
@@ -30,7 +32,7 @@ public class ModelBuildIntersectionTable {
this.tableJoin = manyProp.getTableJoin();
}
public void build() {
public void build() {
intersectionTable = createTable();
ctx.addTable(intersectionTable);
@@ -42,66 +44,38 @@ public class ModelBuildIntersectionTable {
BeanDescriptor<?> localDesc = manyProp.getBeanDescriptor();
buildFkConstraints(localDesc, intersectionTableJoin.columns(), true);
//ctx.addIntersectionTableFk(fk1);
BeanDescriptor<?> targetDesc = manyProp.getTargetDescriptor();
buildFkConstraints(targetDesc, tableJoin.columns(), false);
//ctx.addIntersectionTableFk(fk2);
}
private void buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
String tableName = intersectionTableJoin.getTable();
String baseTable = desc.getBaseTable();
// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable();
//
// fkName = getFkNameWithSuffix(fkName);
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, ++countForeignKey);
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, countForeignKey);
MCompoundForeignKey foreignKey = new MCompoundForeignKey(desc.getBaseTable());
MCompoundForeignKey foreignKey = new MCompoundForeignKey(fkName, desc.getBaseTable(), fkIndex);
intersectionTable.addForeignKey(foreignKey);
// 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 localCol = direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn();
String refCol = !direction ? columns[i].getForeignDbColumn() : columns[i].getLocalDbColumn();
foreignKey.addColumnPair(localCol, refCol);
}
// 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());
String tableName = intersectionTableJoin.getTable();
MTable table = new MTable(tableName);
table.setPkName(ctx.primaryKeyName(tableName));
TableJoinColumn[] columns = intersectionTableJoin.columns();
for (int i = 0; i < columns.length; i++) {
@@ -125,7 +99,6 @@ public class ModelBuildIntersectionTable {
MColumn col = new MColumn(column, ctx.getColumnDefn(p), true);
col.setPrimaryKey(true);
table.addColumn(col);
}
@@ -1,15 +1,17 @@
package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.IndexSet;
import com.avaje.ebean.dbmigration.model.MColumn;
import com.avaje.ebean.dbmigration.model.MCompoundForeignKey;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.visitor.BaseTablePropertyVisitor;
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.CompoundUniqueContraint;
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;
@@ -20,17 +22,69 @@ import java.util.List;
*/
public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
private final ModelBuildContext ctx;
protected final ModelBuildContext ctx;
private final MTable table;
private final IndexSet indexSet = new IndexSet();
private MColumn lastColumn;
public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table) {
private int countForeignKey;
private int countIndex;
private int countUnique;
private int countCheck;
public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table, CompoundUniqueContraint[] compoundUniqueConstraints) {
this.ctx = ctx;
this.table = table;
addCompoundUniqueConstraint(compoundUniqueConstraints);
}
/**
* Add unique constraints defined via JPA UniqueConstraint annotations.
*/
private void addCompoundUniqueConstraint(CompoundUniqueContraint[] compoundUniqueConstraints) {
if (compoundUniqueConstraints != null) {
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
String[] columns = compoundUniqueConstraints[i].getColumns();
String uqName = determineUniqueConstraintName(columns);
table.addCompoundUniqueConstraint(columns, false, uqName);
indexSet.add(columns);
}
}
}
@Override
public void visitEnd() {
// set the primary key name
table.setPkName(determinePrimaryKeyName());
// check if indexes on foreign keys should be suppressed
for (MColumn column : table.getColumns().values()) {
if (hasValue(column.getForeignKeyIndex())) {
if (indexSet.contains(column.getName())) {
// suppress index on foreign key as there is already
// effectively an index (probably via unique constraint)
column.setForeignKeyIndex(null);
}
}
}
for (MCompoundForeignKey compoundKey : table.getCompoundKeys()) {
if (indexSet.contains(compoundKey.getColumns())) {
// suppress index on foreign key as there is already
// effectively an index (probably via unique constraint)
compoundKey.setIndexName(null);
}
}
}
@Override
public void visitMany(BeanPropertyAssocMany<?> p) {
if (p.isManyToMany()) {
@@ -85,7 +139,9 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
if (columns.length > 1) {
// compound foreign key
String refTable = p.getTargetDescriptor().getBaseTable();
compoundKey = new MCompoundForeignKey(refTable);
String fkName = determineForeignKeyConstraintName(p.getName());
String fkIndex = determineForeignKeyIndexName(p.getName());
compoundKey = new MCompoundForeignKey(fkName, refTable, fkIndex);
table.addForeignKey(compoundKey);
}
@@ -109,6 +165,8 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
refTable = p.getTargetDescriptor().getBaseTable();
}
col.setReferences(refTable + "." + refColumn);
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
col.setForeignKeyIndex(determineForeignKeyIndexName(col.getName()));
} else {
compoundKey.addColumnPair(dbCol, refColumn);
}
@@ -119,14 +177,19 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
// adding the unique constraint restricts the cardinality from OneToMany down to OneToOne
// for MsSqlServer we need different DDL to handle NULL values on this constraint
if (modelColumns.size() == 1) {
modelColumns.get(0).setUniqueOneToOne(true);
MColumn col = modelColumns.get(0);
col.setUniqueOneToOne(determineUniqueConstraintName(col.getName()));
indexSetAdd(col.getName());
} else {
table.addCompoundUniqueConstraint(modelColumns, true);
String uqName = determineUniqueConstraintName(p.getName());
table.addCompoundUniqueConstraint(modelColumns, true, uqName);
indexSetAdd(modelColumns);
}
}
}
@Override
@Override
public void visitScalar(BeanProperty p) {
if (p.isSecondaryTable()) {
@@ -146,12 +209,89 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
}
if (p.isUnique() && !p.isId()) {
col.setUnique(true);
col.setUnique(determineUniqueConstraintName(col.getName()));
indexSetAdd(col.getName());
}
String checkConstraint = p.getDbConstraintExpression();
if (checkConstraint != null) {
col.setCheckConstraint(checkConstraint);
col.setCheckConstraintName(determineCheckConstraintName(col.getName()));
}
col.setCheckConstraint(p.getDbConstraintExpression());
lastColumn = col;
table.addColumn(col);
}
private void indexSetAdd(String column) {
indexSet.add(column);
}
private void indexSetAdd(List<MColumn> modelColumns) {
String[] cols = new String[modelColumns.size()];
for (int i = 0; i < modelColumns.size(); i++) {
cols[i] = modelColumns.get(i).getName();
}
indexSet.add(cols);
}
/**
* Return the primary key constraint name.
*/
protected String determinePrimaryKeyName() {
return ctx.primaryKeyName(table.getName());
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
protected String determineForeignKeyConstraintName(String columnName) {
return ctx.foreignKeyConstraintName(table.getName(), columnName, ++countForeignKey);
}
protected String determineForeignKeyIndexName(String column) {
String[] cols = {column};
return determineForeignKeyIndexName(cols);
}
/**
* Return the foreign key constraint name given a single column foreign key.
*/
protected String determineForeignKeyIndexName(String[] columns) {
return ctx.foreignKeyIndexName(table.getName(), columns, ++countIndex);
}
/**
* Return the unique constraint name.
*/
protected String determineUniqueConstraintName(String columnName) {
return ctx.uniqueConstraintName(table.getName(), columnName, ++countUnique);
}
/**
* Return the unique constraint name.
*/
protected String determineUniqueConstraintName(String[] columnNames) {
return ctx.uniqueConstraintName(table.getName(), columnNames, ++countUnique);
}
/**
* Return the constraint name.
*/
protected String determineCheckConstraintName(String columnName) {
return ctx.checkConstraintName(table.getName(), columnName, ++countCheck);
}
private boolean hasValue(String val) {
return val != null && !val.isEmpty();
}
}
@@ -10,6 +10,11 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound;
*/
public interface BeanPropertyVisitor {
/**
* Completed visiting all the properties on the bean.
*/
void visitEnd();
/**
* Visit a OneToMany or ManyToMany property.
*/
@@ -13,4 +13,5 @@ public interface BeanVisitor {
* property on the entity bean (return null to skip visiting this bean).
*/
BeanPropertyVisitor visitBean(BeanDescriptor<?> descriptor);
}
@@ -72,6 +72,7 @@ public class VisitAllUsing {
}
visitInheritanceProperties(desc, propertyVisitor);
propertyVisitor.visitEnd();
}
}