我们知道可以通过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. Express使用html模板

    express默认使用jade模板,可以配置让其支持使用ejs或html模板. 1. 安装ejs 在项目根目录安装ejs. npm install ejs 2.引入ejs var ejs = requ ...

  2. predis如何实现phpredis的pconnect方法

    predis和phpredis都是redis的php客户端,区别可以看这里,这里不赘述. phpredis是php扩展,由C语言编写,诞生较早,很多PHPer都熟悉. predis是用PHP语言编写, ...

  3. ab基本用法

    ab的全称是ApacheBench,是 Apache 附带的一个小工具,专门用于 HTTP Server 的benchmark testing,可以同时模拟多个并发请求.前段时间看到公司的开发人员也在 ...

  4. 最常见的HTTP错误

    1. HTTP 500错误(内部服务器错误)对对HTTP 500错误的定义已经充分证明了这是一个最常见的HTTP错误. 一般来说,HTTP 500 错误就是web服务器发生内部错误时返回的信息. 例如 ...

  5. Spring4.0整合Hibernate3 .6

    转载自:http://greatwqs.iteye.com/blog/1044271 6.5  Spring整合Hibernate 时至今日,可能极少有J2EE应用会直接以JDBC方式进行持久层访问. ...

  6. bzoj 3624: [Apio2008]免费道路 生成树的构造

    3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 111  Solved: 4 ...

  7. java or spring +jython +python (Error:python.home,Determine if the following attributes are correct:)

    最近更新 :16年3月10日更 首先你在用JAVA,你需要运行Python,于是你找了Jython,我不介绍什么是Jythyon了 如何在Java中调用Python的方法,一百度一大堆, 如下:是一种 ...

  8. Keil_C51程序调试过程

    调试一般都是在发生错误与意外的情况下使用的.如果程序能正常执行,调试很多时候都是用不上的.所以,最高效率的程序开发还是程序员自己做好规范,而不是指望调试来解决问题. 单片机的程序调试分为两种,一种是使 ...

  9. android 一个页面内 多个listview的实现

    如果很平常的两个listview组件竖直放在linearLayout布局中,结果是: 两个listview 很独立,中间似乎有个分割线,完全吧他们分离了,各自独立滚动,如果上面的listview把整个 ...

  10. 项目经验谈---IM新消息界面刷新异常处理记录

    项目中使用到ContentObserver来观察Provider的变化,当对Uri做数据库操作时对应的Provider会发一条Notify消息调用UI上层的ContentChange方法,在这个Con ...