我们知道可以通过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上测试成功,因为代码只是在开发期运行,不需要良好的结构和优化,所以只是简单实现了,需要此功能的朋友可以自己实现。

以下是处理结果示例:

@Entity
@Table(name = "AuditRecord_")
@Comment("系统审计表")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class AuditRecord implements Serializable { private static final long serialVersionUID = 8844543936912679937L;
@Id
@GeneratedValue
@Comment("ID主键")
private Long id;
@Comment("审计类型")
@Column(length = 30)
private String auditType;
@Comment("操作人")
@ManyToOne
@JoinColumn(name = "userId")
private Passport operator;
// 操作简述
@Comment("操作简述")
@Column(length = 4000)
private String description;
@Comment("创建时间")
private Date creationTime;
}

生成的DDL如下:

create table audit_record_ (
id bigint not null auto_increment comment 'ID主键',
audit_type varchar(30) comment '审计类型',
creation_time datetime comment '创建时间',
description longtext comment '操作简述',
user_id bigint comment '操作人',
primary key (id)
) comment='系统审计表';

主要代码片断如下:

import java.io.IOException;
import java.util.Properties; import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass; import org.hibernate.MappingException;
import org.hibernate.cfg.MyConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils; public class SchemaExportTool extends MyConfiguration { private static final long serialVersionUID = 1L; private static final String RESOURCE_PATTERN = "/**/*.class"; private static final String PACKAGE_INFO_SUFFIX = ".package-info"; private static final TypeFilter[] ENTITY_TYPE_FILTERS = new TypeFilter[] {
new AnnotationTypeFilter(Entity.class, false),
new AnnotationTypeFilter(Embeddable.class, false),
new AnnotationTypeFilter(MappedSuperclass.class, false) };
private final ResourcePatternResolver resourcePatternResolver; public SchemaExportTool() {
this.resourcePatternResolver = ResourcePatternUtils
.getResourcePatternResolver(new PathMatchingResourcePatternResolver());
} public static void main(String[] args) {
try {
Properties p = new Properties();
p.setProperty("hibernate.dialect",
"org.hibernate.dialect.MySQLDialect");
SchemaExportTool cfg = new SchemaExportTool();
cfg.addProperties(p);
cfg.setNamingStrategy(new ImprovedMyNamingStrategy());
cfg.scanPackage("com.share.passport.domain",
"com.share.authority.domain", "com.share.utils.domain"); SchemaExport se = new SchemaExport(cfg);
if (null != args && args.length > 1)
if ("-f".equals(args[0]))
se.setOutputFile(args[1]);
else
se.setOutputFile("create_table.sql");
else
se.setOutputFile("create_table.sql");
se.setDelimiter(";");
// se.drop(false, false);
se.create(false, false); } catch (Exception e) {
e.printStackTrace();
} } private SchemaExportTool scanPackage(String... packagesToScan) {
try {
for (String pkg : packagesToScan) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(pkg)
+ RESOURCE_PATTERN;
Resource[] resources = this.resourcePatternResolver
.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(
this.resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader reader = readerFactory
.getMetadataReader(resource);
String className = reader.getClassMetadata()
.getClassName();
if (matchesEntityTypeFilter(reader, readerFactory)) {
addAnnotatedClass(this.resourcePatternResolver
.getClassLoader().loadClass(className));
} else if (className.endsWith(PACKAGE_INFO_SUFFIX)) {
addPackage(className.substring(
0,
className.length()
- PACKAGE_INFO_SUFFIX.length()));
}
}
}
}
return this;
} catch (IOException ex) {
throw new MappingException(
"Failed to scan classpath for unlisted classes", ex);
} catch (ClassNotFoundException ex) {
throw new MappingException(
"Failed to load annotated classes from classpath", ex);
}
} /**
* Check whether any of the configured entity type filters matches the
* current class descriptor contained in the metadata reader.
*/
private boolean matchesEntityTypeFilter(MetadataReader reader,
MetadataReaderFactory readerFactory) throws IOException {
for (TypeFilter filter : ENTITY_TYPE_FILTERS) {
if (filter.match(reader, readerFactory)) {
return true;
}
}
return false;
} }
/**
* $Id:$
*/
package com.share.utils.hibernate; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.Ejb3Column;
import org.hibernate.mapping.PersistentClass; import com.share.annotations.Comment; public class CommentBinder {
public static void bindTableComment(XClass clazzToProcess, PersistentClass persistentClass) {
if (clazzToProcess.isAnnotationPresent(Comment.class)) {
String tableComment = clazzToProcess.getAnnotation(Comment.class).value();
persistentClass.getTable().setComment(tableComment); }
} public static void bindColumnComment(XProperty property, Ejb3Column[] columns) {
if (null != columns) if (property.isAnnotationPresent(Comment.class)) {
String comment = property.getAnnotation(Comment.class).value();
for (Ejb3Column column : columns) {
column.getMappingColumn().setComment(comment);
} }
} public static void bindColumnComment(XProperty property, Ejb3Column column) {
if (null != column)
if (property.isAnnotationPresent(Comment.class)) {
String comment = property.getAnnotation(Comment.class).value(); column.getMappingColumn().setComment(comment); }
}
}
/**
* $Id:$
*/
package com.share.annotations; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Comment {
String value() default "";
}

