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

 

以下是处理结果示例:

@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)