From 638446680c82e5aaee7df0682a07a6793afaa6e7 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Wed, 9 Dec 2015 16:52:58 +1300 Subject: [PATCH] #365 - ENH: Add @DbComment with DDL generation of table and column comments --- .../com/avaje/ebean/annotation/DbComment.java | 19 ++++++++++ .../ddlgeneration/platform/BaseTableDdl.java | 38 ++++++++++++++++++- .../ddlgeneration/platform/MySqlDdl.java | 28 ++++++++++++++ .../ddlgeneration/platform/PlatformDdl.java | 35 +++++++++++++++++ .../ebean/dbmigration/model/MColumn.java | 17 +++++++++ .../avaje/ebean/dbmigration/model/MTable.java | 4 ++ .../model/build/ModelBuildBeanVisitor.java | 1 + .../build/ModelBuildPropertyVisitor.java | 1 + .../server/deploy/BeanDescriptor.java | 13 +++++++ .../server/deploy/BeanProperty.java | 14 +++++++ .../deploy/meta/DeployBeanDescriptor.java | 11 ++++++ .../deploy/meta/DeployBeanProperty.java | 9 +++++ .../server/deploy/parse/AnnotationClass.java | 6 +++ .../server/deploy/parse/AnnotationFields.java | 4 ++ .../com/avaje/tests/model/basic/Customer.java | 5 +++ 15 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/avaje/ebean/annotation/DbComment.java diff --git a/src/main/java/com/avaje/ebean/annotation/DbComment.java b/src/main/java/com/avaje/ebean/annotation/DbComment.java new file mode 100644 index 000000000..e5bac32e5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/annotation/DbComment.java @@ -0,0 +1,19 @@ +package com.avaje.ebean.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A database table or column comment. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface DbComment { + + /** + * The database table or column comment. + */ + String value(); +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java index a793cd594..6b512d6c2 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -21,6 +21,7 @@ import com.avaje.ebean.dbmigration.migration.DropTable; import com.avaje.ebean.dbmigration.migration.ForeignKey; import com.avaje.ebean.dbmigration.migration.UniqueConstraint; import com.avaje.ebean.dbmigration.model.MTable; +import com.avaje.ebean.util.StringHelper; import java.io.IOException; import java.math.BigInteger; @@ -123,7 +124,11 @@ public class BaseTableDdl implements TableDdl { writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk)); } - apply.newLine().append(")").endOfStatement(); + apply.newLine().append(")"); + addTableCommentInline(apply, createTable); + apply.endOfStatement(); + + addComments(apply, createTable); writeUniqueOneToOneConstraints(writer, createTable); @@ -150,6 +155,37 @@ public class BaseTableDdl implements TableDdl { } + /** + * Add table and column comments (separate from the create table statement). + */ + private void addComments(DdlBuffer apply, CreateTable createTable) throws IOException { + if (!platformDdl.isInlineComments()) { + String tableComment = createTable.getComment(); + if (!StringHelper.isNull(tableComment)) { + platformDdl.addTableComment(apply, createTable.getName(), tableComment); + } + + List columns = createTable.getColumn(); + for (Column column : columns) { + if (!StringHelper.isNull(column.getComment())) { + platformDdl.addColumnComment(apply, createTable.getName(), column.getName(), column.getComment()); + } + } + } + } + + /** + * Add the table comment inline with the create table statement. + */ + private void addTableCommentInline(DdlBuffer apply, CreateTable createTable) throws IOException { + if (platformDdl.isInlineComments()) { + String tableComment = createTable.getComment(); + if (!StringHelper.isNull(tableComment)) { + platformDdl.inlineTableComment(apply, tableComment); + } + } + } + private void writeTableColumns(DdlBuffer apply, List columns, boolean useIdentity) throws IOException { platformDdl.writeTableColumns(apply, columns, useIdentity); } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MySqlDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MySqlDdl.java index e7c4be732..77d2bb7f8 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MySqlDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MySqlDdl.java @@ -2,7 +2,12 @@ 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.dbmigration.ddlgeneration.DdlBuffer; import com.avaje.ebean.dbmigration.migration.AlterColumn; +import com.avaje.ebean.dbmigration.migration.Column; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; + +import java.io.IOException; /** * MySql specific DDL. @@ -14,6 +19,7 @@ public class MySqlDdl extends PlatformDdl { this.alterColumn = "modify"; this.dropUniqueConstraint = "drop index"; this.historyDdl = new MySqlHistoryDdl(); + this.inlineComments = true; } /** @@ -64,4 +70,26 @@ public class MySqlDdl extends PlatformDdl { // use modify return "alter table " + tableName + " modify " + columnName + " " + type + notnullClause; } + + @Override + protected void writeColumnDefinition(DdlBuffer buffer, Column column, boolean useIdentity) throws IOException { + super.writeColumnDefinition(buffer, column, useIdentity); + String comment = column.getComment(); + if (!StringHelper.isNull(comment)) { + // in mysql 5.5 column comment save in information_schema.COLUMNS.COLUMN_COMMENT(VARCHAR 1024) + if (comment.length() > 500) { + comment = comment.substring(0, 500); + } + buffer.append(String.format(" comment '%s'", comment)); + } + + } + + public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException { + if (tableComment.length() > 1000) { + tableComment = tableComment.substring(0, 1000); + } + apply.append(" comment='").append(tableComment).append("'"); + } + } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java index 0110386b4..922ad6406 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -37,6 +37,11 @@ public class PlatformDdl { */ private final DbIdentity dbIdentity; + /** + * Set to true if table and column comments are included inline with the create statements. + */ + protected boolean inlineComments; + /** * Default assumes if exists is supported. */ @@ -116,6 +121,13 @@ public class PlatformDdl { return columnDefn + identitySuffix; } + /** + * Return true if the table and column comments are included inline. + */ + public boolean isInlineComments() { + return inlineComments; + } + /** * Write all the table columns converting to platform types as necessary. */ @@ -386,4 +398,27 @@ public class PlatformDdl { protected boolean isTrue(Boolean value) { return Boolean.TRUE.equals(value); } + + /** + * Add an inline table comment to the create table statement. + */ + public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException { + // do nothing by default (MySql only) + } + + /** + * Add table comment as a separate statement (from the create table statement). + */ + public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException { + + apply.append(String.format("comment on table %s is '%s'", tableName, tableComment)).endOfStatement(); + } + + /** + * Add column comment as a separate statement. + */ + public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException { + + apply.append(String.format("comment on column %s.%s is '%s'", table, column, comment)).endOfStatement(); + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java index 2e8c41905..705f6337f 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -16,6 +16,7 @@ public class MColumn { private String references; private String foreignKeyName; private String foreignKeyIndex; + private String comment; private boolean historyExclude; private boolean notnull; @@ -43,6 +44,7 @@ public class MColumn { this.checkConstraint = column.getCheckConstraint(); this.checkConstraintName = column.getCheckConstraintName(); this.defaultValue = column.getDefaultValue(); + this.comment = column.getComment(); this.references = column.getReferences(); this.foreignKeyName = column.getForeignKeyName(); this.foreignKeyIndex = column.getForeignKeyIndex(); @@ -76,6 +78,7 @@ public class MColumn { copy.checkConstraintName = checkConstraintName; copy.defaultValue = defaultValue; copy.references = references; + copy.comment = comment; copy.foreignKeyName = foreignKeyName; copy.foreignKeyIndex = foreignKeyIndex; copy.historyExclude = historyExclude; @@ -198,6 +201,19 @@ public class MColumn { return uniqueOneToOne; } + /** + * Return the column comment. + */ + public String getComment() { + return comment; + } + + /** + * Set the column comment. + */ + public void setComment(String comment) { + this.comment = comment; + } /** * Set the draftOnly status for this column. @@ -237,6 +253,7 @@ public class MColumn { c.setForeignKeyName(foreignKeyName); c.setForeignKeyIndex(foreignKeyIndex); c.setDefaultValue(defaultValue); + c.setComment(comment); c.setUnique(unique); c.setUniqueOneToOne(uniqueOneToOne); diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java index 9e901bbd8..e608a5b5e 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -325,6 +325,10 @@ public class MTable { return comment; } + public void setComment(String comment) { + this.comment = comment; + } + public String getTablespace() { return tablespace; } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java index 155c09de8..3b24b2337 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildBeanVisitor.java @@ -35,6 +35,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor { } MTable table = new MTable(descriptor.getBaseTable()); + table.setComment(descriptor.getDbComment()); if (descriptor.isHistorySupport()) { table.setWithHistory(true); BeanProperty whenCreated = descriptor.findWhenCreatedProperty(); diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java index 8878e7fdb..663598805 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -230,6 +230,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { } MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p)); + col.setComment(p.getDbComment()); col.setDraftOnly(p.isDraftOnly()); if (p.isId()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 944d6b388..13f309a63 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -155,6 +155,11 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { private final String draftTable; + /** + * DB table comment. + */ + private final String dbComment; + /** * Set to true if read auditing is on for this bean type. */ @@ -397,6 +402,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { this.baseTable = InternString.intern(deploy.getBaseTable()); this.baseTableAsOf = deploy.getBaseTableAsOf(); this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween(); + this.dbComment = deploy.getDbComment(); this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); // helper object used to derive lists of properties @@ -1936,6 +1942,13 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { return EntityType.SQL.equals(entityType); } + /** + * Return the DB comment for the base table. + */ + public String getDbComment() { + return dbComment; + } + /** * Return the base table. Only properties mapped to the base table are by * default persisted. diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index ef7cbb403..b97f7b4f5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -211,6 +211,11 @@ public class BeanProperty implements ElPropertyValue { */ final String dbColumnDefn; + /** + * Database DDL column comment. + */ + final String dbComment; + /** * DB Constraint (typically check constraint on enum) */ @@ -298,6 +303,7 @@ public class BeanProperty implements ElPropertyValue { this.setter = deploy.getSetter(); this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); + this.dbComment = deploy.getDbComment(); this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); this.formula = sqlFormulaSelect != null; @@ -377,6 +383,7 @@ public class BeanProperty implements ElPropertyValue { this.secondaryTableJoin = source.secondaryTableJoin; this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; + this.dbComment = source.dbComment; this.dbBind = source.getDbBind(); this.dbEncrypted = source.isDbEncrypted(); this.dbEncryptedType = source.getDbEncryptedType(); @@ -999,6 +1006,13 @@ public class BeanProperty implements ElPropertyValue { return dbColumn; } + /** + * Return the comment for the associated DB column. + */ + public String getDbComment() { + return dbComment; + } + /** * Return the database jdbc data type this is mapped to. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 282d4bf8a..d8d630781 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -165,6 +165,9 @@ public class DeployBeanDescriptor { private ChangeLogFilter changeLogFilter; + private String dbComment; + + /** * Construct the BeanDescriptor. */ @@ -207,6 +210,14 @@ public class DeployBeanDescriptor { return readAuditing; } + public void setDbComment(String dbComment) { + this.dbComment = dbComment; + } + + public String getDbComment() { + return dbComment; + } + public void setDraftable() { draftable = true; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 3d658d720..919c9bb83 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -193,6 +193,8 @@ public class DeployBeanProperty { private boolean softDelete; + private String dbComment; + public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { this.desc = desc; this.propertyType = propertyType; @@ -898,4 +900,11 @@ public class DeployBeanProperty { return softDelete; } + public void setDbComment(String dbComment) { + this.dbComment = dbComment; + } + + public String getDbComment() { + return dbComment; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java index 5a6e124b5..1be877fbd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -9,6 +9,7 @@ import javax.persistence.UniqueConstraint; import com.avaje.ebean.annotation.CacheStrategy; import com.avaje.ebean.annotation.CacheTuning; +import com.avaje.ebean.annotation.DbComment; import com.avaje.ebean.annotation.Draftable; import com.avaje.ebean.annotation.DraftableElement; import com.avaje.ebean.annotation.EntityConcurrencyMode; @@ -120,6 +121,11 @@ public class AnnotationClass extends AnnotationParser { descriptor.setHistorySupport(); } + DbComment comment = cls.getAnnotation(DbComment.class); + if (comment != null) { + descriptor.setDbComment(comment.value()); + } + UpdateMode updateMode = cls.getAnnotation(UpdateMode.class); if (updateMode != null) { descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly()); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java index d6dcc65a8..93f82c994 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -166,6 +166,10 @@ public class AnnotationFields extends AnnotationParser { prop.setSoftDelete(); } + DbComment comment = get(prop, DbComment.class); + if (comment != null) { + prop.setDbComment(comment.value()); + } DbJson dbJson = get(prop, DbJson.class); if (dbJson != null) { util.setDbJsonType(prop, dbJson); diff --git a/src/test/java/com/avaje/tests/model/basic/Customer.java b/src/test/java/com/avaje/tests/model/basic/Customer.java index 1314fa6d2..dc6eb4f79 100644 --- a/src/test/java/com/avaje/tests/model/basic/Customer.java +++ b/src/test/java/com/avaje/tests/model/basic/Customer.java @@ -2,6 +2,7 @@ package com.avaje.tests.model.basic; import com.avaje.ebean.annotation.ChangeLog; import com.avaje.ebean.annotation.ChangeLogInsertMode; +import com.avaje.ebean.annotation.DbComment; import com.avaje.ebean.annotation.DbEnumValue; import com.avaje.ebean.annotation.JsonIgnore; import com.avaje.ebean.annotation.Where; @@ -26,6 +27,7 @@ import java.util.concurrent.locks.ReentrantLock; @ChangeLog(inserts = ChangeLogInsertMode.EXCLUDE, updatesThatInclude = {"name","status"}) @Entity @Table(name = "o_customer") +@DbComment("Holds external customers") public class Customer extends BasicDomain { private static final long serialVersionUID = 1L; @@ -60,15 +62,18 @@ public class Customer extends BasicDomain { @Transient ReentrantLock lock = new ReentrantLock(); + @DbComment("status of the customer") Status status; @NotNull @Size(max = 40) String name; + @DbComment("Short notes regarding the customer") @Size(max = 100) String smallnote; + @DbComment("Join date of the customer") @NotNull(groups = { ValidationGroupSomething.class }) Date anniversary;