让Hibernate生成的DDL脚本自动增加注释的更多相关文章

  1. Unity脚本自动添加注释脚本及排版格式

    Unity脚本自动添加注释脚本及头部注释排版格式 公司开发项目,需要声明版权所有,,,,标注公司名,作者,时间,项目名称及描述等等. 自己总结实现的现成脚本及头部注释排版文本,添加到模版即可. 文件不 ...

  2. Hibernate 生成策略和缓存策略

    主键生成策略 一.主键分类 1. 自然主键 主键本身就是表中的一个字段,实体中一个具体的属性,对象本身唯一的特性 比如:创建一个学生表:姓名.年龄.身份证号(自然主键) 2. 代理主键 主键本身不是表 ...

  3. atitit.自动生成数据库结构脚本,或者更换数据库,基于hibernate4

    atitit.自动生成数据库结构脚本,或者更换数据库,基于hibernate4 目前近况:: 更换数据库,但是是使用spring集成的. <!-- hibernate配置文件路径 --> ...

  4. spring和mybatis集成,自动生成model、mapper,增加mybatis分页功能

    软件简介 Spring是一个流行的控制反转(IoC)和面向切面(AOP)的容器框架,在java webapp开发中使用广泛.http://projects.spring.io/spring-frame ...

  5. 自动生成makefile的脚本

    如果需要测试某一个特性,写了一个test.cpp 某天又增加了一个utils.cpp,依此类推,测试文件越来越多 每次测试时都要手动维护一个makefile实在是不明智的 于是萌生了用脚本自动维护的念 ...

  6. 数据泵如何生成导出文件的DDL脚本

    在使用exp/imp时,生成对应dumpfile文件的DDL脚本非常容易,在使用命令imp时,添加参数show, show=y表示展示imp导入的时候,输出相关DDL语句(不包括insert语句),而 ...

  7. 「懒惰的美德」我用 python 写了个自动生成给文档生成索引的脚本

    我用 python 写了一个自动生成索引的脚本 简介:为了刷算法题,建了一个 GitHub仓库:PiperLiu / ACMOI_Journey,记录自己的刷题轨迹,并总结一下方法.心得.想到一个需求 ...

  8. Loadrunner脚本自动关联和手动关联

    关于Loadrunner关联一.什么时候需要关联 1.关联的含义        关联(correlation):在脚本回放过程中,客户端发出请求,通过关联函数所定义的左右边界值(也就是关联规则),在服 ...

  9. 三大框架 之 Hibernate生成策略与缓存策略(主键生成策略、持久化、持久化类划分、一级缓存、事物管理)

    目录 Hibernate生成策略与缓存策略 主键生成策略 主键分类 主键的生成策略 持久化 什么是持久化 什么是持久化类 持久化类编写规则 持久化类的划分 三种状态区分 持久态对象特征 一级缓存 什么 ...

随机推荐

  1. jquery插件-validate

    1.引入js,css 下载地址:http://plugins.jquery.com/validate/ 2.设置验证规则:input的class添加以下验证属性 3.设置不符合规则的提示信息:添加da ...

  2. excel快递单号查询工具以及源码

    Function kdcx(kd, orderid) Dim Err, url, kdtime, link, Errcode, Status Select Case kd '此处支持的快递公司很多的 ...

  3. Android ART简介

    一.    Android ART简介 Android DEX/ODEX/OAT文件

  4. mongodb常用命令【转】

    mongodb由 C++编写,其名字来自humongous这个单词的中间部分,从名字可见其野心所在就是海量数据的处理.关于它的一个最简洁描述为:scalable, high-performance, ...

  5. [小知识]不显示没有内容的UITableViewCell

    开发过程中常常使用到UITableView,当tableView的内容不足一屏时,若设置了talbeView的高度为屏幕高度,就会出现没有内容的cell显示出来,效果非常不好看,要想让没有内容的cel ...

  6. 【Tools】Chrome 控制台不完全指南

    Chrome的开发者工具已经强大到没朋友的地步了,特别是其功能丰富界面友好的console,使用得当可以有如下功效: 更高「逼格」更快「开发调试」更强「进阶级的Frontender」 Bug无处遁形「 ...

  7. web sql Database

    http://www.ibm.com/developerworks/cn/web/1108_zhaifeng_websqldb/ http://baishanheishui.iteye.com/blo ...

  8. Spring MVC 解读——@RequestMapping (1)(转)

    转自:http://my.oschina.net/HeliosFly/blog/212329 Spring MVC 解读——@RequestMapping 为了降低文章篇幅,使得文章更目标化,简洁化, ...

  9. java学习面向对象之final关键字

    之前我们讲过继承的相关知识了,继承就是子类继承父类的属性和方法并且还可以覆盖父类的方法.但是这样有一个缺陷是什么呢,就是当我们一个类当中涉及一些封装的核心的东西或者对整个系统非常关键的方法或者类的时候 ...

  10. [LeetCode#159] Missing Ranges Strobogrammatic Number

    Problem: Given a string, find the length of the longest substring T that contains at most 2 distinct ...