struts2+spring3+hibernate4整合(3)---包的组织和DAO层的重构
? ? ? ? ?在这里稍作解释即可:src下的com.zq下有两个包,分别是common和demo。common中放的都是整个项目中公用的部分,比如po(model),dao,daoImpl,service,serviceImpl,web,util。其中web中又可以分为action,filter,listener等,对于不同的架构ssh或者其他web框架,包的组织是可变的。demo是一个示例性的包,其内容和common中几乎是一样的,在以后的开发中,这个demo相当于一个模块,比如对于一个hr的项目来说,可以是薪资模块,档案模块等。test是我自己写单元测试的一个文件夹,放在这里不甚规范,大家可以视而不见。然后剩下的就是配置文件了。system-config.properties是系统必要的一些配置。
这种包的组织方式结构清晰,可以立刻定位到发生错误的模块和代码,也易于维护。
? ? ? ? ? 二、DAO层的重构。
? ? ? ? 先回顾一下java中的一些概念:
? ? ? ? 继承:为什么要抽象出父类来继承呢?简单的说,继承是为了代码的复用,把一些重复性的代码提取出来,凡是继承我的子类都可以共享。比如你老爸有辆车,你是你老爸的儿子,你继承了你老爸的家产,所以这辆车你就可以开。
? ? ? ? 接口:为什么要抽象出接口去实现?抽象接口的过程就是制定规范的过程,最好的例子就是JDBC。我要连接数据库,就得有Connection,而oracle,SQLServer各个数据库厂商就得去提供这样的实现,才JDBC才能“一统江湖”。
? ? ? ? 先上一个图,在战略上藐视一下这个结构(电脑上没有UML的工具,是我用ppt搞的一个图):
?

