选择显示字体大小

用15分钟了解daozero:让它为你实现dao接口

1. daozero是什么?它可以在哪方面帮助我?

*假设你具有使用spring的ibatis支持类作为持久层实现的实际编码经验(即时没有,学习spring和ibatis也应该不是件怎么难的事情)。

daozero是1个很小的spring java bean。可以到http://dao-zero.sourceforge.net下载。使用daozero可以减少基于 ibatis+spring的持久层代码数量,因为daozero会动态地替我们实现持久层接口。它不是1个spring中ibatis支持类的包装,而是用来直接替换掉我们手工编写的持久层实现代码的。使用daozero时,一旦我们完成了dao接口的定义(java interface),通常情况下,我们只需要再在spring context定义文件中声明类型(class)为daozero.ibatis.dao的bean,并且设置这些bean的targettype属性为已定义好的dao接口,然后这些daozero bean 就会在运行时为我们动态地生成实现了targettype的dao实现类,由这些实现类去调用ibatis api访问数据库。所以,不需要dao接口的实现代码了。

2. daozero的工作原理

daozero约定ibatis sql mapping xml文件中定义的statement的名字需要和dao接口(抽象类)的method的名字保持一致,而且,当前版本还要求statement的parameter的数量及首次出现顺序也必须要和dao接口(抽象类)的method的参数(形参)的数量及出现顺序保持一致,通过做出这些约定(应该不太难于遵守吧),daozero就可以确定如何把method被调用时传入的参数(实参)和statement的 parameter对应起来,于是就可以用这些传入的参数组成statement需要的parameter map,去调用ibatis api访问数据库。daozero是一个实现了org.springframework.beans.factory.factorybean 的bean,就是说它是可以产生bean的factory bean,而daozero factory bean产生的bean就是实现了dao接口的dao对象,这些dao对象负责调用spring的ibatis template的方法,例如queryforobject()、queryforlist()和update()等。这些dao对象也有足够“智能”,它们会依据method的返回类型推断出该调用queryforobject()还是queryforlist()(当前版本尚不支持queryformap)。 daozero factory bean是如何产生dao对象的呢?这要视其属性targettype是接口还是抽象类来定:如果targettype是接口,那么使用jdk标准的 proxy(java.lang.reflect.proxy)机制,由该proxy负责拦截下对该接口方法的调用;如果是抽象类,那么就使用cglib的enhancer.net.sf.cglib.proxy.enhancer)莱拦截下对该抽象类中抽象方法的调用,而将方法调用拦截下来后的处理则基本上一致。使用jdk proxy或cglib enhancer对性能的影响在数百纳秒(ns)这个数量级,因此对于大多数web应用来说相对于数据库sql执行是可以忽略不计的。

事实上daozero不得不hack了一些ibatis的代码,不得不把ibatis提供的一些接口强行转型(cast)到ibatis的内部实现类,原因在于ibatis似乎没有提供检索其statement元数据的方法,使得daozero不得不在代码中留下了一些坏味道。(所以,如果ibatis出现了大的版本改变,那么daozero这部分代码也不得不重新写。)

3. 用daozero代替原来的ibatis dao bean

假设我们有一个数据库表叫"account",表结构如下所示,

create table account (
userid varchar(80) not null,
email varchar(80) not null,
constraint pk_account primary key (userid)
);


我们使用了一个叫account的domain class来代表该表,account是标准的java bean(pojo),具有属性:"userid"和"email",

public class account implements serializable {
private string userid;
private string email;
public string getuserid() { return this.userid; }
public void setuserid(string s) { this.userid=s; }
public string getemail() { return this.email; }
public void setemail(string s) { this.email=s; }
}


操作account的dao接口是这样的,

public interface accountdao {
account getaccountbyuserid(string userid) throws dataaccessexception;
void updateaccount(account account) throws dataaccessexception;
list getusernamelist() throws dataaccessexception;
}



使用daozero之前,一般情况下,我们会用ibatis做一个dao实现类,该类继承自spring的sqlmapclientdaosupport,该基类封装了ibatis sqlmapclient的主要操作以和spring集成起来,该实现类可能是这样的:

import org.springframework.org.ibatis.support.sqlmapclientdaosupport;

public class accountdaoimpl extends sqlmapclientdaosupport implements accountdao {
   public account getaccountbyuseridandemail(string userid, string email) throws dataaccessexception {
    map params = new hashmap();
    params.put( "userid", userid );
    params.put( "email", email );
    return gettemplate().queryforobject( "getaccountbyuseridandemail", params );
   }

  public int updateaccount(account account) throws dataaccessexception {
    return gettemplate().update( "updateaccount", account );
   }
   public list getusernamelist() throws dataaccessexception;
    return gettemplate().queryforlist( "getusernamelist", null );
   }


自然,ibatis的sql mapping xml文件是必不可少的:

<select id=&quot;getaccountbyuseridandemail&quot; resultclass=&quot;account&quot;>
    select * from account where userid=#userid# and email=#email#
</select>

<select id=&quot;getusernamelist&quot; resultclass=&quot;java.lang.string&quot;>
    select userid from account
</select>

<update id=&quot;updateaccount&quot;>
    update account set email = #email# where userid=#userid#
