让Hibernate生成的DDL脚本自动增加注释

我们知道可以通过Hibernate对象自动生成DDL建表语句,通过PowerDesigner工具可以反向工程生成数据字典,但是在生成的DDL中一直不能写上中文的注释,这就使我们生成的数据字典不具有可用性。

这个假期宅在家里调试代码,发现Hibernate的Dialect,Table,Column的映射中已经做了comment的处理,只是Hibernate团队认为这个功能的重要性太小,一直没有时间提供这个需求,于是就自己动手实现这个功能了,这可是使用我们的数据对象代码与数据字典文档同步的关键一环啊!

通过对Hibernate代码的跟踪发现了处理映射的逻辑是在代码AnnotationBinder中,我们不需要在运行期间处理comment,只是在用SchemaExport时处理就可以,于是简单的实现了此功能:

1. 增加一个注解 @Comment(“这是表的说明,也可以是字段的说明”),适用在类名和属性名

2. 复制org.hibernate.cfg.AnnotationBuilder代码为org.hibernate.cfg.MyAnnotationBuilder,处理注解@Comment

3. 复制org.hibernate.cfg.Configuration为org.hibernate.cfg.MyConfiguration,将AnnotationBuilder的调用换成MyAnnotationBuilder

3. 实现SchemaExportTool类,传入MyConfiguration处理Hibernate对象的映射关系,并生成DDL

 

以上思路在基于Hibernate 4.2.3版本在MySql 5.1上测试成功,因为代码只是在开发期运行,不需要良好的结构和优化,所以只是简单实现了,需要此功能的朋友可以自己实现。

 

以下是处理结果示例:

[java][/java] view plaincopy

  1. @Entity
  2. @Table(name = “AuditRecord_”)
  3. @Comment(“系统审计表”)
  4. @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
  5. public class AuditRecord implements Serializable {
  6.     private static final long serialVersionUID = 8844543936912679937L;
  7.     @Id
  8.     @GeneratedValue
  9.     @Comment(“ID主键”)
  10.     private Long id;
  11.     @Comment(“审计类型”)
  12.     @Column(length = 30)
  13.     private String auditType;
  14.     @Comment(“操作人”)
  15.     @ManyToOne
  16.     @JoinColumn(name = “userId”)
  17.     private Passport operator;
  18.     // 操作简述
  19.     @Comment(“操作简述”)
  20.     @Column(length = 4000)
  21.     private String description;
  22.     @Comment(“创建时间”)
  23.     private Date creationTime;
  24. }

生成的DDL如下:

[sql][/sql] view plaincopy

  1. create table audit_record_ (
  2.         id bigint not null auto_increment comment ‘ID主键’,
  3.         audit_type varchar(30) comment ‘审计类型’,
  4.         creation_time datetime comment ‘创建时间’,
  5.         description longtext comment ‘操作简述’,
  6.         user_id bigint comment ‘操作人’,
  7.         primary key (id)
  8.     ) comment=’系统审计表’;

主要代码片断如下:

 

