Posted on 2006-11-26 21:08
Zou Ang 阅读(5431)
评论(8) 编辑 收藏 所属分类:
由于要求在项目中使用泛型的DAO,所以上网Google了一下,找到了IBM的一篇文章。文章讲得不错,但是有些地方不清楚,如果完全按照那篇文章可能还会遇到一些困难。所以写了这篇文章,解释如何在项目中加入泛型的DAO实现。
首先是总的类关系的UML图:
然后是在配置文件中的关系图:
其中,IStaffDao是我们自己定义的接口,这个接口类似:
public
interface
IStaffDAO
extends
GenericDao
<
Staff, Integer
>
{
public
List listAll();
public
Staff getByLogonAndId(String logon, Integer id);
//
more
}
GenericDao<T , PK extends Serilizable>
是泛型的
Dao
接口:
/** */
/**
* 2006-11-22
* 范型DAO接口
*
@author
Zou Ang
* Contact <a href ="mailto:richardeee@gmail.com">Zou Ang</a>
*/
public
interface
GenericDao
<
T, PK
extends
Serializable
>
{
/** */
/**
* 保存一个对象到数据库
*
@param
newInstance 需要保存的对象
*
@return
*/
PK create(T newInstance);
/** */
/**
* 从数据库读取一个对象
*
@param
id 主键
*
@return
*/
T read(PK id);
/** */
/**
* 更新一个对象
*
@param
transientObject 被更新的对象
*/
void
update(T transientObject);
/** */
/**
* 删除一个对象
*
@param
transientObject 被删除的对象
*/
void
delete(T transientObject);
}
GenericDaoHibernateImpl
是
GenericDao
接口的泛型实现
:
/** */
/**
* 2006-11-22
* 范型DAO实现
*
@author
Zou Ang
* Contact <a href ="mailto:richardeee@gmail.com">Zou Ang</a>
*/
public
class
GenericDaoHibernateImpl
<
T,PK
extends
Serializable
>
extends
HibernateDaoSupport
implements
GenericDao
<
T, PK
>
,FinderExecutor
{
private
Class
<
T
>
type;
private
FinderNamingStrategy namingStrategy
=
new
SimpleFinderNamingStrategy();
//
Default. Can override in config
private
FinderArgumentTypeFactory argumentTypeFactory
=
new
SimpleFinderArgumentTypeFactory();
//
Default. Can override in config
public
GenericDaoHibernateImpl(Class
<
T
>
type)
{
this
.type
=
type;
}
/**/
/*
(non-Javadoc)
* @see com.gdnfha.atcs.common.service.dao.GenericDao#create(java.lang.Object)
*/
public
PK create(T newInstance)
{
return
(PK)getHibernateTemplate().save(newInstance);
}
/**/
/*
(non-Javadoc)
* @see com.gdnfha.atcs.common.service.dao.GenericDao#delete(java.lang.Object)
*/
public
void
delete(T transientObject)
{
getHibernateTemplate().delete(transientObject);
}
/**/
/*
(non-Javadoc)
* @see com.gdnfha.atcs.common.service.dao.GenericDao#read(java.io.Serializable)
*/
public
T read(PK id)
{
return
(T)getHibernateTemplate().get(type, id);
}
/**/
/*
(non-Javadoc)
* @see com.gdnfha.atcs.common.service.dao.GenericDao#update(java.lang.Object)
*/
public
void
update(T transientObject)
{
getHibernateTemplate().update(transientObject);
}
public
List
<
T
>
executeFinder(Method method,
final
Object[] queryArgs)
{
final
Query namedQuery
=
prepareQuery(method, queryArgs);
return
(List
<
T
>
) namedQuery.list();
}
public
Iterator
<
T
>
iterateFinder(Method method,
final
Object[] queryArgs)
{
final
Query namedQuery
=
prepareQuery(method, queryArgs);
return
(Iterator
<
T
>
) namedQuery.iterate();
}
private
Query prepareQuery(Method method, Object[] queryArgs)
{
final
String queryName
=
getNamingStrategy().queryNameFromMethod(type, method);
final
Query namedQuery
=
getSession().getNamedQuery(queryName);
String[] namedParameters
=
namedQuery.getNamedParameters();
if
(namedParameters.length
==
0
)
{
setPositionalParams(queryArgs, namedQuery);
}
else
{
setNamedParams(namedParameters, queryArgs, namedQuery);
}
return
namedQuery;
}
private
void
setPositionalParams(Object[] queryArgs, Query namedQuery)
{
//
Set parameter. Use custom Hibernate Type if necessary
if
(queryArgs
!=
null
)
{
for
(
int
i
=
0
; i
<
queryArgs.length; i
++
)
{
Object arg
=
queryArgs[i];
Type argType
=
getArgumentTypeFactory().getArgumentType(arg);
if
(argType
!=
null
)
{
namedQuery.setParameter(i, arg, argType);
}
else
{
namedQuery.setParameter(i, arg);
}
}
}
}
private
void
setNamedParams(String[] namedParameters, Object[] queryArgs, Query namedQuery)
{
//
Set parameter. Use custom Hibernate Type if necessary
if
(queryArgs
!=
null
)
{
for
(
int
i
=
0
; i
<
queryArgs.length; i
++
)
{
Object arg
=
queryArgs[i];
Type argType
=
getArgumentTypeFactory().getArgumentType(arg);
if
(argType
!=
null
)
{
namedQuery.setParameter(namedParameters[i], arg, argType);
}
else
{
if
(arg
instanceof
Collection)
{
namedQuery.setParameterList(namedParameters[i], (Collection) arg);
}
else
{
namedQuery.setParameter(namedParameters[i], arg);
}
}
}
}
}
public
FinderNamingStrategy getNamingStrategy()
{
return
namingStrategy;
}
public
void
setNamingStrategy(FinderNamingStrategy namingStrategy)
{
this
.namingStrategy
=
namingStrategy;
}
public
FinderArgumentTypeFactory getArgumentTypeFactory()
{
return
argumentTypeFactory;
}
public
void
setArgumentTypeFactory(FinderArgumentTypeFactory argumentTypeFactory)
{
this
.argumentTypeFactory
=
argumentTypeFactory;
}
}
FinderNamingStrategy
是查找方法的命名规范:
public
interface
FinderNamingStrategy
{
public
String queryNameFromMethod(Class findTargetType, Method finderMethod);
}
目前有两个命名查找策略,使用的是
Simple
的,也就是直接是
<
类型名
>.<
方法名
>
的形式。
public
class
SimpleFinderNamingStrategy
implements
FinderNamingStrategy
{
public
String queryNameFromMethod(Class findTargetType, Method finderMethod)
{
return
findTargetType.getSimpleName()
+
"
.
"
+
finderMethod.getName();
}
}
FinderArgumentTypeFactory
目前还没有什么作用,主要是返回自定义的
Hibernate
类型:
public
class
SimpleFinderArgumentTypeFactory
implements
FinderArgumentTypeFactory
{
public
Type getArgumentType(Object arg)
{
//
if(arg instanceof Enum)
//
{
//
return getEnumType(arg.getClass());
//
}
//
else
//
{
return
null
;
//
}
}
//
private Type getEnumType(Class<? extends Object> argClass)
//
{
//
Properties p = new Properties();
//
p.setProperty("enumClassName", argClass.getName());
//
Type enumType = TypeFactory.heuristicType("org.hibernate.demo.EnumUserType", p);
//
return enumType;
//
}
}
FinderIntroductionAdvisor
和
FinderIntroductionInterceptor:
public
class
FinderIntroductionAdvisor
extends
DefaultIntroductionAdvisor
{
public
FinderIntroductionAdvisor()
{
super
(
new
FinderIntroductionInterceptor());
}
}
public
class
FinderIntroductionInterceptor
implements
IntroductionInterceptor
{
public
Object invoke(MethodInvocation methodInvocation)
throws
Throwable
{
FinderExecutor executor
=
(FinderExecutor) methodInvocation.getThis();
String methodName
=
methodInvocation.getMethod().getName();
if
(methodName.startsWith(
"
get
"
)
||
methodName.startsWith(
"
list
"
))
{
Object[] arguments
=
methodInvocation.getArguments();
return
executor.executeFinder(methodInvocation.getMethod(), arguments);
}
else
if
(methodName.startsWith(
"
iterate
"
))
{
Object[] arguments
=
methodInvocation.getArguments();
return
executor.iterateFinder(methodInvocation.getMethod(), arguments);
}
//
else if(methodName.startsWith("scroll"))
//
{
//
Object[] arguments = methodInvocation.getArguments();
//
return executor.scrollFinder(methodInvocation.getMethod(), arguments);
//
}
else
{
return
methodInvocation.proceed();
}
}
public
boolean
implementsInterface(Class intf)
{
return
intf.isInterface()
&&
FinderExecutor.
class
.isAssignableFrom(intf);
}
}
然后就到了配置文件了:
<!--
Start :范型DAO配置
-->
<
bean
id
="abstractDaoTarget"
class
="com.gdnfha.atcs.common.service.dao.hibernate.GenericDaoHibernateImpl"
abstract
="true"
>
<
property
name
="sessionFactory"
>
<
ref
local
="sessionFactory"
/>
</
property
>
<
property
name
="namingStrategy"
>
<
ref
bean
="simpleFinderNamingStratrgy"
/>
</
property
>
</
bean
>
<
bean
id
="abstractDao"
class
="org.springframework.aop.framework.ProxyFactoryBean"
abstract
="true"
>
<
property
name
="interceptorNames"
>
<
list
>
<
value
>
finderIntroductionAdvisor
</
value
>
</
list
>
</
property
>
</
bean
>
<
bean
id
="finderIntroductionAdvisor"
class
="com.gdnfha.atcs.common.service.dao.finder.FinderIntroductionAdvisor"
/>
<
bean
id
="namingStrategy"
class
="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"
>
<
property
name
="staticField"
>
<
value
>
org.hibernate.cfg.ImprovedNamingStrategy.INSTANCE
</
value
>
</
property
>
</
bean
>
<
bean
id
="extendedFinderNamingStrategy"
class
="com.gdnfha.atcs.common.service.dao.finder.impl.ExtendedFinderNamingStrategy"
/>
<
bean
id
="simpleFinderNamingStratrgy"
class
="com.gdnfha.atcs.common.service.dao.finder.impl.SimpleFinderNamingStrategy"
/>
<!--
End: 范型DAO配置
-->
<!--
Start: 测试范型DAO
-->
<
bean
id
="staffDao"
parent
="abstractDao"
>
<
property
name
="proxyInterfaces"
>
<
value
>
com.gdnfha.atcs.maintain.service.dao.IStaffDAO
</
value
>
</
property
>
<
property
name
="target"
>
<
bean
parent
="abstractDaoTarget"
>
<
constructor-arg
>
<
value
>
com.gdnfha.atcs.common.pojo.Staff
</
value
>
</
constructor-arg
>
</
bean
>
</
property
>
</
bean
>
<!--
End:测试范型DAO
-->
还要在Staff.hbm.xml中配置:
<
query
name
="Staff.getByLogonAndId"
>
<![CDATA[
select s from Staff s where s.staffLogon = ? and s.staffId = ?
]]>
</
query
>
这里要特别注意<query></query>这个要写在<class></class>的外面,否则会提示Mapping Exception:No Named Query
好了,大公告成了!现在可以跟以前一样使用
appContext.getBean("staffDao");
这样进行测试了
staffDao.read(new Integer(1));
staffDao.getByLogonAndId("abc",new Integer(2));