由于要求在项目中使用泛型的DAO,所以上网Google了一下,找到了IBM的一篇文章。文章讲得不错,但是有些地方不清楚,如果完全按照那篇文章可能还会遇到一些困难。所以写了这篇文章,解释如何在项目中加入泛型的DAO实现。
首先是总的类关系的UML图:
然后是在配置文件中的关系图:
其中,IStaffDao是我们自己定义的接口,这个接口类似:
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 String queryNameFromMethod(Class findTargetType, Method finderMethod);
}
目前有两个命名查找策略,使用的是
{
public String queryNameFromMethod(Class findTargetType, Method finderMethod)
{
return findTargetType.getSimpleName() + " . " + finderMethod.getName();
}
}
FinderArgumentTypeFactory 目前还没有什么作用,主要是返回自定义的 Hibernate 类型:
{
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 FinderIntroductionAdvisor()
{
super ( new FinderIntroductionInterceptor());
}
}
{
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" />
<!-- 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中配置:
<![CDATA[ select s from Staff s where s.staffLogon = ? and s.staffId = ? ]]>
</ query >
这里要特别注意<query></query>这个要写在<class></class>的外面,否则会提示Mapping Exception:No Named Query
好了,大公告成了!现在可以跟以前一样使用
staffDao.read(new Integer(1));
staffDao.getByLogonAndId("abc",new Integer(2));