摘自:http://www.wujianrong.com/archives/hibernate/index4.html
•XDoclet概要:
XDoclet 是一个通用的代码生成实用程序,是一个扩展的Javadoc Doclet引擎(现已与Javadoc Doclet独立),XDoclet是EJBDoclet的后继者,而EJBDoclet是由Rickard Oberg发起的。(http://xdoclet.sourceforge.net/xdoclet/index.html), 它允许您使用象 JavaDoc 标记之类的东西来向诸如类、方法和字段之类的语言特征添加元数据。随后,它利用这些额外的元数据来生成诸如部署描述符和源代码之类的相关文件。可以让你创 建自己的javadoc @tags进而利用XDoclet中的Templet enging基于这些@tags生成源代码或其他文件(例如xml的deployment descriptors)。
XDoclet 继承了 JavaDoc 引擎的思想,允许根据定制 JavaDoc 标记生成代码和其他文件。当然,XDoclet 也可以访问整个解析树。这样,它就可以访问类、类的包结构和类的方法。
人类发明了计算机来让它做那些枯燥无味的事情,而将自己解脱出来,去做有创造性的事情。使用 XDoclet 模板将使开发人员从单调无味的代码中解脱出来。 |
XDoclet 提供了自己的模板引擎。该模板引擎在概念上类似于 JavaServer Pages(JSP)技术。它实质上包含两类标记:块标记(block tag)和内容标记(content tag)。块标记控制如 Java 编程语言中的 if
和 for
语句之类的流。内容标记打印当前解析树上下文的片段,如类名称、方法名称和参数,等等。
•模板示例:
这里有一个简单的模板,它寻找所有的实体 Bean(EntityBean),然后打印出它们的类名称及其所有 cmp 字段的名称:
<XDtClass:forAllClasses type="javax.ejb.EntityBean"> Classname=<XDtClass:className/> <XDtProperty:forAllPropertiesWithTag tagName="ejb.persistence"> CMP Field = <XDtMethod:propertyName/> </XDtProperty:forAllPropertiesWithTag> </XDtClass:forAllClasses> |
这个模板同时演示了块标记和内容标记。
—forAllClasses 是一个块标记示例,它遍历传递给模板引擎的所有类,这些类是通过调用 XDoclet 的 ant 构建文件中的 fileset
传递的。forAllClasses
使用 type
属性(type="javax.ejb.EntityBean"
)过滤掉不属于类型 javax.ejb.EntityBean 的那些类。
—className
是内容标记的一个示例,它打印出当前类的名称。
forAllProperitesWithTag
是另一个块标记示例。它遍历实现类中所有具有 XDoclet 标记 @ejb.persistence
的特性(这些特性与该 bean 的 cmp 字段相关)。最后,propertyName
是内容标记的另外一个示例,因为它在遍历过程中显示当前特性名称。
下面用黑色粗体字体显示了块标记,用红色粗体字体显示了内容标记。另外,这些块还用圆矩形框来演示其范围。
|
这个代码模板的输出如下:
Classname=EmployeeBean CMP Field = id CMP Field = firstName CMP Field = lastName CMP Field = phone Classname=DeptBean CMP Field = id CMP Field = name |
要运行该模板,您的 ant 构建文件中将需要下列代码:
<target name="templatedoclet" > <taskdef name="templatedoclet" classname="xdoclet.DocletTask" classpathref="xdocpath" /> <templatedoclet destdir="test"> <fileset dir="${src}"> <include name="**/*Bean.java"/> </fileset> <template templateFile="template/template.xdt" destinationfile="test.txt"/> </templatedoclet> </target> |
templatedoclet 任务用来执行模板。fileset
子元素用于指定:您只想要 src 目录中以“Bean.java”结尾的文件。模板子任务用于指定被使用的模板文件以及目标文件。
•XDoclet 体系结构:
XDoclet 由三个主要组件组成:
模块由几个部分组成:
|
—XJavaDoc 引擎:XJavaDoc 解析 Java 源文件,然后构建有关类和语言特征(包、方法和字段)以及元数据的信息树。XJavaDoc 引擎通过一个易于使用的 API 提供访问。该 API 提供了与带有一些额外特征的 JavaDoc API 相同的类信息,这些额外特征与存储及读取元数据以及其他结构相关联。XJavaDoc 增加了在运行时修改 JavaDoc 标记的能力。这样就可以推断元数据,并可以将其缺省值设为比较合理的值。
—XDoclet 引擎:XJavaDoc 引擎读取标记,这些标记组成了类的元数据和结构。XDoclet 引擎使用来自 XJavaDoc 引擎的信息,来生成支持文件(源代码和部署描述符)。XDoclet 提供了一个优秀的模板生成引擎,该引擎将模板转换成一个或多个支持文件。XDoclet 有一个模块装入程序,它动态地装入用 xdoclet.xml 文件(包含在模块的 jar 文件中)指定的 XDoclet 模块。
您无需创建模块就可创建模板。每个顶级 XDoclet Ant 任务都有执行任意模板的能力,以此替换随模块一起提供的模板。
—模块引擎:模块由任务、子任务、标记处理程序和模板组成。
子任务:子任务指定要调用的缺省模板,它允许您向该模板传递配置参数。在编写了一些模板之后,您将编写一个子任务。子任务的示例如下所示:
上面的 模板:XDoclet 模板生成部署描述符和源代码文件。实际上,XDoclet 模板可以生成任何类型的文件。正是这种能力为组件开发提供了帮助,生成了组件的各种配置文件和部署描述符。实际上,您甚至可以编写自己的组件框架,然后使 用 XDoclet 来生成另外的部署类型文件。 标记处理程序:标记处理程序对于 XDoclet 标记,就如同定制标记(Custom Tag)处理程序对于 JSP 定制标记。XDoclet 引擎根据标记的名称将标记映射到与该标记对应的标记处理程序。标记处理程序类必须生成 xdoclet.TagHandler 的子类。XDoclet 使用反射来根据标记名称调用标记处理程序的方法。因此,XDtProperty:forAllPropertiesWithTag 在 Property 标记处理程序中寻找方法 forAllPropertiesWithTag。 模板引擎通过在与标记一起提供的 xdoclet.xml 文件中查找标记处理程序,来了解要使用哪个标记处理程序。以下是核心 xdoclet.xml 文件中用于 Property 标记处理程序入口的代码片段:
当 XDoclet 碰到 XDtProperty:forAllPropertiesWithTag 标记时,它便在 以下是 Property 标记处理程序的部分清单:
|
•查找模板块和内容标记:
要开发自己的定制模板,最好的方法是参考 XDoclet 在其模块中提供的类似模板。您可以这样做:解压缩(unjar)那些模块,寻找以 xdt 结尾的文件,或者下载 XDoclet 源代码并寻找那些模板。此外,还有一种方法,借助类似于 JavaDoc 的文档来查找与 XDoclet 一起提供的所有可用的模板标记。
幸运的是,XDoclet 提供了整个模板语言的参考大全。可以在 [XDoclet Install Dir]/docs/templates/index.html 中找到该参考大全。它是您开发真正属于自己的定制模板的指南。
如果您熟悉 JavaDoc,那么该模板语言参考大全使用起来就很简单。XDoclet 标记处理程序对应于那些标记的名称空间,它们显示在左上面板中。特定处理程序的实际标记显示在左下方,而该标记的文档则显示在右边的面板中。如果您想查找 一个标记(例如,XDtClass:forAllClasses),那么请从名称空间剥离掉 Xdt(例如,XdtClass 变成 class),然后查找该标记(如,forAllClasses)。请参阅上图以便搞清楚如何查找 XDtClass:forAllClasses。
Continue reading "XDoclet 学习笔记"
使用Hibernate,对实体类,需要Map 文件,XDoclet在写类时加入类似Javadoc的@hibernate.xxx 指示,然后用XDoclet自动生成Map 文件,使写程序更加方便。
使用XDoclet一般使用Ant,在build.xml中加入XDoclet的部分。 XDoclet需要有相应的jar文件支持,不过,因为XDoclet是在Ant运行时使用的,所以XDoclet的jar可以不放在webapps /../WEB-INF/lib下,可另外放一个目录(有时XDoclet的jar会和其它的jar冲突)
从XDoclet网上下 xdoclet.zip 或tar.gz,可放在一个单独的目录,如lib-xdoc下,一般需要:(以1.2版本为例)
- xdoclet-1.2.jar
- xdoclet-xdoclet-module-1.2.jar
- xdoclet-hibernate-module-1.2.jar
- xjavadoc-1.0.2.jar
其它的通用的jar,Hibernate等也用到,如commons-logging-x.x.x.jar,xerces-x.x.x.jar,可放在lib目录下。
目录结构WEB-INF
- lib
- lib-xdoc
- src
- classes
- build.xml
- web.xml
这样,在Ant 的build.xml中同时加入lib 和 lib-xdoc下的jars,编译时用到 XDoclet,运行时就不用XDoclet了。
build.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project name="Test" default="compile" basedir=".">
<property name="classes" location="./classes"/>
<property name="src" value="src" />
<property name="db" value="db" />
<property name="lib" value="lib" />
<property name="lib-xdoc" value="lib-xdoc" />
<property name="build" value="classes" />
<path id="myclasspath">
<fileset dir="${lib}">
<include name="*.jar"/>
</fileset>
<fileset dir="${lib-xdoc}">
<include name="*.jar"/>
</fileset>
<pathelement location="${build}"/>
</path>
<target name="init">
<mkdir dir="${classes}"/>
</target>
<target name="compile" depends="init">
<javac executable="jikes" classpathref="myclasspath" srcdir="${src}" destdir="${classes}"/>
</target>
<target name="clean">
<delete dir="${classes}"/>
</target>
<target name="xdoc" description="Generates Hibernate class descriptor files.">
<taskdef name="hibernatedoclet"
classname="xdoclet.modules.hibernate.HibernateDocletTask">
<classpath refid="myclasspath"/>
</taskdef>
<hibernatedoclet
destdir="${classes}"
excludedtags="@version,@author,@todo"
force="true"
verbose="true">
<fileset dir="${src}">
<include name="**/*.java"/>
</fileset>
<hibernate version="2.0"/>
</hibernatedoclet>
</target>
<!-- Other target omited -->
</project>
使用XDoclet 的例子:
例1.Org.java 表示一个公司的各个组织,组成一个树形结构,就是一个Org 可能有一个父的Org
用many-to-one/one-to-many,用同一个column(parent) 指向本身(bean.Org),有一个用inverse
package bean;
import java.util.*;
/**
* @hibernate.class table="test_org"
*/
public class Org extends Persistent{
//注:id在Persistent中
private String name;
private Org parent;
private Set children=new TreeSet();
/**
* @hibernate.property length="60"
*/
public String getName(){
return name;
}
public void setName(String value){
name=value;
}
/**
* @hibernate.many-to-one class="bean.Org" column="parent"
*/
public Org getParent(){
return parent;
}
public void setParent(Org value){
parent=value;
}
/**
* @hibernate.set inverse="true" lazy="true" cascade="all"
* @hibernate.collection-key column="parent"
* @hibernate.collection-one-to-many class="bean.Org"
*/
public Set getChildren(){
return children;
}
public void setChildren(Set value){
children=value;
}
}
Persistent.java
package bean;
import net.sf.hibernate.Validatable;
import net.sf.hibernate.ValidationFailure;
/**
* @hibernate.class table="test_persistent"
*/
public class Persistent implements Validatable{
protected int id;
/**
* @hibernate.id generator-class="increment" type="int" unsaved-value="null"
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void validate() throws ValidationFailure {
}
}
自动生成的Map 文件如下:
Org.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="bean.Org"
table="test_org"
dynamic-update="false"
dynamic-insert="false">
<id
name="id"
column="id"
type="int"
unsaved-value="null">
<generator class="increment">
</generator>
</id>
<property
name="name"
type="java.lang.String"
update="true"
insert="true"
column="name"
length="60"/>
<many-to-one
name="parent"
class="bean.Org"
cascade="none"
outer-join="auto"
update="true"
insert="true"
column="parent"/>
<set
name="children"
lazy="true"
inverse="true"
cascade="all"
sort="unsorted">
<key
column="parent"/>
<one-to-many
class="bean.Org"/>
</set>
</class>
</hibernate-mapping>
例2.用户(User)和职位(Position),一个用户可有多个职位,可用many-to-may 关系
用的表是同一个(test_user_position),在Position中用"inverse",实现双向关联(Bidirectional Associations)
Users.java:
package bean;
import java.util.*;
/**
* @hibernate.class table="test_user"
*/
public class User extends Persistent{
//......
private Set positions=new TreeSet();
/**
* @hibernate.set table="test_user_position" lazy="true"
* @hibernate.collection-key column="user_id"
* @hibernate.collection-many-to-many class="bean.Position" column="position_id"
*/
public Set getPositions(){
return positions;
}
public void setPositions(Set value){
positions=value;
}
}
Position.java
package bean;
import java.util.*;
/**
* @hibernate.class table="test_position"
*/
public class Position extends Persistent{
//......
private Set users=new TreeSet();
/**
* @hibernate.set inverse="true" lazy="true" table="test_user_position"
* @hibernate.collection-key column="position_id"
* @hibernate.collection-many-to-many class="bean.User" column="user_id"
*/
public Set getUsers(){
return users;
}
public void setUsers(Set value){
users=value;
}
}
小结:
使用XDoclet,可方便生成Hibernate 的Map文件,加快开发。
注意事项:使用双向关联(Bidirectional Associations)时相应的column一定要正确,否则XDoclet不会报错,但是生成的数据库表不正确。
参考:
Hibernate 主站:www.hibernate.org
Hibernate Xdoclet:http://www.chinaunix.net/jh/26/92019.html
XDoclet 主站:http://xdoclet.sourceforge.net/
用XDoclet生成hbm.xml就是在.java文件里写入一些元数据,XDoclet会从这些数据以及类本身得到足够的信息来生成目标文件。当然,除了用于hibernate,XDoclet还可以用于web、ejb等等很多用途。
XDoclet要从sourceforge上下载,包含了很多jar包、文档和例子,我觉得文档做得还是不错的,查起来比较方便。要使用XDoclet,一般要通过ant来完成,也就是在ant脚本里加入XDoclet的内容。
由于eclipse已经包含了ant支持,因此我没有专门去下载一个ant回来,而是直接使用eclipse带的,版本是1.5.3。
创建一个名为build.xml的脚本(其实应该换个名,比如gen-hbm.xml,看起来比较明白),内容如下:
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="XDoclet Examples" default="hibernate" basedir=".">
<property name="xdoclet.root.dir" value="c:/xdoclet-1.2.1"/>
<property name="xdoclet.lib.dir" value="${xdoclet.root.dir}/lib"/>
<path id="myclasspath">
<fileset dir="${xdoclet.lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<taskdef
name="hibernatedoclet"
classname="xdoclet.modules.hibernate.HibernateDocletTask"
classpathref="myclasspath"
/>
<target name="hibernate" description="Generate mapping documents">
<echo>+---------------------------------------------------+</echo>
<echo>| |</echo>
<echo>| R U N N I N G H I B E R N A T E D O C L E T |</echo>
<echo>| |</echo>
<echo>+---------------------------------------------------+</echo>
<hibernatedoclet
destdir="./src"
excludedtags="@version,@author,@todo,@see"
addedtags="@xdoclet-generated at ${TODAY},@copyright The XDoclet Team,@author XDoclet,@version ${version}"
force="false"
verbose="true">
<fileset dir="./src">
<include name="org/haree/struts/form/UserForm.java"/>
</fileset>
<hibernate version="2.0"/>
</hibernatedoclet>
</target>
</project>
我曾经卡住的一个地方就是在taskdef里的classpathref属性。一开始我在eclipse的ant运行参数里设置了XDoclet相关的包,总是提示:
Can't create a hibernate element under hibernatedoclet. Make sure the jar file containing the corresponding subtask class is on the classpath specified in the <taskdef> that defined {2}.
后来如上设置了classpathref,即包含了XDoclet使用到的包,并将eclipse的ant里关于XDoclet的包都去掉,竟然就成功了。其实现在也不明白为什么会这样。。。