</update>


然后,我们通常会到spring context xml文件中为该dao声明1个spring bean?:

<bean id=&quot;accountdao&quot; class=&quot;accountdaoimpl&quot;>
     <property name=&quot;sqlmapclient&quot; ref=&quot;sqlmapclient&quot;/>
</bean>


现在,我们来试试看使用了daozero后daozero怎样来去掉accountdaoimpl那些繁复无聊的代码:很简单,第一步是删掉那个accountdaoimpl.java! 然后,修改上面那段spring context xml文件中bean的定义,改成:

<bean id=&quot;accountdao&quot; class=&quot;daozero.ibatis.dao&quot;>
     <property name=&quot;sqlmapclient&quot; ref=&quot;sqlmapclient&quot;/>
    <property name=&quot;targettype&quot; value=&quot;accountdao&quot; /> <!-- interface -->
</bean>


可以看到,新的使用daozero的方式用的bean的class是daozero库提供的1个固定的类--&quot;daozero.ibatis.dao&quot;该类会在运行时自动提供accountdao接口的实现类,所以accountdaoimpl.java就可以扔掉了,就这么简单。

4. 使用抽象类的例子

虽然上述例子足以应付多数情况,但是daozero不可能为我们完成任何事情,有时候还是有不得不手工实现一些dao方法的需要的,这时候我们如何既保留下daozero提供的好处, 又能够告诉daozero哪些方法我们已经自己手工实现了而不需要daozero代劳了呢?解决办法是很自然的,写好我们自己的dao类,但是只需要填上需要手工实现的,其他的可以继续由daozero代劳, daozero会依据该实现类的某方法是否是abstract来判断是否需要替我们去做事情,不需要的话就什么也不做,直接转交给我们填好的方法代码,所以,这时候的dao实现类是抽象类。另外需要做的事情就是把bean的targettype换成这个abstract的dao实现类。对于上面那个例子,我们保留下accountdaoimpl.java,手工填写updateaccount()方法:

public abstract class accountdaoimpl implements accountdao {
public abstract int __updateaccount(account account) throws dataaccessexception;
public int updateaccount(account account) throws dataaccessexception {
    // insert additional operation code here
    return __updateaccount(account);
}
}


最后,需要修改sql mapped statement的名字,改成&quot;__updateaccount&quot;,以便让daozero了解statement __updateaccount是和dao方法__updateaccount联系在一起的:

<select id=&quot;getaccountbyuseridandemail&quot; resultclass=&quot;account&quot;>
    select * from account where userid=#userid# and email=#email#
</select>

<select id=&quot;getusernamelist&quot; resultclass=&quot;java.lang.string&quot;>
    select userid from account
</select>

<update id=&quot;__updateaccount&quot;>
    update account set email = #email# where userid=#userid#
</update>


当然,daozero并不反对你不修改statement名字,不增加一个__updateaccount方法,而是在updateaccount()方法中按照从前的写法自己调用ibatis api(不过,既然改改statement的名字 就可以让daozero继续为我们服务,而且能保持代码的一致性,我想通常还是加上个__updateaccount方法更合适一些,这也是daozero推荐的一个实践 -- daozero希望你关注于完成daozero替你 代劳不了的事情,而不是去写那些重复性很大的代码。)

5. 使用当前版本的daozero的一些限制条件

# 同一个类中不允许有同名方法(名字重载),除非这些同名方法的参数个数不一样(否则,daozero为statement寻找对应方法时会因出现歧义而失败)。
# 由daozero负责实现的抽象方法必须是public abstract。因为当前版本的daozero使用java标准的reflection机制(class.getmethods())来遍历dao类的method,所以非public abstract的方法是找不到的,这是一个可以改进的限制条件 -- daozero计划在后续版本中自行分析class文件来找到protected abstract方法,这样就可以不用强迫我们不得不把内部方法也public出来了;
# 目前daozero尚不支持ibatis api中的queryformap()方法和pagination机制(queryformap在实现计划之中,而pagination我正考虑如何实现,而且既然使用ibatis就是为了获得使用native sql 的灵活性和性能,我个人更倾向于不使用ibatis的pagination作为分页支持机制,而习惯于利用数据库sql对分页的支持语法);
# 虽然目前只支持ibatis,但为hibernate给出一个daozero实现也是在计划之中(因为我和不少人一样,也喜欢hibernate);

6. 使用时出现问题?

# 确保参数的数量和首次出现顺序在statement和method中是保持一致的,而且必须是abstract,根据使用经验,大多数错误来自于此;
# 看看是否满足第5节说明的一些限制条件,很可能忽略的一种情况就是把method做成protected abstract了。

7. 其他

实际上还有1个小技巧:namespace -- 是的,这可以用于支持ibatis的namespace机制,但是1个dao只能限定于1个namespace。具体使用方法请参照下载下来的sample。sample是修改自spring的jpetstore例子,比较一下它和原始的spring的jpetstore的实现例子,可以找到daozero提倡的如何在service层使用dao的理念,例如daozero更提倡不要只把service作为dao层的delegation,提倡大部分对dao的操作的组合都放到service层中去。

8. license:

apache license version 2.0


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons