在最近的一个项目中,使用了 hibernate+struts ,由于中间没有使用 spring 来管理 bean ,所以我使用了 DAO+Service 来做持久层和业务层。受 spring 对 hibernate 的 template 的封装的影响,我自己实现了一个简单的 hibernateTemplate —— MHibernateTemplate 和调用接口 MHibernateCallback 。在其中包装了错误拦截等动作。所以我的 DAO 就不能直接从 Myecipse 生成了。但由于 Domain bean 太多,而且 DAO 中又是简单的代码重复,所以想到了使用代码生成。刚开始想直接在 Myeclipse 中生成 DAO 的时候使用自己的模版就可以了,但是在网上找了半天都没有相关的信息。后来模仿 easyjtools 使用 velocity 自己做代码生成,但感觉时间不够,所以,就选择了 XDoclet 。
网上相关的内容不是很多,所以就只有啃 En 版的 XDoclet in Action 。由于我的需求很简单,所以只使用了简单的 template (就是 .xdt )来生成代码,搞了两天,效果还不错。
比如一个 model :(这个项目的需求比较奇怪,由于要同步,所以每个对象都必须有一个 pkid (包括中间表), 2 就是每个对象有个 dr 标志,表示删除(而不是真正的删除))。
package com.my.xdoclet;
/**
* PubCompproper generated by MyEclipse - Hibernate Tools
* @hasRef
* 公司性质
*/
public class PubCompproper extends BaseDomain implements java.io.Serializable {
// Fields
/**
* @pkid
*/
private String cproPkid ;
// 系统类型信息
/**
* @ref .model name="sytp"
*/
private PubSystype sytp ;
// 公司性质编码
private String cproCode ;
// 公司性质名称
private String cproName ;
// 删除表示
/**
* @del
*/
private String cproDr ;
// Constructors
/** default constructor */
public PubCompproper() {
this .setCproDr( "0" );
}
// Property accessors
public String getCproPkid() {
return this . cproPkid ;
}
public void setCproPkid(String cproPkid ) {
this . cproPkid = cproPkid ;
}
public String getCproCode() {
return this . cproCode ;
}
public void setCproCode(String cproCode ) {
this . cproCode = cproCode ;
}
public String getCproName() {
return this . cproName ;
}
public void setCproName(String cproName ) {
this . cproName = cproName ;
}
/**
* @return the sytp
*/
public PubSystype getSytp() {
return sytp ;
}
/**
* @param sytp the sytp to set
*/
public void setSytp(PubSystype sytp ) {
this . sytp = sytp ;
}
/**
* @return the cproDr
*/
public String getCproDr() {
return cproDr ;
}
/**
* @param cproDr the cproDr to set
*/
public void setCproDr(String cproDr ) {
this . cproDr = cproDr ;
}
}
其中有一些相关的对象,在 DAO 中要使用到。
看看模版文件
package com.hycs.bs.client.itf;
import java.util.List;
import <XDtPackage:packageName />.<XDtClass:className />;
public interface <XDtClass:className />DAO {
// 添加
boolean add(<XDtClass:className /> instance);
<XDtClass:ifHasClassTag tagName="hasRef">
// 添加
boolean add(<XDtClass:className /> instance,<XDtField:forAllFields><XDtField:ifHasFieldTag tagName="ref.model" paramName="name"> String <XDtField:fieldTagValue tagName="ref.model" paramName="name" />pkid</XDtField:ifHasFieldTag></XDtField:forAllFields>);
</XDtClass:ifHasClassTag>
// 删除
boolean del (String pkid);
// 更新
boolean update(<XDtClass:className /> instance);
// 列出所有
List list();
List list(boolean withDr);
// 得到一个对象
<XDtClass:className /> get(String pkid);
}
这个是 interface 的模版,其中的模版标签都很简单易懂;
<XDtTagDef:tagDef namespace="Primitive" handler="com.my.xdoclet.customTags.UpperName" />
package com.hycs.bs.client.call;
import com.hycs.bs.sys.MHibernateTemplate;
import com.hycs.util.Constant;
import com.hycs.util.OidHelper;
import com.hycs.bs.sys.HibernateCodeUtil;
import com.hycs.bs.sys.HibernateUtil;
public class <XDtClass:className/>DAOImpl implements <XDtClass:className/>DAO{
private MHibernateTemplate template;
<XDtField:forAllFields>
<XDtField:ifHasFieldTag tagName="ref.model" paramName="name">
private final <XDtField:fieldType/>DAO <XDtField:fieldTagValue tagName="ref.model" paramName="name"/>dao = new <XDtField:fieldType/>DAOImpl();
</XDtField:ifHasFieldTag>
</XDtField:forAllFields>
public <XDtClass:className/>DAOImpl(){
this.template=new MHibernateTemplate(HibernateUtil.getSessionFactory());
}
public boolean add(<XDtClass:className/> instance) {
// TODO Auto-generated method stub
// add your code and pkid generhere;
//instance.setCproCode(HibernateCodeUtil.getLastCode("PubCompproper", "cproCode", "cproPkid"));
//instance.setCproPkid(OidHelper.oidSingle());
return this.template.save(instance);
}
<XDtClass:ifHasClassTag tagName="hasRef">
public boolean add(<XDtClass:className/> instance, <XDtField:forAllFields><XDtField:ifHasFieldTag tagName="ref.model" paramName="name"> String <XDtField:fieldTagValue tagName="ref.model" paramName="name" />pkid</XDtField:ifHasFieldTag></XDtField:forAllFields>) {
// TODO Auto-generated method stub
<XDtField:forAllFields>
<XDtField:ifHasFieldTag tagName="ref.model" paramName="name">
<XDtField:fieldType/> <XDtField:fieldTagValue tagName="ref.model" paramName="name" />=this.<XDtField:fieldTagValue tagName="ref.model" paramName="name"/>dao.get(<XDtField:fieldTagValue tagName="ref.model" paramName="name" />pkid);
if(<XDtField:fieldTagValue tagName="ref.model" paramName="name" />==null){
return false;
}
instance.set<XDtPrimitive:upperName value='<XDtField:fieldTagValue tagName="ref.model" paramName="name" />' />(<XDtField:fieldTagValue tagName="ref.model" paramName="name" />);
</XDtField:ifHasFieldTag>
</XDtField:forAllFields>
return this.add(instance);
}
</XDtClass:ifHasClassTag>
public boolean del (String pkid) {
// TODO Auto-generated method stub
<XDtClass:className /> instance =this.get(pkid);
if(instance==null||instance.get<XDtField:forAllFields><XDtField:ifHasFieldTag tagName="pkid" ><XDtPrimitive:upperName value="<XDtField:fieldName />" /></XDtField:ifHasFieldTag></XDtField:forAllFields>==null){
return false;
}
instance.set<XDtField:forAllFields><XDtField:ifHasFieldTag tagName=" del " ><XDtPrimitive:upperName value="<XDtField:fieldName />" /></XDtField:ifHasFieldTag></XDtField:forAllFields>(Constant.MODEL_DEL);
return this.template.update(instance);
}
public <XDtClass:className /> get(String pkid) {
// TODO Auto-generated method stub
return (<XDtClass:className />)this.template.get(<XDtClass:className />.class, pkid);
}
public List list() {
// TODO Auto-generated method stub
return this.list(true);
}
public List list(boolean withDr) {
// TODO Auto-generated method stub
if(withDr){
return HibernateCodeUtil.listWithDr("<XDtClass:className />","<XDtField:forAllFields><XDtField:ifHasFieldTag tagName=" del "><XDtField:fieldName /></XDtField:ifHasFieldTag></XDtField:forAllFields>" );
}else{
return this.template.getAll(<XDtClass:className />.class);
}
}
public boolean update(<XDtClass:className /> instance) {
// TODO Auto-generated method stub
<XDtClass:ifHasClassTag tagName="hasRef">
<XDtClass:className /> temp=this.get(instance.get<XDtField:forAllFields><XDtField:ifHasFieldTag tagName="pkid" ><XDtPrimitive:upperName value="<XDtField:fieldName />" /></XDtField:ifHasFieldTag></XDtField:forAllFields>());
<XDtField:forAllFields>
<XDtField:ifHasFieldTag tagName="ref.model" paramName="name">
if(instance.get<XDtPrimitive:upperName value='<XDtField:fieldTagValue tagName="ref.model" paramName="name" />' />()==null){
instance.set<XDtPrimitive:upperName value='<XDtField:fieldTagValue tagName="ref.model" paramName="name" />' />(temp.get<XDtPrimitive:upperName value='<XDtField:fieldTagValue tagName="ref.model" paramName="name" />' />());
}
</XDtField:ifHasFieldTag>
</XDtField:forAllFields>
</XDtClass:ifHasClassTag>
return this.template.update(instance);
}
}
这是个比较复杂的模版了,是 DAO 的具体实现,
在写这个模版的时候,我遇到了几个问题,
1 就是标签的嵌套,比如 <XDtPrimitive:upperName value='<XDtField:fieldTagValue tagName="ref.model" paramName="name" />' /> ,开始内部标签都使用转义符,结果搞不定,网上找没有任何相关的内容,后来直接在外层使用 ’ (就象 js ),搞定。
2 就是对于标签的内容的首字符大写,没有提供这个功能的标签,使用了自定义的:
package com.my.xdoclet.customTags;
import java.util.Properties;
import xdoclet.XDocletTagSupport;
public class UpperName extends XDocletTagSupport {
public String upperName(Properties attribute){
String value=attribute.getProperty("value");
String upper= upper(value);
return upper;
}
private static String upper(String value){
return value.toUpperCase().substring(0,1)+value.substring(1);
}
}
并在模版文件中使用 <XDtTagDef:tagDef namespace="Primitive" handler="com.my.xdoclet.customTags.UpperName" /> 来应用就直接能在模版文件中使用 <XDtPrimitive:upperName > 来使用了,
下面是 bulid.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<project name= "xdocletExample" default= "doall" basedir= "." >
<property name= "xdoclet.lib.dir" location= "${basedir}/lib" />
<property name= "gen.src.dir" location= "${basedir}/target" />
<property name= "src.dir" location= "${basedir}/src" />
<property name= "template.dir" location= "${basedir}/template" />
<property name= "customtag.dir" location= "${basedir}/bin" />
<path id= "xdoclet.lib.path" >
<fileset dir= "${xdoclet.lib.dir}" includes= "*.jar" />
</path>
<taskdef name= "xdoclet" classname= "xdoclet.DocletTask" classpathref= "xdoclet.lib.path" />
<target name= "init" />
<target name= "daogener" depends= "init" >
<xdoclet destdir= "${gen.src.dir}" >
<fileset dir= "${src.dir}" includes= "**/*.java" />
<template templateFile= "${template.dir}/daointerface.xdt" acceptInterfaces= "false" acceptAbstractClasses= "false" destinationfile= "{0}DAO.java" />
</xdoclet>
</target>
<target name= "daoimplgener" depends= "init" >
<xdoclet destdir= "${gen.src.dir}" >
<fileset dir= "${src.dir}" includes= "**/*.java" />
<template templateFile= "${template.dir}/daoimpl.xdt" acceptInterfaces= "false" acceptAbstractClasses= "false" destinationfile= "{0}DAOImpl.java" />
</xdoclet>
</target>
<target name= "doall" depends= "daogener,daoimplgener" />
</project>
build 一下:
生成的代码如下:
package com.hycs.bs.client.itf;
import java.util.List;
import com.my.xdoclet.PubCompproper;
public interface PubCompproperDAO {
// 添加
boolean add(PubCompproper instance);
// 添加
boolean add(PubCompproper instance, String sytppkid);
// 删除
boolean del (String pkid);
// 更新
boolean update(PubCompproper instance);
// 列出所有
List list();
List list(boolean withDr);
// 得到一个对象
PubCompproper get(String pkid);
}
这个是接口
package com.hycs.bs.client.call;
import com.hycs.bs.sys.MHibernateTemplate;
import com.hycs.util.Constant;
import com.hycs.util.OidHelper;
import com.hycs.bs.sys.HibernateCodeUtil;
import com.hycs.bs.sys.HibernateUtil;
public class PubCompproperDAOImpl implements PubCompproperDAO{
private MHibernateTemplate template;
private final com.my.xdoclet.PubSystypeDAO sytpdao = new com.my.xdoclet.PubSystypeDAOImpl();
public PubCompproperDAOImpl(){
this.template=new MHibernateTemplate(HibernateUtil.getSessionFactory());
}
public boolean add(PubCompproper instance) {
// TODO Auto-generated method stub
// add your code and pkid generhere;
//instance.setCproCode(HibernateCodeUtil.getLastCode("PubCompproper", "cproCode", "cproPkid"));
//instance.setCproPkid(OidHelper.oidSingle());
return this.template.save(instance);
}
public boolean add(PubCompproper instance, String sytppkid) {
// TODO Auto-generated method stub
com.my.xdoclet.PubSystype sytp=this.sytpdao.get(sytppkid);
if(sytp==null){
return false;
}
instance.setSytp(sytp);
return this.add(instance);
}
public boolean del (String pkid) {
// TODO Auto-generated method stub
PubCompproper instance =this.get(pkid);
if(instance==null||instance.getCproPkid==null){
return false;
}
instance.setCproDr(Constant.MODEL_DEL);
return this.template.update(instance);
}
public PubCompproper get(String pkid) {
// TODO Auto-generated method stub
return (PubCompproper)this.template.get(PubCompproper.class, pkid);
}
public List list() {
// TODO Auto-generated method stub
return this.list(true);
}
public List list(boolean withDr) {
// TODO Auto-generated method stub
if(withDr){
return HibernateCodeUtil.listWithDr("PubCompproper","cproDr" );
}else{
return this.template.getAll(PubCompproper.class);
}
}
public boolean update(PubCompproper instance) {
// TODO Auto-generated method stub
PubCompproper temp=this.get(instance.getCproPkid());
if(instance.getSytp()==null){
instance.setSytp(temp.getSytp());
}
return this.template.update(instance);
}
}
这个是代码。
于是我的工作就很简单了,适用 Myeclipse 直接从 DataExplor 中生成 Domain bean 和映射文件,改一下关联,在 domain 中添加必要的 XDoclet 标记, build ,就可以专著于具体的业务了。
但这个代码还有点问题就是当遇到一个类有多个关联对象的时候,在生成的一些方法上,要自己手动增加或者删除一个 ”,” 。这个还要继续学习。
同时 XDoclet 提供了很好的扩展机制,这个也要继续研究。
再次就是我在想 XDoclet 中有没有直接使用标签来定义标签的功能,或者在模版内定义变量??
easyjweb 使用 XDoclet 来生成代码也会是很简单而且稳定的。
(注:本文作者, EasyJF开源团队 stef_wu,转载请保留作者声明!)