?
首先有一个老大接口,他是所有DAO的祖宗,里面有所有DAO通用的一些方法,比如save啦,delete啦等。
然后各个DAO还有自己一些独特的方法,比如根据在一个网上商城的项目中,需要根据用户的ID去查询该用户所有的送货地址。这个功能是某个DAO独有的,那怎么办呢?简单!创建一个DAO(图中的XXXDAO),让他去继承IBaseDAO,通用的方法也继承过来了,特有的方法也定义好了,nice~
然后有了一个“特立独行的DAO(XXXDAO)”之后,我们就需要去实现啦!怎么实现呢?写个DAO的实现类就成了呗!可是问题又来了,用什么技术去实现呢,hibernate?mybatis?还是原生JDBC呢?要是项目经理说:hibernate最能满足咱们的需求,咱们就用hibernate吧!好嘞,咱们就用hibernate,于是就有了XXXHibernateDAO,但是接口继承了接口,里面需要去实现的方法太多了!少说也得十多个,而且就那些重复的代码也太多了吧,烦死人了。于是乎,BaseHibernateDAO横空出世。它是干嘛的呢?他就是帮助你少写代码的好帮手啊,让他去实现IBaseDAO里面的方法,然后被XXXHibernateDAO继承,这样不久不用去实那些通用方法了吗,那些通用方法被BaseHibernateDAO搞定啦!XXXHibernateDAO需要做的就是去实现那些“特立独行”的方法就可以啦!这样又少了很多工作量,perfect!
? ? ? ? 说了这么多理论的东西,都快晕了,贴上代码,满满去体味吧。
先来个实体类PO:Customer:
?
package com.zq.demo.po;import javax.persistence.*;import java.io.Serializable;import java.util.Date;@Entity@Table(name="customer")public class Customer implements Serializable { private long dbid; private String name; private String pwd; private Date birthday; public Customer() { } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public long getDbid() { return dbid; } public void setDbid(long dbid) { this.dbid = dbid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; }}?然后重点推荐:IBaseDAO
?
package com.zq.common.dao;import java.io.Serializable;import java.util.List;public interface IBaseDAO<M extends Serializable> { public long save(M model); public void saveOrUpdate(M model); public void update(M model); public void delete(long id); public void deleteObject(M model); public M get(long id); public int countAll(); public List<M> listAll(); public List<M> listAll(int start, int limit); boolean exists(long id); public void flush(); public void clear();}?然后在看看他的hibernate的实现:BaseHibernateDAO
package com.zq.common.dao.hibernate;import com.zq.common.dao.IBaseDAO;import org.hibernate.Query;import org.hibernate.SQLQuery;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.transform.Transformers;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;import javax.persistence.Id;import java.io.Serializable;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.util.Date;import java.util.List;import java.util.Map;@Component("baseHibernateDAO")public abstract class BaseHibernateDAO<M extends Serializable> implements IBaseDAO<M> { @Autowired @Qualifier("sessionFactory") //按名字自动输入,省略setter和getter方法 private SessionFactory sessionFactory; private final Class<M> entityClass; private final String HQL_LIST_ALL; private final String HQL_COUNT_ALL; private String pkName = null; @SuppressWarnings("unchecked") public BaseHibernateDAO() { /* * getClass().getGenericSuperclass()返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type * 然后将其转换ParameterizedType。 getActualTypeArguments()返回表示此类型实际类型参数的 Type 对象的数组。 * [0]就是这个数组中第一个了。 简而言之就是获得超类的泛型参数的实际类型。 * * */ this.entityClass = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; Field fields[] = this.entityClass.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Id.class)) { this.pkName = field.getName(); } } //Assert.notNull(pkName); HQL_LIST_ALL = "from " + this.entityClass.getSimpleName() + " order by " + pkName + " desc"; HQL_COUNT_ALL = " select count(*) from " + this.entityClass.getSimpleName(); } public Session getSession() { //事务必须是开启的(Required),否则获取不到 return sessionFactory.getCurrentSession(); } @Override //保存 public long save(M model) { return ((Long)this.getSession().save(model)).longValue(); } @Override //保存或者更新 public void saveOrUpdate(M model) { this.getSession().saveOrUpdate(model); } @Override //更新 public void update(M model) { this.getSession().update(model); } //根据sql语句更新 public void updateBySQL(String sql, Object... params) { Query query = getSession().createQuery(sql); setParameters(query, params); query.executeUpdate(); } @Override //按照主键删除 public void delete(long id) { this.getSession().delete(id); } @Override //按照对象删除 public void deleteObject(M model) { this.getSession().delete(model); } @Override //通过主键获得 public M get(long id) { return (M) getSession().get(this.entityClass, id); } @Override //根据主键判断是否存在 public boolean exists(long id) { return this.get(id) != null; } @Override //刷新 public void flush() { getSession().flush(); } @Override //清理 public void clear() { getSession().clear(); } @Override //获取所有挑数 public int countAll() { return getSession().createQuery(HQL_COUNT_ALL).list().size(); } @Override //获取所有对象 public List<M> listAll() { return getSession().createQuery(HQL_LIST_ALL).list(); } @Override //按照分页获取 public List<M> listAll(int start, int limit) { return getSession().createQuery(HQL_LIST_ALL).setFirstResult(start).setMaxResults(limit).list(); } //根据条件分页查询 public List<M> listAllByConditionsAndPaging(String sql, int start, int limit, Object... params) { Session session = getSession(); Query query = session.createQuery(sql); setParameters(query, params); query.setFirstResult(start).setMaxResults(limit); return query.list(); } //根据条件不分页查询 public List<M> listAllByConditionsAndPaging(String sql, Object... params) { Session session = getSession(); Query query = session.createQuery(sql); setParameters(query, params); return query.list(); } //设置参数 protected void setParameters(Query query, Object[] paramlist) { if (paramlist != null) { for (int i = 0; i < paramlist.length; i++) { if (paramlist[i] instanceof Date) { query.setTimestamp(i, (Date) paramlist[i]); } else { query.setParameter(i, paramlist[i]); } } } } /** * 根据查询条件返回唯一一条记录 */ protected <T> T getUniqueResult(final String hql, final Object... paramlist) { Query query = getSession().createQuery(hql); setParameters(query, paramlist); return (T) query.setMaxResults(1).uniqueResult(); } /** * 执行nativeSQL批处理语句.如 之间insert, update, delete 等. */ protected int execteNativeSQLUpdate(final String natvieSQL, final Object... paramlist) { Query query = getSession().createSQLQuery(natvieSQL); setParameters(query, paramlist); Object result = query.executeUpdate(); return result == null ? 0 : ((Integer) result).intValue(); } protected List<Map<String, Object>> execteNativeSQLSelect(String natvieSQL, final Object... paramlist) { SQLQuery query = getSession().createSQLQuery(natvieSQL); setParameters(query, paramlist); query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); return query.list(); } protected List<Map<String, Object>> execteNativeSQLSelect(String natvieSQL, int start, int limit, Object... paramlist) { SQLQuery query = getSession().createSQLQuery(natvieSQL); setParameters(query, paramlist); query.setFirstResult(start); query.setMaxResults(limit); query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); return query.list(); }}?再然后,看看具体的DAO,这里面有业务逻辑需要的独特的方法:CustomerDAO
package com.zq.demo.dao;import com.zq.common.dao.IBaseDAO;import com.zq.demo.po.Customer;import java.util.List;import java.util.Map;public interface CustomerDAO extends IBaseDAO<Customer> { public List<Map<String,Object>> listAllAddrOfCustomer();}?然后实际去干活的人出场喽:CustomerDAOImpl
package com.zq.demo.daoImpl;import com.zq.common.dao.hibernate.BaseHibernateDAO;import com.zq.demo.dao.CustomerDAO;import com.zq.demo.po.Customer;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.List;import java.util.Map;@Component("customerDAO")public class CustomerDAOImpl extends BaseHibernateDAO<Customer> implements CustomerDAO { @Autowired @Qualifier("sessionFactory") private SessionFactory sessionFactory; @Override public List<Map<String, Object>> listAllAddrOfCustomer() { Session session=sessionFactory.getCurrentSession(); //do something List<Map<String,Object>> list=new ArrayList<Map<String, Object>>(); return list; }}?以上只是简单的演示,里面有很多逻辑都是简化的不能再简化了。至于这四个DAO之间的关系,还是看着那个图理解比较好,一个图胜过一千行文字。再贴上一段简单的测试代码,保存一个customer:
@Test public void testSave01() { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_new.xml"); CustomerDAO customerDAO=(CustomerDAO)ac.getBean("customerDAO"); Customer customer = new Customer(); customer.setBirthday(new Date()); customer.setName("peterandy"); customer.setPwd("ssssss"); customerDAO.save(customer); }?发出的SQL语句如下:

?到此为止,DAO层的重构就已经完成,里面参考了大量的Zhang Kaitao老师的代码,感谢他的无私奉献。
? ? ? ? 剩下的工作还有:
? ? ? ? 一、继续完善DAO层,增加对异常的处理等。
? ? ? ? 二、思考如何完成动态查询,即查询条件数量不定的查询。我先简单描述一下这个问题,请教一下大家:比如页面上有10个可供查询的条件,但是用户需要的数量是不确定的,可能有的用户只需要2个,有的可能得需要8个,这样一来,用不到的查询条件就得在输出的sql里转化成1=1才能保证程序的正确性。可是这个怎么具体实现呢,简单地拼接SQL字符串就怕被SQL注入,这个地方是我下面要继续完善的地方。
?
?
?
?
?
?
?
?
?
?
?
?
?
??
谢谢
有什么不对的地方还望指出哈,嘿嘿 好 好
呵呵,还望多多指教啊! ,稍后整理一下源码就上传。 我再改进一下。 ,抽空再继续优化一下,供大家分享。 谢谢指正for (int i = 0; i < paramlist.length; i++) { 可改为
for (int i = 0,length=paramList.length; i < length; i++) { for (int i = 0; i < paramlist.length; i++) { 可改为
for (int i = 0,length=paramList.length; i < length; i++) {
嗯嗯,谢谢指正
可以直接调用父类的getSession获取session。关于那个循环确实有点草率了,以前就吃过这样的亏,谢谢你又提醒了我~~
谢谢
有什么不对的地方还望指出哈,嘿嘿 好 好呵呵,还望多多指教啊! ,稍后整理一下源码就上传。 我再改进一下。 ,抽空再继续优化一下,供大家分享。 谢谢指正for (int i = 0; i < paramlist.length; i++) { 可改为
for (int i = 0,length=paramList.length; i < length; i++) { for (int i = 0; i < paramlist.length; i++) { 可改为for (int i = 0,length=paramList.length; i < length; i++) { 嗯嗯,谢谢指正
可以直接调用父类的getSession获取session。关于那个循环确实有点草率了,以前就吃过这样的亏,谢谢你又提醒了我~~