由于要求在项目中使用泛型的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
是泛型的 Dao 接口:
/** */
/**
* 2006-11-22
* 范型DAO接口
* @author Zou Ang
* Contact
Zou Ang
*/
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
Zou Ang
*/
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
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);
}
}
然后就到了配置文件了:
<
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" />
< 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 >
还要在Staff.hbm.xml中配置:
<
query
name
="Staff.getByLogonAndId"
>
select s from Staff s where s.staffLogon = ? and s.staffId = ? ]]>
query >
这里要特别注意
这个要写在
的外面,否则会提示Mapping Exception:No Named Query
好了,大公告成了!现在可以跟以前一样使用
appContext.getBean("staffDao");
这样进行测试了
staffDao.read(new Integer(1));
staffDao.getByLogonAndId("abc",new Integer(2));