Update for pg - Internal changes for DB Migration / DDL generation #369

This commit is contained in:
Robin Bygrave
2015-08-05 20:59:09 +12:00
parent 22407506ff
commit 689d201b04
92 changed files with 2976 additions and 661 deletions
@@ -7,6 +7,7 @@ import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.Query;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -171,6 +172,10 @@ public class DatabasePlatform {
return platformDdl;
}
public DdlHandler createDdlHandler() {
return platformDdl.createDdlHandler();
}
/**
* Return true if the JDBC driver does not allow additional queries to execute
* when a resultSet is being 'streamed' as is the case with findEach() etc.
@@ -1,5 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.dbmigration.migration.IdentityType;
/**
* Defines the identity/sequence behaviour for the database.
*/
@@ -114,4 +116,26 @@ public class DbIdentity {
this.idType = idType;
}
/**
* Determine the id type to use based on requested identityType and
* the support for that in the database platform.
*/
public IdType useIdentityType(IdentityType identityType) {
if (identityType == null) {
// use the default
return idType;
}
switch (identityType) {
case GENERATOR:
return IdType.GENERATOR;
case SEQUENCE:
return supportsSequence ? IdType.SEQUENCE : idType;
case IDENTITY:
return supportsIdentity ? IdType.IDENTITY : idType;
}
// use the default
return idType;
}
}
@@ -13,8 +13,6 @@ public class H2Platform extends DatabasePlatform {
public H2Platform() {
super();
this.name = "h2";
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
@@ -27,6 +25,8 @@ public class H2Platform extends DatabasePlatform {
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
this.platformDdl = new H2Ddl(this.dbTypeMap, dbIdentity);
this.openQuote = "\"";
this.closeQuote = "\"";
@@ -1,7 +1,7 @@
package com.avaje.ebean.config.dbplatform;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.H2Ddl;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
import javax.sql.DataSource;
@@ -18,14 +18,13 @@ public class PostgresPlatform extends DatabasePlatform {
public PostgresPlatform() {
super();
this.name = "postgres";
this.platformDdl = new PostgresDdl(this.dbTypeMap);
// OnQueryOnly.CLOSE as a performance optimisation on Postgres
this.onQueryOnly = OnQueryOnly.CLOSE;
this.likeClause = "like ? escape''";
this.dbDdlSyntax = new PostgresDdlSyntax();
this.selectCountWithAlias = true;
this.blobDbType = Types.LONGVARBINARY;
this.clobDbType = Types.VARCHAR;
@@ -38,7 +37,9 @@ public class PostgresPlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.columnAliasPrefix = "as c";
this.platformDdl = new PostgresDdl(this.dbTypeMap, this.dbIdentity);
//this.columnAliasPrefix = "as c";
this.openQuote = "\"";
this.closeQuote = "\"";
@@ -67,6 +68,12 @@ public class PostgresPlatform extends DatabasePlatform {
dbDdlSyntax.setDropIfExists("if exists");
}
/**
* Return a DdlHandler instance for generating DDL for the specific platform.
*/
public DdlHandler createDdlHandler() {
return this.platformDdl.createDdlHandler();
}
/**
* Create a Postgres specific sequence IdGenerator.
@@ -1,5 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
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;
@@ -34,15 +35,25 @@ public class BaseTableDdl implements TableDdl {
@Override
public void generate(DdlWrite writer, CreateTable createTable) throws IOException {
String tableName = createTable.getName();
String tableName = lowerName(createTable.getName());
List<Column> columns = createTable.getColumn();
List<Column> pk = determinePrimaryKeyColumns(columns);
boolean singleColumnPrimaryKey = pk.size() == 1;
boolean useIdentity = false;
boolean useSequence = false;
if (singleColumnPrimaryKey) {
IdType useDbIdentityType = platformDdl.useIdentityType(createTable.getIdentityType());
useIdentity = (IdType.IDENTITY == useDbIdentityType);
useSequence = (IdType.SEQUENCE == useDbIdentityType);
}
DdlBuffer apply = writer.apply();
apply.append("create table ").append(tableName).append(" (");
for (int i = 0; i < columns.size(); i++) {
apply.newLine();
writeColumnDefinition(apply, columns.get(i));
writeColumnDefinition(apply, columns.get(i), useIdentity);
if (i < columns.size() - 1) {
apply.append(",");
}
@@ -62,7 +73,9 @@ public class BaseTableDdl implements TableDdl {
// we drop the related sequence (if sequences are used)
dropTable(writer.rollback(), tableName);
writeSequence(writer, createTable);
if (useSequence) {
writeSequence(writer, createTable);
}
// add blank line for a bit of whitespace between tables
apply.end();
@@ -77,14 +90,17 @@ public class BaseTableDdl implements TableDdl {
}
private void writeSequence(DdlWrite writer, CreateTable createTable) throws IOException {
String name = createTable.getSequenceName();
// explicit sequence use or platform decides
String explicitSequenceName = createTable.getSequenceName();
int initial = toInt(createTable.getSequenceInitial());
int allocate = toInt(createTable.getSequenceAllocate());
String createSeq = platformDdl.createSequence(name, initial, allocate);
String seqName = namingConvention.sequenceName(createTable.getName(), explicitSequenceName);
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
if (createSeq != null) {
writer.apply().append(createSeq).newLine();
writer.rollback().append(platformDdl.dropSequence(name));
writer.rollback().append(platformDdl.dropSequence(seqName)).endOfStatement();
}
}
@@ -144,6 +160,7 @@ public class BaseTableDdl implements TableDdl {
protected void writeForeignKey(DdlWrite write, String fkName, String tableName, String[] columns, String refTable, String[] refColumns) throws IOException {
tableName = lowerName(tableName);
DdlBuffer fkeyBuffer = write.applyForeignKeys();
fkeyBuffer
.append("alter table ").append(tableName)
@@ -152,7 +169,7 @@ public class BaseTableDdl implements TableDdl {
appendColumns(columns, fkeyBuffer);
fkeyBuffer
.append(" references ")
.append(refTable);
.append(lowerName(refTable));
appendColumns(refColumns, fkeyBuffer);
fkeyBuffer.appendWithSpace(platformDdl.getForeignKeyRestrict())
.endOfStatement();
@@ -183,7 +200,7 @@ public class BaseTableDdl implements TableDdl {
if (i > 0) {
buffer.append(",");
}
buffer.append(columns[i].trim());
buffer.append(lowerName(columns[i].trim()));
}
buffer.append(")");
}
@@ -194,7 +211,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected void dropTable(DdlBuffer buffer, String tableName) throws IOException {
buffer.append("drop table ").append(tableName).endOfStatement();
buffer.append(platformDdl.dropTable(tableName)).endOfStatement();
}
/**
@@ -250,7 +267,7 @@ public class BaseTableDdl implements TableDdl {
buffer.append(",").newLine();
buffer.append(" constraint ").append(uqName).append(" unique ");
buffer.append("(");
buffer.append(column.getName());
buffer.append(lowerName(column.getName()));
buffer.append(")");
}
@@ -259,7 +276,7 @@ public class BaseTableDdl implements TableDdl {
*/
protected void writePrimaryKeyConstraint(DdlBuffer buffer, String tableName, String[] pkColumns) throws IOException {
String pkName = determinePrimaryKeyName(tableName, pkColumns);
String pkName = determinePrimaryKeyName(tableName);
buffer.append(",").newLine();
buffer.append(" constraint ").append(pkName).append(" primary key");
@@ -272,7 +289,7 @@ public class BaseTableDdl implements TableDdl {
public void alterTableAddPrimaryKey(DdlBuffer buffer, String tableName, List<Column> pk) throws IOException {
String[] pkColumns = toColumnNames(pk);
String pkName = determinePrimaryKeyName(tableName, pkColumns);
String pkName = determinePrimaryKeyName(tableName);
buffer.append("alter table ").append(tableName);
buffer.append(" add primary key ").append(pkName);
@@ -299,15 +316,27 @@ public class BaseTableDdl implements TableDdl {
return columns.split(",");
}
/**
* Convert the table or column name to lower case.
* <p>
* This is passed up to the platformDdl to override as desired.
* Generally lower case with underscore is a good cross database
* choice for column/table names.
*/
protected String lowerName(String name) {
return platformDdl.lowerName(name);
}
/**
* Write the column definition to the create table statement.
*/
protected void writeColumnDefinition(DdlBuffer buffer, Column column) throws IOException {
protected void writeColumnDefinition(DdlBuffer buffer, Column column, boolean useIdentity) throws IOException {
String platformType = convertToPlatformType(column.getType(), isTrue(column.isIdentity()));
boolean identityColumn = useIdentity && isTrue(column.isPrimaryKey());
String platformType = convertToPlatformType(column.getType(), identityColumn);
buffer.append(" ");
buffer.append(column.getName(), 30);
buffer.append(lowerName(column.getName()), 30);
buffer.append(platformType);
if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
buffer.append(" not null");
@@ -330,9 +359,9 @@ public class BaseTableDdl implements TableDdl {
/**
* Return the primary key constraint name.
*/
protected String determinePrimaryKeyName(String tableName, String[] pkColumns) {
protected String determinePrimaryKeyName(String tableName) {
return namingConvention.primaryKeyName(tableName, pkColumns);
return namingConvention.primaryKeyName(tableName);
}
/**
@@ -1,42 +0,0 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
/**
* Used to normalise table and column names which means stripping out
* quoted identifier characters and any catalog or schema prefix.
*/
public class DbNameNormalise {
protected boolean lowerCase = true;
protected String[] quotedIdentifiers = {"\"", "'", "[", "]", "`"};
/**
* Normalise the table name by trimming catalog and schema and removing any
* quoted identifier characters (",',[,] etc).
*/
public String normalise(String tableName) {
tableName = trimQuotes(tableName);
int lastPeriod = tableName.lastIndexOf('.');
if (lastPeriod > -1) {
tableName = tableName.substring(lastPeriod + 1);
}
if (lowerCase) {
tableName = tableName.toLowerCase();
}
return tableName;
}
/**
* Trim off the platform quoted identifier quotes like [ ' and ".
*/
protected String trimQuotes(String tableName) {
// remove quoted identifier characters
for (int i = 0; i < quotedIdentifiers.length; i++) {
tableName = tableName.replace(quotedIdentifiers[i], "");
}
return tableName;
}
}
@@ -0,0 +1,62 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.DbQuotes;
/**
* Used to normalise table and column names which means stripping out
* quoted identifier characters and any catalog or schema prefix.
*/
public class DdlNameNormalise {
protected boolean lowerCaseTables = true;
protected boolean lowerCaseColumns = true;
protected DbQuotes quotes = new DbQuotes();
public DdlNameNormalise() {
}
public boolean notQuoted(String tableName) {
return quotes.notQuoted(tableName);
}
/**
* Normalise the table name by trimming catalog and schema and removing any
* quoted identifier characters (",',[,] etc).
*/
public String normaliseTable(String tableName) {
tableName = trimQuotes(tableName);
int lastPeriod = tableName.lastIndexOf('.');
if (lastPeriod > -1) {
// trim off catalog and schema prefix
tableName = tableName.substring(lastPeriod + 1);
}
if (lowerCaseTables) {
tableName = tableName.toLowerCase();
}
return tableName;
}
/**
* Normalise the column name by removing any quoted identifier characters.
*/
public String normaliseColumn(String columnName) {
columnName = trimQuotes(columnName);
if (lowerCaseColumns) {
columnName = columnName.toLowerCase();
}
return columnName;
}
/**
* Trim off the platform quoted identifier quotes like [ ' and ".
*/
protected String trimQuotes(String tableName) {
return quotes.trimQuotes(tableName);
}
}
@@ -1,9 +1,5 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebeaninternal.server.type.ScalarTypeBoolean;
import java.util.List;
/**
* Naming convention used for constraint names.
*/
@@ -26,25 +22,26 @@ public class DdlNamingConvention {
protected String ckPrefix = "ck_";
protected String ckSuffix = "";
protected final DbNameNormalise normalise;
protected boolean lowerCaseNames = true;
protected DdlNameNormalise normalise = new DdlNameNormalise();
public DdlNamingConvention() {
this.normalise = new DbNameNormalise();
}
/**
* Return the primary key constraint name.
*/
public String primaryKeyName(String tableName, String[] pkColumns) {
public String primaryKeyName(String tableName) {
return pkPrefix + normalise(tableName) + pkSuffix;
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 + normalise(tableName) + fkMiddle + normalise(columnName) + fkSuffix;
return fkPrefix + normaliseTable(tableName) + fkMiddle + normaliseColumn(columnName) + fkSuffix;
}
/**
@@ -52,21 +49,21 @@ public class DdlNamingConvention {
*/
public String foreignKeyIndexName(String tableName, String[] columns) {
String cols = columns.length == 1 ? normalise(columns[0]) : joinColumns(columns);
return fkIndexPrefix + normalise(tableName) + fkIndexMiddle + cols + fkIndexSuffix;
}
private String joinColumns(String[] columns) {
//TODO: Fix this to handle maximum constraint name limits
StringBuilder sb = new StringBuilder(30);
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append("_");
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]));
}
sb.append(columns[i]);
colPart = sb.toString();
}
return sb.toString();
//FIXME: apply max length
return fkIndexPrefix + normaliseTable(tableName) + fkIndexMiddle + colPart + fkIndexSuffix;
}
/**
@@ -74,7 +71,7 @@ public class DdlNamingConvention {
*/
public String uniqueConstraintName(String tableName, String columnName) {
return uqPrefix + normalise(tableName) + "_" + normalise(columnName) + uqSuffix;
return uqPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + uqSuffix;
}
/**
@@ -82,15 +79,44 @@ public class DdlNamingConvention {
*/
public String checkConstraintName(String tableName, String columnName) {
return ckPrefix + normalise(tableName) + "_" + normalise(columnName) + ckSuffix;
return ckPrefix + normaliseTable(tableName) + "_" + normaliseColumn(columnName) + ckSuffix;
}
/**
* 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";
}
/**
* Normalise the table name by trimming catalog and schema and removing any
* quoted identifier characters (",',[,] etc).
*/
protected String normalise(String tableName) {
protected String normaliseTable(String tableName) {
return normalise.normalise(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;
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
/**
@@ -7,10 +8,9 @@ import com.avaje.ebean.config.dbplatform.DbTypeMap;
*/
public class H2Ddl extends PlatformDdl {
public H2Ddl(DbTypeMap platformTypes, boolean useSequences) {
super(platformTypes, new H2HistoryDdl());
this.foreignKeyRestrict = "on delete restrict on update restrict";
this.useSequences = useSequences;
public H2Ddl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.historyDdl = new H2HistoryDdl();
}
}
@@ -0,0 +1,19 @@
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;
/**
* Default history implementation that does nothing. Needs to be replaced
* with an appropriate implementation for the given database platform.
*/
public class NoHistorySupportDdl implements PlatformHistoryDdl {
@Override
public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
// does nothing
}
}
@@ -1,7 +1,13 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.IdType;
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.migration.IdentityType;
import com.avaje.ebean.dbmigration.model.MTable;
import java.io.IOException;
@@ -11,24 +17,48 @@ import java.io.IOException;
*/
public class PlatformDdl {
protected final PlatformHistoryDdl historyDdl;
protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
protected final PlatformTypeConverter typeConverter;
protected DdlNamingConvention namingConvention = new DdlNamingConvention();
protected String foreignKeyRestrict = "";
private final PlatformTypeConverter typeConverter;
protected boolean useSequences;
private final DbIdentity dbIdentity;
public PlatformDdl(DbTypeMap platformTypes, PlatformHistoryDdl historyDdl) {
/**
* Default assumes if exists is supported.
*/
protected String dropTableIfExists = "drop table if exists ";
/**
* Default assumes if exists is supported.
*/
protected String dropSequenceIfExists = "drop sequence if exists ";
protected String foreignKeyRestrict = "on delete restrict on update restrict";
protected String identitySuffix = " auto_increment";
public PlatformDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
this.dbIdentity = dbIdentity;
this.typeConverter = new PlatformTypeConverter(platformTypes);
this.historyDdl = historyDdl;
}
public DdlHandler createDdlHandler() {
return new BaseDdlHandler(namingConvention, this);
}
public IdType useIdentityType(IdentityType modelIdentityType) {
return dbIdentity.useIdentityType(modelIdentityType);
}
/**
* Modify and return the column definition for autoincrement or identity definition.
*/
public String asIdentityColumn(String columnDefn) {
return columnDefn;
return columnDefn + identitySuffix;
}
/**
@@ -58,10 +88,6 @@ public class PlatformDdl {
*/
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) {
@@ -77,6 +103,15 @@ public class PlatformDdl {
}
public String dropSequence(String sequenceName) {
return "drop sequence "+sequenceName+";";
return dropSequenceIfExists + sequenceName;
}
public String dropTable(String tableName) {
return dropTableIfExists + tableName;
}
public String lowerName(String name) {
return namingConvention.lowerName(name);
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
/**
@@ -7,19 +8,15 @@ 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;
public PostgresDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.historyDdl = new PostgresHistoryDdl(this.namingConvention.normalise);
}
/**
* Map bigint, integer and smallint into their equivalent serial types.
*/
@Override
public String asIdentityColumn(String columnDefn) {
if ("bigint".equalsIgnoreCase(columnDefn)) {
@@ -13,7 +13,11 @@ import java.util.Collection;
*/
public class PostgresHistoryDdl implements PlatformHistoryDdl {
DbNameNormalise normalise = new DbNameNormalise();
private final DdlNameNormalise normalise;
public PostgresHistoryDdl(DdlNameNormalise normalise) {
this.normalise = normalise;
}
protected String historyTableName(String baseTableName) {
return baseTableName + "_history";
@@ -38,7 +42,7 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
public void addHistoryTable(DdlWrite writer, MTable table) throws IOException {
String baseTableName = this.normalise.normalise(table.getName());
String baseTableName = this.normalise.normaliseTable(table.getName());
DdlBuffer buffer = writer.applyHistory();
@@ -62,7 +66,7 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
public void addTrigger(DdlWrite writer, MTable table) throws IOException {
String baseTableName = this.normalise.normalise(table.getName());
String baseTableName = this.normalise.normaliseTable(table.getName());
String procedureName = procedureName(baseTableName);
String triggerName = triggerName(baseTableName);
@@ -77,7 +81,7 @@ public class PostgresHistoryDdl implements PlatformHistoryDdl {
public void addStoredFunction(DdlWrite writer, MTable table) throws IOException {
String baseTableName = this.normalise.normalise(table.getName());
String baseTableName = this.normalise.normaliseTable(table.getName());
String procedureName = procedureName(baseTableName);
DdlBuffer buffer = writer.applyHistory();
@@ -0,0 +1,48 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform.util;
/**
* Used to normalise table and column names which means stripping out
* quoted identifier characters and any catalog or schema prefix.
*/
public class DbQuotes {
private final String[] quotedIdentifiers;
public DbQuotes() {
this.quotedIdentifiers = new String[]{"\"", "'", "[", "]", "`"};
}
public DbQuotes(String[] quotedIdentifiers) {
this.quotedIdentifiers = quotedIdentifiers;
}
/**
* Trim off the platform quoted identifier quotes like [ ' and ".
*/
public boolean notQuoted(String 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;
}
}
@@ -1,4 +1,4 @@
package com.avaje.ebean.dbmigration.ddlgeneration.platform;
package com.avaje.ebean.dbmigration.ddlgeneration.platform.util;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
@@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="identityType" type="{http://ebean-orm.github.io/xml/ns/dbmigration}identityType" />
* &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" />
@@ -57,6 +58,8 @@ public class CreateTable {
protected String name;
@XmlAttribute(name = "withHistory")
protected Boolean withHistory;
@XmlAttribute(name = "identityType")
protected IdentityType identityType;
@XmlAttribute(name = "sequenceName")
protected String sequenceName;
@XmlAttribute(name = "sequenceInitial")
@@ -207,6 +210,30 @@ public class CreateTable {
this.withHistory = value;
}
/**
* Gets the value of the identityType property.
*
* @return
* possible object is
* {@link IdentityType }
*
*/
public IdentityType getIdentityType() {
return identityType;
}
/**
* Sets the value of the identityType property.
*
* @param value
* allowed object is
* {@link IdentityType }
*
*/
public void setIdentityType(IdentityType value) {
this.identityType = value;
}
/**
* Gets the value of the sequenceName property.
*
@@ -0,0 +1,42 @@
package com.avaje.ebean.dbmigration.migration;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for identityType.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
* &lt;simpleType name="identityType">
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
* &lt;enumeration value="IDENTITY"/>
* &lt;enumeration value="SEQUENCE"/>
* &lt;enumeration value="GENERATOR"/>
* &lt;enumeration value="DEFAULT"/>
* &lt;/restriction>
* &lt;/simpleType>
* </pre>
*
*/
@XmlType(name = "identityType")
@XmlEnum
public enum IdentityType {
IDENTITY,
SEQUENCE,
GENERATOR,
DEFAULT;
public String value() {
return name();
}
public static IdentityType fromValue(String v) {
return valueOf(v);
}
}
@@ -0,0 +1,333 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://ebean-orm.github.io/xml/ns/dbmigration"
targetNamespace="http://ebean-orm.github.io/xml/ns/dbmigration" elementFormDefault="qualified">
<!-- =========================================================== -->
<!-- APPLICATIONS -->
<!-- =========================================================== -->
<!-- Root level type : applications -->
<xsd:element name="applications">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="application" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="application">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="resourcePath" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- =========================================================== -->
<!-- CHANGE LOG -->
<!-- =========================================================== -->
<!-- Root level type : migration -->
<xsd:element name="migration">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="changeSet" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="changeSet">
<xsd:complexType>
<xsd:sequence>
<xsd:choice>
<xsd:group ref="changeSetChildren" minOccurs="0" maxOccurs="unbounded"/>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:positiveInteger" use="required"/>
<xsd:attribute name="comment" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<!-- =========================================================== -->
<!-- CHANGE SET CHILDREN -->
<!-- =========================================================== -->
<xsd:element name="sql">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="apply" minOccurs="1" maxOccurs="1"/>
<xsd:element ref="rollback" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="apply">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="rollback">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<!-- CONFIGURATION -->
<xsd:element name="configuration">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="defaultTablespace"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="defaultTablespace">
<xsd:complexType>
<xsd:attribute name="tables" type="xsd:string"/>
<xsd:attribute name="indexes" type="xsd:string"/>
<xsd:attribute name="history" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<!-- TABLE -->
<xsd:element name="createTable">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="column" minOccurs="1" maxOccurs="unbounded"/>
<xsd:element ref="uniqueConstraint" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="foreignKey" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="withHistory" type="xsd:boolean"/>
<xsd:attribute name="identityType" type="identityType"/>
<xsd:attribute name="sequenceName" type="xsd:string"/>
<xsd:attribute name="sequenceInitial" type="xsd:positiveInteger"/>
<xsd:attribute name="sequenceAllocate" type="xsd:positiveInteger"/>
<xsd:attributeGroup ref="tablespaceAttributes"/>
</xsd:complexType>
</xsd:element>
<xsd:simpleType name="identityType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="IDENTITY"/>
<xsd:enumeration value="SEQUENCE"/>
<xsd:enumeration value="GENERATOR"/>
<xsd:enumeration value="DEFAULT"/>
</xsd:restriction>
</xsd:simpleType>
<!-- Only expected to be used for compound unique constraint -->
<xsd:element name="uniqueConstraint">
<xsd:complexType>
<xsd:attribute name="columnNames" type="xsd:string" use="required"/>
<xsd:attribute name="constraintName" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<!-- Only expected to be used for compound foreign keys -->
<xsd:element name="foreignKey">
<xsd:complexType>
<xsd:attribute name="columnNames" type="xsd:string" use="required"/>
<xsd:attribute name="refColumnNames" type="xsd:string" use="required"/>
<xsd:attribute name="refTableName" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="dropTable">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="renameTable">
<xsd:complexType>
<xsd:attribute name="oldName" type="xsd:string" use="required"/>
<xsd:attribute name="newName" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- HISTORY -->
<xsd:element name="createHistoryTable">
<xsd:complexType>
<xsd:attribute name="baseTable" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="dropHistoryTable">
<xsd:complexType>
<xsd:attribute name="baseTable" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- COLUMN -->
<xsd:element name="addColumn">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="column" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="tableName" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="dropColumn">
<xsd:complexType>
<xsd:attribute name="columnName" type="xsd:string" use="required"/>
<xsd:attribute name="tableName" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="renameColumn">
<xsd:complexType>
<xsd:attribute name="oldName" type="xsd:string" use="required"/>
<xsd:attribute name="newName" type="xsd:string" use="required"/>
<xsd:attribute name="tableName" type="xsd:string" use="required"/>
<xsd:attribute name="dataType" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<!-- VIEW -->
<xsd:element name="createView">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="replaceIfExists" type="xsd:boolean"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="dropView">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="renameView">
<xsd:complexType>
<xsd:attribute name="oldName" type="xsd:string" use="required"/>
<xsd:attribute name="newName" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- FOREIGN KEY -->
<xsd:element name="addForeignKey">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="columns" type="xsd:string" use="required"/>
<xsd:attribute name="references" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="dropForeignKey">
<xsd:complexType mixed="true">
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- ============================================ -->
<xsd:element name="column">
<xsd:complexType mixed="true">
<xsd:attributeGroup ref="column"/>
<xsd:attributeGroup ref="columnAttributes"/>
</xsd:complexType>
</xsd:element>
<xsd:attributeGroup name="tablespaceAttributes">
<xsd:attribute name="tablespace" type="xsd:string"/>
<xsd:attribute name="indexTablespace" type="xsd:string"/>
<xsd:attribute name="remarks" type="xsd:string"/>
</xsd:attributeGroup>
<xsd:attributeGroup name="columnAttributes">
<xsd:attribute name="notnull" type="xsd:boolean"/>
<xsd:attribute name="checkConstraint" type="xsd:string"/>
<xsd:attribute name="unique" type="xsd:boolean"/>
<xsd:attribute name="primaryKey" type="xsd:boolean"/>
<xsd:attribute name="identity" type="xsd:boolean"/> <!-- aka autoincrement/identity -->
<xsd:attribute name="references" type="xsd:string"/>
<!--<xsd:attribute name="primaryKeyTablespace" type="xsd:string"/>-->
<!--<xsd:attribute name="deleteCascade" type="xsd:boolean"/>-->
<!--<xsd:attribute name="deferrable" type="xsd:boolean"/>-->
<!--<xsd:attribute name="initiallyDeferred" type="xsd:boolean"/>-->
</xsd:attributeGroup>
<xsd:attributeGroup name="column">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="type" type="xsd:string" use="required"/>
<xsd:attribute name="defaultValue" type="xsd:string"/>
<xsd:attribute name="remarks" type="xsd:string"/>
</xsd:attributeGroup>
<!-- Children for changeSet -->
<xsd:group name="changeSetChildren">
<xsd:choice>
<xsd:element ref="configuration" maxOccurs="1"/>
<xsd:element ref="sql" maxOccurs="unbounded"/>
<xsd:element ref="createTable" maxOccurs="unbounded"/>
<xsd:element ref="dropTable" maxOccurs="unbounded"/>
<xsd:element ref="renameTable" maxOccurs="unbounded"/>
<xsd:element ref="createHistoryTable" maxOccurs="unbounded"/>
<xsd:element ref="createView" maxOccurs="unbounded"/>
<xsd:element ref="dropView" maxOccurs="unbounded"/>
<xsd:element ref="renameView" maxOccurs="unbounded"/>
<xsd:element ref="addColumn" maxOccurs="unbounded"/>
<xsd:element ref="dropColumn" maxOccurs="unbounded"/>
<xsd:element ref="renameColumn" maxOccurs="unbounded"/>
<xsd:element ref="addForeignKey" maxOccurs="unbounded"/>
<xsd:element ref="dropForeignKey" maxOccurs="unbounded"/>
<!--<xsd:element ref="createIndex" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="dropIndex" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="createSequence" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="alterSequence" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="dropSequence" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="addNotNullConstraint" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="dropNotNullConstraint" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="addPrimaryKey" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="dropPrimaryKey" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="addUniqueConstraint" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="dropUniqueConstraint" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="addDefaultValue" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="dropDefaultValue" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="sql" maxOccurs="unbounded"/>-->
<!--<xsd:element ref="createProcedure" maxOccurs="unbounded"/>-->
</xsd:choice>
</xsd:group>
</xsd:schema>
@@ -1,13 +1,8 @@
package com.avaje.ebean.dbmigration.model;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbTypeMap;
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler;
import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl;
import com.avaje.ebean.dbmigration.ddlgeneration.platform.PostgresDdl;
import com.avaje.ebean.dbmigration.migration.ChangeSet;
import com.avaje.ebean.dbmigration.migration.Migration;
import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter;
@@ -98,7 +93,7 @@ public class CurrentModel {
DdlWrite write = new DdlWrite();
BaseDdlHandler handler = handler();
DdlHandler handler = handler();
handler.generate(write, changeSet);
return write;
@@ -111,17 +106,14 @@ public class CurrentModel {
write = new DdlWrite();
BaseDdlHandler handler = handler();
DdlHandler handler = handler();
handler.generate(write, createChangeSet);
}
}
private BaseDdlHandler handler() {
private DdlHandler handler() {
DatabasePlatform databasePlatform = server.getDatabasePlatform();
PlatformDdl platformDdl = databasePlatform.getPlatformDdl();
return new BaseDdlHandler(namingConvention, platformDdl);
return server.getDatabasePlatform().createDdlHandler();
}
/**
@@ -4,7 +4,7 @@ import com.avaje.ebean.dbmigration.migration.AddColumn;
import com.avaje.ebean.dbmigration.migration.Column;
import com.avaje.ebean.dbmigration.migration.CreateTable;
import com.avaje.ebean.dbmigration.migration.DropColumn;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
import com.avaje.ebean.dbmigration.migration.IdentityType;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -15,16 +15,16 @@ import java.util.Map;
/**
* Holds the logical model for a given Table and everything associated to it.
* <p>
* This effectively represents a table, its columns and all associated
* constraints, foreign keys and indexes.
* This effectively represents a table, its columns and all associated
* constraints, foreign keys and indexes.
* </p>
* <p>
* Migrations can be applied to this such that it represents the state
* of a given table after various migrations have been applied.
* Migrations can be applied to this such that it represents the state
* of a given table after various migrations have been applied.
* </p>
* <p>
* This table model can also be derived from the EbeanServer bean descriptor
* and associated properties.
* This table model can also be derived from the EbeanServer bean descriptor
* and associated properties.
* </p>
*/
public class MTable {
@@ -41,17 +41,25 @@ public class MTable {
private String tablespace;
private String indexTablespace;
/**
* If set then this overrides the platform default so for UUID generated values
* or DB's supporting both sequences and autoincrement.
*/
private IdentityType identityType;
private String sequenceName;
private int sequenceInitial;
private int sequenceAllocate;
private Boolean withHistory;
private Map<String,MColumn> columns = new LinkedHashMap<String,MColumn>();
private Map<String, MColumn> columns = new LinkedHashMap<String, MColumn>();
private List<MCompoundUniqueConstraint> compoundUniqueConstraints = new ArrayList<MCompoundUniqueConstraint>();
private List<MCompoundForeignKey> compoundKeys = new ArrayList<MCompoundForeignKey>();
/**
* Construct for migration.
*/
@@ -89,6 +97,7 @@ public class MTable {
createTable.setSequenceName(sequenceName);
createTable.setSequenceInitial(toBigInteger(sequenceInitial));
createTable.setSequenceAllocate(toBigInteger(sequenceAllocate));
createTable.setIdentityType(identityType);
for (MColumn column : this.columns.values()) {
createTable.getColumn().add(column.createColumn());
@@ -183,6 +192,26 @@ public class MTable {
this.sequenceAllocate = sequenceAllocate;
}
/**
* Set the identity type to use for this table.
* <p>
* If set then this overrides the platform default so for UUID generated values
* or DB's supporting both sequences and autoincrement.
*/
public void setIdentityType(IdentityType identityType) {
this.identityType = identityType;
}
/**
* Returns the identity type to use for this table.
* <p>
* If set then this overrides the platform default so for UUID generated values
* or DB's supporting both sequences and autoincrement.
*/
public IdentityType getIdentityType() {
return identityType;
}
/**
* Return the list of columns that make the primary key.
*/
@@ -198,7 +227,7 @@ public class MTable {
private void checkTableName(String tableName) {
if (!name.equals(tableName)) {
throw new IllegalArgumentException("addColumn tableName ["+tableName+"] does not match ["+name+"]");
throw new IllegalArgumentException("addColumn tableName [" + tableName + "] does not match [" + name + "]");
}
}
@@ -220,7 +249,7 @@ public class MTable {
* Add a compound unique constraint.
*/
public void addCompoundUniqueConstraint(String[] columns) {
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns));
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns));
}
/**
@@ -105,11 +105,6 @@ 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);
}
}
@@ -1,13 +1,19 @@
package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.dbmigration.migration.IdentityType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebean.dbmigration.model.MColumn;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor;
import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor;
import com.avaje.ebeaninternal.server.type.ScalarType;
import java.sql.Types;
/**
* Used to build the Model objects MTable etc.
@@ -35,9 +41,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
MTable table = new MTable(descriptor.getBaseTable());
table.setSequenceName(descriptor.getSequenceName());
table.setSequenceInitial(descriptor.getSequenceInitialValue());
table.setSequenceAllocate(descriptor.getSequenceAllocationSize());
setIdentity(descriptor, table);
// add the table to the model
ctx.addTable(table);
@@ -62,4 +66,43 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
return new ModelBuildPropertyVisitor(ctx, table);
}
private void setIdentity(BeanDescriptor<?> descriptor, MTable table) {
if (IdType.GENERATOR == descriptor.getIdType()) {
// explicit generator like UUID
table.setIdentityType(IdentityType.GENERATOR);
return;
}
int initialValue = descriptor.getSequenceInitialValue();
int allocationSize = descriptor.getSequenceAllocationSize();
if (!descriptor.isIdTypePlatformDefault() || initialValue > 0 || allocationSize > 0) {
// explicitly set to use sequence or identity (generally not recommended practice)
if (IdType.IDENTITY == descriptor.getIdType()) {
table.setIdentityType(IdentityType.IDENTITY);
} else {
// explicit sequence defined
table.setIdentityType(IdentityType.SEQUENCE);
table.setSequenceName(descriptor.getSequenceName());
table.setSequenceInitial(initialValue);
table.setSequenceAllocate(allocationSize);
}
return;
}
BeanProperty idProperty = descriptor.getIdProperty();
if (idProperty != null) {
ScalarType<Object> scalarType = idProperty.getScalarType();
if (scalarType != null) {
int jdbcType = scalarType.getJdbcType();
if (jdbcType == Types.VARCHAR) {
System.out.println("asd");
}
}
}
}
}
@@ -1,14 +1,13 @@
package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.dbmigration.migration.ForeignKey;
import com.avaje.ebean.dbmigration.model.MColumn;
import com.avaje.ebean.dbmigration.model.MCompoundForeignKey;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
import com.avaje.ebeaninternal.server.deploy.TableJoinColumn;
import com.avaje.ebean.dbmigration.model.MColumn;
import com.avaje.ebean.dbmigration.model.MTable;
/**
@@ -100,6 +100,8 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
*/
private final IdType idType;
private final boolean idTypePlatformDefault;
private final IdGenerator idGenerator;
/**
@@ -339,6 +341,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause);
this.idType = deploy.getIdType();
this.idTypePlatformDefault = deploy.isIdTypePlatformDefault();
this.idGenerator = deploy.getIdGenerator();
this.sequenceName = deploy.getSequenceName();
this.sequenceInitialValue = deploy.getSequenceInitialValue();
@@ -1677,6 +1680,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return idType;
}
/**
* Return true if the identity is the platform default (not explicitly set).
*/
public boolean isIdTypePlatformDefault() {
return idTypePlatformDefault;
}
/**
* Return the sequence name.
*/
@@ -1062,8 +1062,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
if (desc.getIdType() == null) {
if (desc.isPrimaryKeyCompoundOrNonNumeric()) {
// assuming that this is a user supplied key like ISO country code or ISO currency code or lookup table code
logger.debug("Expecting user defined identity on " + desc.getFullName() + " - not using db sequence or autoincrement");
return;
}
// use the default. IDENTITY or SEQUENCE.
desc.setIdType(dbIdentity.getIdType());
desc.setIdTypePlatformDefault();
}
if (IdType.GENERATOR.equals(desc.getIdType())) {
@@ -73,6 +73,11 @@ public class DeployBeanDescriptor<T> {
*/
private IdType idType;
/**
* Set to true if the identity is default for the platform.
*/
private boolean idTypePlatformDefault;
/**
* The name of an IdGenerator (optional).
*/
@@ -545,6 +550,20 @@ public class DeployBeanDescriptor<T> {
this.idType = idType;
}
/**
* Set when the identity type is the platform default.
*/
public void setIdTypePlatformDefault() {
this.idTypePlatformDefault = true;
}
/**
* Return true when the identity is the platform default.
*/
public boolean isIdTypePlatformDefault() {
return idTypePlatformDefault;
}
/**
* Return the DB sequence name (can be null).
*/
@@ -685,6 +704,25 @@ public class DeployBeanDescriptor<T> {
return Collections.unmodifiableSet(set);
}
/**
* Return true if the primary key is a compound key or if it's database type
* is non-numeric (and hence not suitable for db identity or sequence.
*/
public boolean isPrimaryKeyCompoundOrNonNumeric() {
List<DeployBeanProperty> ids = propertiesId();
if (ids.size() != 1) {
// compound key
return true;
}
DeployBeanProperty p = ids.get(0);
if (p instanceof DeployBeanPropertyAssocOne<?>) {
return ((DeployBeanPropertyAssocOne<?>)p).isCompound();
} else {
return !p.isDbNumberType();
}
}
/**
* Return the Primary Key column assuming it is a single column (not
* compound). This is for the purpose of defining a sequence name.
@@ -625,6 +625,35 @@ public class DeployBeanProperty {
}
}
public boolean isDbNumberType() {
return isNumericType(dbType);
}
private boolean isNumericType(int type) {
switch (type) {
case Types.BIGINT:
return true;
case Types.DECIMAL:
return true;
case Types.DOUBLE:
return true;
case Types.FLOAT:
return true;
case Types.INTEGER:
return true;
case Types.NUMERIC:
return true;
case Types.REAL:
return true;
case Types.SMALLINT:
return true;
case Types.TINYINT:
return true;
default:
return false;
}
}
/**
* Return true if this property is based on a secondary table.
*/
@@ -30,6 +30,14 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
return deployEmbedded;
}
/**
* Return true if this has multiple properties (expected for embedded id).
*/
public boolean isCompound() {
// just checking for compound and not doing numeric check at this stage
return getDeployEmbedded().getPropertyColumnMap().size() > 1;
}
@Override
public String getDbColumn() {
DeployTableJoinColumn[] columns = tableJoin.columns();
@@ -112,12 +112,8 @@ public class DataBind {
pstmt.setString(++pos, String.valueOf(v));
}
public void setBlob(InputStream inputStream, long length) throws SQLException {
pstmt.setBlob(++pos, inputStream, length);
}
public void setBlob(InputStream inputStream) throws SQLException {
pstmt.setBlob(++pos, inputStream);
public void setBinaryStream(InputStream inputStream, long length) throws SQLException {
pstmt.setBinaryStream(++pos, inputStream, length);
}
public void setBlob(byte[] bytes) throws SQLException {
@@ -18,16 +18,8 @@ public interface DataReader {
byte[] getBinaryBytes() throws SQLException;
byte[] getBlobBytes() throws SQLException;
InputStream getBlobInputStream() throws SQLException;
String getStringFromStream() throws SQLException;
String getStringClob() throws SQLException;
Reader getClobReader() throws SQLException;
String getString() throws SQLException;
Boolean getBoolean() throws SQLException;
@@ -1,13 +1,13 @@
package com.avaje.ebeaninternal.server.type;
import com.avaje.ebeaninternal.server.core.Message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.Ref;
import java.sql.ResultSet;
@@ -15,8 +15,6 @@ import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import com.avaje.ebeaninternal.server.core.Message;
public class RsetDataReader implements DataReader {
private static final int bufferSize = 512;
@@ -57,10 +55,6 @@ public class RsetDataReader implements DataReader {
return rset.getArray(pos());
}
public InputStream getAsciiStream() throws SQLException {
return rset.getAsciiStream(pos());
}
public Object getObject() throws SQLException {
return rset.getObject(pos());
}
@@ -168,28 +162,6 @@ public class RsetDataReader implements DataReader {
return readStringLob(reader);
}
@Override
public Reader getClobReader() throws SQLException {
Clob clob = rset.getClob(pos());
if (clob == null) {
return null;
}
return clob.getCharacterStream();
}
public String getStringClob() throws SQLException {
Clob clob = rset.getClob(pos());
if (clob == null) {
return null;
}
Reader reader = clob.getCharacterStream();
if (reader == null) {
return null;
}
return readStringLob(reader);
}
protected String readStringLob(Reader reader) throws SQLException {
char[] buffer = new char[clobBufferSize];
@@ -212,23 +184,6 @@ public class RsetDataReader implements DataReader {
return getBinaryLob(in);
}
public byte[] getBlobBytes() throws SQLException {
Blob blob = rset.getBlob(pos());
if (blob == null) {
return null;
}
InputStream in = blob.getBinaryStream();
return getBinaryLob(in);
}
public InputStream getBlobInputStream() throws SQLException {
Blob blob = rset.getBlob(pos());
if (blob == null) {
return null;
}
return blob.getBinaryStream();
}
protected byte[] getBinaryLob(InputStream in) throws SQLException {
try {
@@ -14,7 +14,7 @@ public class ScalarTypeBytesBlob extends ScalarTypeBytesBase {
public byte[] read(DataReader dataReader) throws SQLException {
return dataReader.getBlobBytes();
return dataReader.getBinaryBytes();
}
}
@@ -40,7 +40,7 @@ public class ScalarTypeClob extends ScalarTypeBaseVarchar<String> {
@Override
public String read(DataReader dataReader) throws SQLException {
return dataReader.getStringClob();
return dataReader.getStringFromStream();
}
@Override
@@ -38,7 +38,7 @@ public class ScalarTypeFile extends ScalarTypeBase<File> {
* Construct with reasonable defaults of Blob and 8096 buffer size.
*/
public ScalarTypeFile() {
this(Types.BLOB, "db-", null, null, 8096);
this(Types.LONGVARBINARY, "db-", null, null, 8096);
}
/**
@@ -92,7 +92,7 @@ public class ScalarTypeFile extends ScalarTypeBase<File> {
try {
// stream from our file to the db
InputStream fi = getInputStream(value);
b.setBlob(fi, value.length());
b.setBinaryStream(fi, value.length());
} catch (IOException e) {
throw new SQLException("Error trying to set file inputStream", e);
}
@@ -5,7 +5,6 @@ import com.avaje.ebean.text.json.EJson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -31,17 +30,11 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
@Override
public Map read(DataReader dataReader) throws SQLException {
Reader reader = dataReader.getClobReader();
if (reader == null) {
String content = dataReader.getStringFromStream();
if (content == null) {
return null;
}
try {
Map map = parse(reader);
reader.close();
return map;
} catch (IOException e) {
throw new SQLException("Error reading Clob stream from DB", e);
}
return parse(content);
}
}
@@ -60,15 +53,17 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
@Override
public Map read(DataReader dataReader) throws SQLException {
InputStream is = dataReader.getBlobInputStream();
InputStream is = dataReader.getBinaryStream();
if (is == null) {
return null;
}
try {
InputStreamReader reader = new InputStreamReader(is);
Map map = parse(reader);
reader.close();
return map;
try {
return parse(reader);
} finally {
reader.close();
}
} catch (IOException e) {
throw new SQLException("Error reading Blob stream from DB", e);
}
@@ -81,8 +76,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
b.setNull(Types.BLOB);
} else {
String rawJson = formatValue(value);
InputStream stream = new ByteArrayInputStream(rawJson.getBytes(StandardCharsets.UTF_8));
b.setBlob(stream);
b.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
}
}
}
@@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -34,17 +33,11 @@ public abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
@Override
public JsonNode read(DataReader dataReader) throws SQLException {
Reader reader = dataReader.getClobReader();
if (reader == null) {
String content = dataReader.getStringFromStream();
if (content == null) {
return null;
}
try {
JsonNode tree = parse(reader);
reader.close();
return tree;
} catch (IOException e) {
throw new SQLException("Error reading Clob stream from DB", e);
}
return parse(content);
}
}
@@ -70,7 +63,7 @@ public abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
@Override
public JsonNode read(DataReader dataReader) throws SQLException {
InputStream is = dataReader.getBlobInputStream();
InputStream is = dataReader.getBinaryStream();
if (is == null) {
return null;
}
@@ -91,8 +84,7 @@ public abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
dataBind.setNull(Types.BLOB);
} else {
String rawJson = formatValue(value);
InputStream stream = new ByteArrayInputStream(rawJson.getBytes(StandardCharsets.UTF_8));
dataBind.setBlob(stream);
dataBind.setBlob(rawJson.getBytes(StandardCharsets.UTF_8));
}
}
}