[java][/java] view plaincopy

  1. import java.io.IOException;
  2. import java.util.Properties;
  3. import javax.persistence.Embeddable;
  4. import javax.persistence.Entity;
  5. import javax.persistence.MappedSuperclass;
  6. import org.hibernate.MappingException;
  7. import org.hibernate.cfg.MyConfiguration;
  8. import org.hibernate.tool.hbm2ddl.SchemaExport;
  9. import org.springframework.core.io.Resource;
  10. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  11. import org.springframework.core.io.support.ResourcePatternResolver;
  12. import org.springframework.core.io.support.ResourcePatternUtils;
  13. import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
  14. import org.springframework.core.type.classreading.MetadataReader;
  15. import org.springframework.core.type.classreading.MetadataReaderFactory;
  16. import org.springframework.core.type.filter.AnnotationTypeFilter;
  17. import org.springframework.core.type.filter.TypeFilter;
  18. import org.springframework.util.ClassUtils;
  19. public class SchemaExportTool extends MyConfiguration {
  20.     private static final long serialVersionUID = 1L;
  21.     private static final String RESOURCE_PATTERN = “/**/*.class”;
  22.     private static final String PACKAGE_INFO_SUFFIX = “.package-info”;
  23.     private static final TypeFilter[] ENTITY_TYPE_FILTERS = new TypeFilter[] {
  24.             new AnnotationTypeFilter(Entity.class, false),
  25.             new AnnotationTypeFilter(Embeddable.class, false),
  26.             new AnnotationTypeFilter(MappedSuperclass.class, false) };
  27.     private final ResourcePatternResolver resourcePatternResolver;
  28.     public SchemaExportTool() {
  29.         this.resourcePatternResolver = ResourcePatternUtils
  30.                 .getResourcePatternResolver(new PathMatchingResourcePatternResolver());
  31.     }
  32.     public static void main(String[] args) {
  33.         try {
  34.             Properties p = new Properties();
  35.             p.setProperty(“hibernate.dialect”,
  36.                     “org.hibernate.dialect.MySQLDialect”);
  37.             SchemaExportTool cfg = new SchemaExportTool();
  38.             cfg.addProperties(p);
  39.             cfg.setNamingStrategy(new ImprovedMyNamingStrategy());
  40.             cfg.scanPackage(“com.share.passport.domain”,
  41.                     “com.share.authority.domain”, “com.share.utils.domain”);
  42.             SchemaExport se = new SchemaExport(cfg);
  43.             if (null != args && args.length > 1)
  44.                 if (“-f”.equals(args[0]))
  45.                     se.setOutputFile(args[1]);
  46.                 else
  47.                     se.setOutputFile(“create_table.sql”);
  48.             else
  49.                 se.setOutputFile(“create_table.sql”);
  50.             se.setDelimiter(“;”);
  51.             // se.drop(false, false);
  52.             se.create(false, false);
  53.         } catch (Exception e) {
  54.             e.printStackTrace();
  55.         }
  56.     }
  57.     private SchemaExportTool scanPackage(String… packagesToScan) {
  58.         try {
  59.             for (String pkg : packagesToScan) {
  60.                 String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
  61.                         + ClassUtils.convertClassNameToResourcePath(pkg)
  62.                         + RESOURCE_PATTERN;
  63.                 Resource[] resources = this.resourcePatternResolver
  64.                         .getResources(pattern);
  65.                 MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(
  66.                         this.resourcePatternResolver);
  67.                 for (Resource resource : resources) {
  68.                     if (resource.isReadable()) {
  69.                         MetadataReader reader = readerFactory
  70.                                 .getMetadataReader(resource);
  71.                         String className = reader.getClassMetadata()
  72.                                 .getClassName();
  73.                         if (matchesEntityTypeFilter(reader, readerFactory)) {
  74.                             addAnnotatedClass(this.resourcePatternResolver
  75.                                     .getClassLoader().loadClass(className));
  76.                         } else if (className.endsWith(PACKAGE_INFO_SUFFIX)) {
  77.                             addPackage(className.substring(
  78.                                     0,
  79.                                     className.length()
  80.                                             – PACKAGE_INFO_SUFFIX.length()));
  81.                         }
  82.                     }
  83.                 }
  84.             }
  85.             return this;
  86.         } catch (IOException ex) {
  87.             throw new MappingException(
  88.                     “Failed to scan classpath for unlisted classes”, ex);
  89.         } catch (ClassNotFoundException ex) {
  90.             throw new MappingException(
  91.                     “Failed to load annotated classes from classpath”, ex);
  92.         }
  93.     }
  94.     /**
  95.      * Check whether any of the configured entity type filters matches the
  96.      * current class descriptor contained in the metadata reader.
  97.      */
  98.     private boolean matchesEntityTypeFilter(MetadataReader reader,
  99.             MetadataReaderFactory readerFactory) throws IOException {
  100.         for (TypeFilter filter : ENTITY_TYPE_FILTERS) {
  101.             if (filter.match(reader, readerFactory)) {
  102.                 return true;
  103.             }
  104.         }
  105.         return false;
  106.     }
  107. }

 

[java][/java] view plaincopy

  1. /**
  2.  * $Id:$
  3.  */
  4. package com.share.utils.hibernate;
  5. import org.hibernate.annotations.common.reflection.XClass;
  6. import org.hibernate.annotations.common.reflection.XProperty;
  7. import org.hibernate.cfg.Ejb3Column;
  8. import org.hibernate.mapping.PersistentClass;
  9. import com.share.annotations.Comment;
  10. public class CommentBinder {
  11.     public static void bindTableComment(XClass clazzToProcess, PersistentClass persistentClass) {
  12.         if (clazzToProcess.isAnnotationPresent(Comment.class)) {
  13.             String tableComment = clazzToProcess.getAnnotation(Comment.class).value();
  14.             persistentClass.getTable().setComment(tableComment);
  15.         }
  16.     }
  17.     public static void bindColumnComment(XProperty property, Ejb3Column[] columns) {
  18.         if (null != columns)
  19.             if (property.isAnnotationPresent(Comment.class)) {
  20.                 String comment = property.getAnnotation(Comment.class).value();
  21.                 for (Ejb3Column column : columns) {
  22.                     column.getMappingColumn().setComment(comment);
  23.                 }
  24.             }
  25.     }
  26.     public static void bindColumnComment(XProperty property, Ejb3Column column) {
  27.         if (null != column)
  28.             if (property.isAnnotationPresent(Comment.class)) {
  29.                 String comment = property.getAnnotation(Comment.class).value();
  30.                 column.getMappingColumn().setComment(comment);
  31.             }
  32.     }
  33. }
[java][/java] view plaincopy

  1. /**
  2.  * $Id:$
  3.  */
  4. package com.share.annotations;
  5. import java.lang.annotation.ElementType;
  6. import java.lang.annotation.Retention;
  7. import java.lang.annotation.RetentionPolicy;
  8. import java.lang.annotation.Target;
  9. @Target({ElementType.TYPE,ElementType.FIELD})
  10. @Retention(RetentionPolicy.RUNTIME)
  11. public @interface Comment {
  12.     String value() default “”;
  13. }

标签