站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2EE 专题用15分钟了解DaoZero:让它为你实现DAO接口

用15分钟了解DaoZero:让它为你实现DAO接口

摘要: 用15分钟了解DaoZero:让它为你实现DAO接口

DaoZero是1个很小的Spring Java Bean。可以到http://dao-zero.sourceforge.net下载。使用DaoZero可以减少基于 iBatis+Spring的持久层代码数量,因为DaoZero会动态地替我们实现持久层接口。

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="getAccountByUserIdAndEmail" resultClass="Account">
select * from account where userid=#userId# AND email=#email#
</select>

<select id="getUsernameList" resultClass="java.lang.String">
select userid from account
</select>


update account set email = #email# where userid=#userId#


然后,我们通常会到Spring Context XML文件中为该DAO声明1个Spring Bean?:





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