DB migration initial

This commit is contained in:
Robin Bygrave
2015-08-04 23:23:32 +12:00
parent 009f91b949
commit 22407506ff
25 changed files with 2844 additions and 382 deletions
@@ -13,7 +13,8 @@ public class H2Platform extends DatabasePlatform {
public H2Platform() {
super();
this.name = "h2";
this.platformDdl = new H2Ddl(this.dbTypeMap);
boolean useSequences = true;
this.platformDdl = new H2Ddl(this.dbTypeMap, useSequences);
this.dbEncrypt = new H2DbEncrypt();
// like ? escape'' not working in the latest version H2 so just using no
// escape clause for now noting that backslash is an escape char for like in H2
@@ -6,10 +6,10 @@ import com.avaje.ebean.dbmigration.ddlgeneration.TableDdl;
import com.avaje.ebean.dbmigration.migration.Column;
import com.avaje.ebean.dbmigration.migration.CreateTable;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
import com.avaje.ebean.dbmigration.migration.PrimaryKey;
import com.avaje.ebean.dbmigration.model.MTable;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
@@ -54,29 +54,40 @@ public class BaseTableDdl implements TableDdl {
if (!pk.isEmpty()) {
// defined on the columns
writePrimaryKeyConstraint(apply, tableName, toColumnNames(pk));
} else {
// defined on the table
if (createTable.getPrimaryKey() == null) {
System.out.print("asd");
} else {
writePrimaryKeyConstraint(apply, tableName, createTable.getPrimaryKey());
}
}
apply.newLine().append(")").endOfStatement();
// add drop table to the rollback buffer - do this before
// we drop the related sequence (if sequences are used)
dropTable(writer.rollback(), tableName);
writeSequence(writer, createTable);
// add blank line for a bit of whitespace between tables
apply.end();
writer.rollback().end();
writeAddForeignKeys(writer, createTable);
// add drop table to the rollback buffer
dropTable(writer.rollback(), tableName);
if (isTrue(createTable.isWithHistory())) {
createWithHistory(writer, createTable.getName());
}
}
private void writeSequence(DdlWrite writer, CreateTable createTable) throws IOException {
String name = createTable.getSequenceName();
int initial = toInt(createTable.getSequenceInitial());
int allocate = toInt(createTable.getSequenceAllocate());
String createSeq = platformDdl.createSequence(name, initial, allocate);
if (createSeq != null) {
writer.apply().append(createSeq).newLine();
writer.rollback().append(platformDdl.dropSequence(name));
}
}
private void createWithHistory(DdlWrite writer, String name) throws IOException {
MTable table = writer.getTable(name);
@@ -183,7 +194,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected void dropTable(DdlBuffer buffer, String tableName) throws IOException {
buffer.append("drop table ").append(tableName).endOfStatement().end();
buffer.append("drop table ").append(tableName).endOfStatement();
}
/**
@@ -243,13 +254,6 @@ public class BaseTableDdl implements TableDdl {
buffer.append(")");
}
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String tableName, PrimaryKey pk) throws IOException {
String columnNames = pk.getColumnNames();
String[] cols = columnNames.split(",");
writePrimaryKeyConstraint(buffer, tableName, cols);
}
/**
* Write the primary key constraint inline with the create table statement.
*/
@@ -390,5 +394,8 @@ public class BaseTableDdl implements TableDdl {
return Boolean.TRUE.equals(value);
}
private int toInt(BigInteger value) {
return (value == null) ? 0 : value.intValue();
}
}
@@ -7,8 +7,10 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
*/
public class H2Ddl extends PlatformDdl {
public H2Ddl(DbTypeMap platformTypes) {
public H2Ddl(DbTypeMap platformTypes, boolean useSequences) {
super(platformTypes, new H2HistoryDdl());
this.foreignKeyRestrict = "on delete restrict on update restrict";
this.useSequences = useSequences;
}
}
@@ -17,6 +17,8 @@ public class PlatformDdl {
protected String foreignKeyRestrict = "";
protected boolean useSequences;
public PlatformDdl(DbTypeMap platformTypes, PlatformHistoryDdl historyDdl) {
this.typeConverter = new PlatformTypeConverter(platformTypes);
this.historyDdl = historyDdl;
@@ -50,4 +52,31 @@ public class PlatformDdl {
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
historyDdl.createWithHistory(writer, table);
}
/**
* Generate and return the create sequence DDL.
*/
public String createSequence(String sequenceName, int initialValue, int allocationSize) {
if (!useSequences || sequenceName == null || sequenceName.trim().length() == 0) {
return null;
}
StringBuilder sb = new StringBuilder("create sequence ");
sb.append(sequenceName);
if (initialValue > 1) {
sb.append(" start with ").append(initialValue);
}
if (allocationSize > 0 && allocationSize != 50) {
// at this stage ignoring allocationSize 50 as this is the 'default' and
// not consistent with the way Ebean batch fetches sequence values
sb.append(" increment by ").append(allocationSize);
}
sb.append(";");
return sb.toString();
}
public String dropSequence(String sequenceName) {
return "drop sequence "+sequenceName+";";
}
}
@@ -8,8 +8,13 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
public class PostgresDdl extends PlatformDdl {
public PostgresDdl(DbTypeMap platformTypes) {
this(platformTypes, false);
}
public PostgresDdl(DbTypeMap platformTypes, boolean useSequences) {
super(platformTypes, new PostgresHistoryDdl());
this.foreignKeyRestrict = "on delete restrict on update restrict";
this.useSequences = useSequences;
}
/**
@@ -1,6 +1,7 @@
package com.avaje.ebean.dbmigration.migration;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
@@ -8,6 +9,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -24,11 +26,13 @@ import javax.xml.bind.annotation.XmlType;
* <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}column" maxOccurs="unbounded"/>
* <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}uniqueConstraint" maxOccurs="unbounded" minOccurs="0"/>
* <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}foreignKey" maxOccurs="unbounded" minOccurs="0"/>
* <element ref="{http://ebean-orm.github.io/xml/ns/dbmigration}primaryKey" minOccurs="0"/>
* </sequence>
* <attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
* <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* <attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* <attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
* <attribute name="sequenceInitial" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* <attribute name="sequenceAllocate" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* </restriction>
* </complexContent>
* </complexType>
@@ -40,8 +44,7 @@ import javax.xml.bind.annotation.XmlType;
@XmlType(name = "", propOrder = {
"column",
"uniqueConstraint",
"foreignKey",
"primaryKey"
"foreignKey"
})
@XmlRootElement(name = "createTable")
public class CreateTable {
@@ -50,11 +53,18 @@ public class CreateTable {
protected List<Column> column;
protected List<UniqueConstraint> uniqueConstraint;
protected List<ForeignKey> foreignKey;
protected PrimaryKey primaryKey;
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "withHistory")
protected Boolean withHistory;
@XmlAttribute(name = "sequenceName")
protected String sequenceName;
@XmlAttribute(name = "sequenceInitial")
@XmlSchemaType(name = "positiveInteger")
protected BigInteger sequenceInitial;
@XmlAttribute(name = "sequenceAllocate")
@XmlSchemaType(name = "positiveInteger")
protected BigInteger sequenceAllocate;
@XmlAttribute(name = "tablespace")
protected String tablespace;
@XmlAttribute(name = "indexTablespace")
@@ -149,30 +159,6 @@ public class CreateTable {
return this.foreignKey;
}
/**
* Gets the value of the primaryKey property.
*
* @return
* possible object is
* {@link PrimaryKey }
*
*/
public PrimaryKey getPrimaryKey() {
return primaryKey;
}
/**
* Sets the value of the primaryKey property.
*
* @param value
* allowed object is
* {@link PrimaryKey }
*
*/
public void setPrimaryKey(PrimaryKey value) {
this.primaryKey = value;
}
/**
* Gets the value of the name property.
*
@@ -221,6 +207,78 @@ public class CreateTable {
this.withHistory = value;
}
/**
* Gets the value of the sequenceName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Sets the value of the sequenceName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setSequenceName(String value) {
this.sequenceName = value;
}
/**
* Gets the value of the sequenceInitial property.
*
* @return
* possible object is
* {@link BigInteger }
*
*/
public BigInteger getSequenceInitial() {
return sequenceInitial;
}
/**
* Sets the value of the sequenceInitial property.
*
* @param value
* allowed object is
* {@link BigInteger }
*
*/
public void setSequenceInitial(BigInteger value) {
this.sequenceInitial = value;
}
/**
* Gets the value of the sequenceAllocate property.
*
* @return
* possible object is
* {@link BigInteger }
*
*/
public BigInteger getSequenceAllocate() {
return sequenceAllocate;
}
/**
* Sets the value of the sequenceAllocate property.
*
* @param value
* allowed object is
* {@link BigInteger }
*
*/
public void setSequenceAllocate(BigInteger value) {
this.sequenceAllocate = value;
}
/**
* Gets the value of the tablespace property.
*
@@ -0,0 +1,114 @@
package com.avaje.ebean.dbmigration.migration;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &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;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "foreignKey")
public class ForeignKey {
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "refColumnNames", required = true)
protected String refColumnNames;
@XmlAttribute(name = "refTableName", required = true)
protected String refTableName;
/**
* Gets the value of the columnNames property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getColumnNames() {
return columnNames;
}
/**
* Sets the value of the columnNames property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setColumnNames(String value) {
this.columnNames = value;
}
/**
* Gets the value of the refColumnNames property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getRefColumnNames() {
return refColumnNames;
}
/**
* Sets the value of the refColumnNames property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setRefColumnNames(String value) {
this.refColumnNames = value;
}
/**
* Gets the value of the refTableName property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getRefTableName() {
return refTableName;
}
/**
* Sets the value of the refTableName property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setRefTableName(String value) {
this.refTableName = value;
}
}
@@ -61,14 +61,6 @@ public class ObjectFactory {
return new ForeignKey();
}
/**
* Create an instance of {@link PrimaryKey }
*
*/
public PrimaryKey createPrimaryKey() {
return new PrimaryKey();
}
/**
* Create an instance of {@link DropForeignKey }
*
@@ -6,6 +6,7 @@ import com.avaje.ebean.dbmigration.migration.CreateTable;
import com.avaje.ebean.dbmigration.migration.DropColumn;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -40,6 +41,9 @@ public class MTable {
private String tablespace;
private String indexTablespace;
private String sequenceName;
private int sequenceInitial;
private int sequenceAllocate;
private Boolean withHistory;
@@ -57,12 +61,16 @@ public class MTable {
this.tablespace = createTable.getTablespace();
this.indexTablespace = createTable.getIndexTablespace();
this.withHistory = createTable.isWithHistory();
this.sequenceName = createTable.getSequenceName();
this.sequenceInitial = toInt(createTable.getSequenceInitial());
this.sequenceAllocate = toInt(createTable.getSequenceAllocate());
List<Column> cols = createTable.getColumn();
for (Column column : cols) {
addColumn(column);
}
}
/**
* Construct typically from EbeanServer meta data.
*/
@@ -78,6 +86,9 @@ public class MTable {
createTable.setTablespace(tablespace);
createTable.setIndexTablespace(indexTablespace);
createTable.setWithHistory(withHistory);
createTable.setSequenceName(sequenceName);
createTable.setSequenceInitial(toBigInteger(sequenceInitial));
createTable.setSequenceAllocate(toBigInteger(sequenceAllocate));
for (MColumn column : this.columns.values()) {
createTable.getColumn().add(column.createColumn());
@@ -148,6 +159,30 @@ public class MTable {
return compoundKeys;
}
public String getSequenceName() {
return sequenceName;
}
public void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
public int getSequenceInitial() {
return sequenceInitial;
}
public void setSequenceInitial(int sequenceInitial) {
this.sequenceInitial = sequenceInitial;
}
public int getSequenceAllocate() {
return sequenceAllocate;
}
public void setSequenceAllocate(int sequenceAllocate) {
this.sequenceAllocate = sequenceAllocate;
}
/**
* Return the list of columns that make the primary key.
*/
@@ -203,4 +238,30 @@ public class MTable {
compoundKeys.add(compoundKey);
}
private int toInt(BigInteger value) {
return (value == null) ? 0 : value.intValue();
}
private BigInteger toBigInteger(int value) {
return (value == 0) ? null : BigInteger.valueOf(value);
}
/**
* Add a column checking if it already exists and if so return the existing column.
* Sometimes the case for a primaryKey that is also a foreign key.
*/
public MColumn addColumn(String dbCol, String columnDefn, boolean notnull) {
MColumn existingColumn = columns.get(dbCol);
if (existingColumn != null) {
if (notnull) {
existingColumn.setNotnull(true);
}
return existingColumn;
}
MColumn newCol = new MColumn(dbCol, columnDefn, notnull);
addColumn(newCol);
return newCol;
}
}
@@ -105,6 +105,11 @@ public class ModelContainer {
* Add a table (typically from reading EbeanServer meta data).
*/
public void addTable(MTable table) {
if (table.getName().equalsIgnoreCase("item")) {
System.out.println();
}
tables.put(table.getName(), table);
}
}
@@ -35,6 +35,10 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
MTable table = new MTable(descriptor.getBaseTable());
table.setSequenceName(descriptor.getSequenceName());
table.setSequenceInitial(descriptor.getSequenceInitialValue());
table.setSequenceAllocate(descriptor.getSequenceAllocationSize());
// add the table to the model
ctx.addTable(table);
@@ -5,6 +5,7 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.ModelContainer;
import com.avaje.ebeaninternal.server.type.ScalarType;
/**
* The context used during DDL generation.
@@ -49,14 +50,23 @@ public class ModelBuildContext {
if (p.isDbEncrypted()) {
return dbTypeMap.get(p.getDbEncryptedType());
}
if (p.isLocalEncrypted()) {
// scalar type potentially wrapping varbinary db type
ScalarType<Object> scalarType = p.getScalarType();
int jdbcType = scalarType.getJdbcType();
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) {
// ScalarType<Object> scalarType = p.getScalarType();
// if (scalarType == null) {
throw new RuntimeException("No scalarType for " + p.getFullBeanName());
// }
// dbType = scalarType.getJdbcType();
throw new RuntimeException("No scalarType defined for " + p.getFullBeanName());
}
return dbTypeMap.get(dbType);
}
@@ -1,5 +1,7 @@
package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
import com.avaje.ebean.dbmigration.model.MCompoundForeignKey;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
@@ -20,6 +22,8 @@ public class ModelBuildIntersectionTable {
private final TableJoin intersectionTableJoin;
private final TableJoin tableJoin;
private MTable intersectionTable;
public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
this.ctx = ctx;
this.manyProp = manyProp;
@@ -29,44 +33,49 @@ public class ModelBuildIntersectionTable {
public void build() {
MTable table = createTable();
ctx.addTable(table);
intersectionTable = createTable();
ctx.addTable(intersectionTable);
//buildFkConstraints();
buildFkConstraints();
}
// private void buildFkConstraints(MTable table) {
//
// BeanDescriptor<?> localDesc = manyProp.getBeanDescriptor();
// String fk1 = buildFkConstraints(localDesc, intersectionTableJoin.columns(), true);
// ctx.addIntersectionTableFk(fk1);
//
// BeanDescriptor<?> targetDesc = manyProp.getTargetDescriptor();
// String fk2 = buildFkConstraints(targetDesc, tableJoin.columns(), false);
// ctx.addIntersectionTableFk(fk2);
// }
private void buildFkConstraints() {
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 String buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
//
//
private void buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
// String fkName = "fk_"+intersectionTableJoin.getTable()+"_"+desc.getBaseTable();
//
// fkName = getFkNameWithSuffix(fkName);
//
MCompoundForeignKey foreignKey = new MCompoundForeignKey(desc.getBaseTable());
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++) {
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);
// }
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++) {
@@ -86,7 +95,7 @@ public class ModelBuildIntersectionTable {
// fkBuf.append(";").append(NEW_LINE);
//
// return fkBuf.toString();
// }
}
private MTable createTable() {
@@ -99,16 +99,20 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
String columnDefn = ctx.getColumnDefn(importedProperty);
String refColumn = importedProperty.getDbColumn();
MColumn col = new MColumn(dbCol, columnDefn, !p.isNullable());
MColumn col = table.addColumn(dbCol, columnDefn, !p.isNullable());
if (columns.length == 1) {
// single references column (put it on the column)
String refTable = importedProperty.getBeanDescriptor().getBaseTable();
if (refTable == null) {
// odd case where an EmbeddedId only has 1 property
refTable = p.getTargetDescriptor().getBaseTable();
}
col.setReferences(refTable + "." + refColumn);
} else {
compoundKey.addColumnPair(dbCol, refColumn);
}
modelColumns.add(col);
table.addColumn(col);
}
@@ -127,6 +131,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
public void visitScalar(BeanProperty p) {
if (p.isSecondaryTable()) {
lastColumn = null;
return;
}