虚拟数据层 Struts2、Hibernate、Spring整合的泛型DAO Version 2010.9.27
Struts2、Hibernate、Spring整合的泛型DAO (本人评价: 代码开发效率提高30% 代码出错率减少70%)
对于大多数开发人员,系统中的每个 DAO 编写几乎相同的代码到目前为止已经成为一种习惯。虽然所有人都将这种重复标识为 “代码味道”,但我们大多数都已经学会忍受它。能不能不写重复的dao 呢 ?
泛型dao,顾名思义就是一个dao可以对多个实体对象进行持久化。当应用中需要使用到上十张表时,DAO的维护变得日益困难,主要表现在这几个方面:
1)dao类的繁多,很多设计都是一个entity对应一个dao (不同的只有类名和方法名)
2)dao接口需要维护的method庞大。
3)业务逻辑改变时,dao需要同时修改两个类文件(接口和实现类)
在本文中,我将为您展示如何避免再三地重复 DAO 代码。
在这里我建议项目最好使用一个共通的DAO,因为这样会为你省去非常多的类,而那些类里的逻辑往往差不多。当然是用共通的DAO你需要对结果转型,转成你需要的bean,但这也比写那么多DAO强多了,你可以放下包袱,只关注你的业务逻辑。
如果你真能只用一个dao解决,那么祝贺你,你得到了一个虚拟数据层(高度抽象的数据接口)。这是一个比dao更高级的存在。
欢迎大家指正 -_- 虚心求教
代码层次: bean-->dao-->service-->action
技术概述:1.继承
继承是利用现有的类创建新类的过程,现有的类称作基类(或父类),创建的新类称作派生类(子类)。继承其实就是自动地共享基类中成员属性和成员方法的机制。引入继承,实现了代码重用;
2.泛型 泛型类型的限定
3.反射
代码概述:
bean :Person.java 这个人员类我就不说了
泛型dao接口 :GenericDao<T, ID extends Serializable> 泛型作为DAO的通用接口 CRUD方法
dao接口 : PersonDAO extends GenericDao<Person, Integer> 可以不写代码,方法已经在父类泛型dao里了,这里为了说明:可扩展添加 findByNameExact()方法 子类的附加方法。
泛型daoimpl :GenericDaoImpl<T, ID extends Serializable> implements GenericDao<T, ID>
必须提供的构造方法,以便创建实例的时候就知道具体实体的类型。
daoimpl :PersonDAOImpl extends GenericDaoImpl<Person, Integer> implements PersonDAO
public PersonDAOImpl() {
super(Person.class);
} 告诉对哪个类操作,如不需要自定义扩展方法就作有一个构造方法。
泛型Service:GenericService.java 与泛型dao没有区别
Service :PersonService.java 直接继承。
泛型serviceimpl与serviceimpl实现和dao层实现一样。
Action : SavePersonAction直接调用PersonService。
下面是代码 为了演示减少代码量而且直观去掉些方法方便读者自己扩展写出适合自己的代码,这里我只抛砖引玉了。主要介绍这个技术。
bean
package test.s2sh.bean; import java.io.Serializable; @SuppressWarnings("serial") public class Person implements Serializable{ private Integer id; private String name; private int age; //set and get .... }
Person.hbm.xml 这个自己写比较简单。
GenericDao.java 泛型dao通用接口
package abu.****.dao; public interface GenericDao<T, ID extends Serializable> { /** * 在查找所有记录的时候,使用提供查询语句,查询匹配的记录,否则将使用默认的查询语句查询数据的所有记录. * * @param hql : 自定义的HQL语句 */ public void setHql(String hql); /** * * @return 自定义的HQL语句 */ public String getHql(); /** * 保存实体 * * @param entity : * 实体 * @return 保存后得到的id */ public ID save(T entity); /** * <p> * 删除实体 * </p> * * @param entity : * 实体 */ public void remove(T entity); /** * <p> * 删除实体集合 * </p> * * @param entities : * 实体 */ public void removeAll(Collection<T> entities); /** * <p> * 修改实体 * </p> * * @param entity : * 实体 */ public void modify(T entity); /** * <p> * 通过名字查找 * </p> * * @param id : * id * @return 找到的实体 */ public T findById(ID id); /** * <p/> * 查找全部实体 * <p/> * * @return 所有实体的列表 */ public List<T> findAll(); /** * <p> * 计算匹配查询条件的记录总数,如果没有注入或者设置hql语句,将使用默认的查询语句返回数据库中所有记录 * </p> * * @return 记录总数 */ public int getTotalRows(); /** * <p> * 根据每页记录的数量,计算出总的分页数 * </p> * * @param size 每页记录的数量 * @return 分页总数 */ public int getPageSize(int size); /** * <p/> * 根据给定的页码进行分页查找,这是纯Hibernate分页. * <p/> * * @param page : 要查询的页码 * * @param size : 每页记录数 * * @return 匹配的实体列表 */ public List<T> findByPage(final int page, final int size); }
GenericDaoImpl.java 泛型dao实现
package abu.****.dao.impl; public class GenericDaoImpl<T, ID extends Serializable> implements GenericDao<T, ID> { // 具体的实体类型 private Class<T> type; // Spring提供的Hibernate工具类 private HibernateTemplate hibernateTemplate; // 查询条件 private String hql; /** * <p> * 必须提供的构造方法,以便创建实例的时候就知道具体实体的类型 * <p> * * @param type : * 实体类型 */ public GenericDaoImpl(Class<T> type) { this.type = type; this.hql = "from " + type.getName(); } /** * <p> * 因为这个类没有继承HibernateDaoSupport,所以现在由Spring注入HibernateTemplate * </p> * * @param hibernateTemplate : * Spring提供的Hibernate工具类 */ public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } public void setHql(String hql) { this.hql = hql; } public HibernateTemplate getHibernateTemplate() { return hibernateTemplate; } public String getHql() { return hql; } @SuppressWarnings("unchecked") public List<T> findAll() { String hql = "from " + type.getName(); return (List<T>) hibernateTemplate.find(hql); } @SuppressWarnings("unchecked") public T findById(ID id) { return (T) hibernateTemplate.get(type, id); } public void modify(T entity) { hibernateTemplate.update(entity); } public void remove(T entity) { hibernateTemplate.delete(entity); } public void removeAll(Collection<T> entities) { hibernateTemplate.deleteAll(entities); } @SuppressWarnings("unchecked") public ID save(T entity) { return (ID) hibernateTemplate.save(entity); } public int getTotalRows() { String actualHql = "select count(*) " + hql.substring(hql.indexOf("from")); return ((Long) this.hibernateTemplate.find(actualHql).get(0)) .intValue(); } public int getPageSize(int size) { // 最大页数 int pageSize; // 实际每页数据条数 int actualSize; // 总记录数 int totalRows = this.getTotalRows(); // 计算实际每页的条数,如果请求的每页数据条数大于总条数, 则等于总条数 actualSize = (size > totalRows) ? totalRows : size; if (totalRows > 0) { pageSize = (totalRows % size == 0) ? (totalRows / actualSize) : (totalRows / actualSize + 1); } else { pageSize = 0; } return pageSize; } @SuppressWarnings("unchecked") public List<T> findByPage(final int page, final int size) { final int pageSize = this.getPageSize(size); final int totalRows = this.getTotalRows(); return hibernateTemplate.executeFind(new HibernateCallback() { public List<T> doInHibernate(Session session) throws HibernateException, SQLException { // 实际页码 int actualPage = (page > pageSize) ? pageSize : page; // 计算实际每页的条数,如果请求的每页数据条数大于总条数, 则等于总条数 int actualSize = (size > totalRows) ? totalRows : size; // 计算请求页码的第一条记录的索引值,如果 int startRow = (actualPage > 0) ? (actualPage - 1) * actualSize : 0; Query query = session.createQuery(hql); // 设置第一条记录 query.setFirstResult(startRow); query.setMaxResults(actualSize); return (List<T>) query.list(); } }); } }
PersonDao.java
package test.s2sh.dao; import abu.****.dao.GenericDao; import test.s2sh.bean.Person; public interface PersonDAO extends GenericDao<Person, Integer>{ /** * <p> * 根据用户名精确查找 * </p> * @param uname : 用户名 * @return : 匹配的实体 */ public Person findByNameExact(String uname); }
PersonDAOImpl.java
package test.s2sh.dao.impl; public class PersonDAOImpl extends GenericDaoImpl<Person, Integer> implements PersonDAO { public PersonDAOImpl() { super(Person.class); } @SuppressWarnings("unchecked") public Person findByNameExact(String uname) { List<Person> list = (List<Person>) this.getHibernateTemplate().find( "from Person u where u.name = ?", uname).get(0); return (!list.isEmpty() && list.size() == 1) ? null : list.get(0); } }
GenericService.java
package abu.****.service; public interface GenericService<T, ID extends Serializable> { /** * 保存实体 * * @param entity : * 实体 * @return 保存后得到的id */ public ID save(T entity); /** * <p> * 删除实体 * </p> * * @param entity : * 实体 */ public void remove(T entity); /** * <p> * 删除实体集合 * </p> * * @param entities : * 实体 */ public void removeAll(Collection<T> entities); /** * <p> * 修改实体 * </p> * * @param entity : * 实体 */ public void modify(T entity); /** * <p> * 通过名字查找 * </p> * * @param id : * id * @return 找到的实体 */ public T findById(ID id); /** * <p> * 查找全部实体 * <p> * * @return 所有实体的列表 */ public List<T> findAll(); /** * <p> * 根据给定的hql语句进行分页查找 * <p> * * @param page : * 要查询的页码 * @param size : * 每页记录条数 * @return 匹配的实体列表 */ public List<T> findByPage(final int page, final int size); /** * <p> * 计算匹配查询条件的记录总数,如果没有注入或者设置hql语句,将使用默认的查询语句返回数据库中所有记录 * </p> * * @return 记录总数 */ public int getTotalRows(); /** * <p> * 根据每页记录的数量,计算出总的分页数 * </p> * * @param size * 每页记录的数量 * @return 分页总数 */ public int getPageSize(int size); }
GenericServiceImpl.java
package abu.****.service.impl; public class GenericServiceImpl<T, ID extends Serializable> implements GenericService<T, ID> { private GenericDao<T,ID> genericDao; public List<T> findAll() { return genericDao.findAll(); } public T findById(ID id) { return genericDao.findById(id); } public List<T> findByPage(int page, int size) { return genericDao.findByPage(page, size); } public int getPageSize(int size) { return genericDao.getPageSize(size); } public int getTotalRows() { return genericDao.getTotalRows(); } public void modify(T entity) { genericDao.modify(entity); } public void remove(T entity) { genericDao.remove(entity); } public void removeAll(Collection<T> entities) { genericDao.removeAll(entities); } public ID save(T entity) { return genericDao.save(entity); } public void setGenericDao(GenericDao<T, ID> genericDao) { this.genericDao = genericDao; } }
PersonService.java
package test.s2sh.service; import abu.****.service.GenericService; import test.s2sh.bean.Person; public interface PersonService extends GenericService<Person, Integer>{ }
PersonServiceImpl.java
package test.s2sh.service.impl; public class PersonServiceImpl extends GenericServiceImpl<Person, Integer> implements PersonService { }
SavePersonAction.java
package test.s2sh.action.person; import test.s2sh.bean.Person; import test.s2sh.service.PersonService; import com.opensymphony.xwork2.ActionSupport; // @SuppressWarnings("serial") public class SavePersonAction extends ActionSupport { private Person p; private PersonService service; public Person getP() { return p; } public void setP(Person p) { this.p = p; } public PersonService getService() { return service; } public void setService(PersonService service) { this.service = service; } @SuppressWarnings("static-access") public String execute() throws Exception { this.service.save(p); return this.SUCCESS; } public void validate() { if(p.getName()==null||"".equals(p.getName())){ this.addFieldError("p.name", "name is not null"); } } }
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 设置隔离层次,控制事务的并发,缺省时为Read Committed: 2 --> <property name="connection.isolation">2</property> <!-- 配置事务实现类 --> <property name="transaction.factory_class"> org.hibernate.transaction.JDBCTransactionFactory </property> <!-- 配置Jdbc里Batch的大小 --> <property name="jdbc.batch_size">50</property> <property name="cache.use_second_level_cache">false</property> <!-- 配置线程安全的session --> <property name="current_session_context_class">thread</property> <!-- 显示SQL --> <property name="show_sql">true</property> <property name="format_sql">true</property> <!-- 配置数据库方言 --> <property name="dialect"> org.hibernate.dialect.SQLServerDialect </property> <!-- 配置数据库连接 --> <property name="connection.driver_class"> com.microsoft.jdbc.sqlserver.SQLServerDriver </property> <property name="connection.username">sa</property> <property name="connection.password"></property> <property name="connection.url"> jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=softsea08;SelectMethod=Cursor </property> <!-- 配置连接池 --> <property name="c3p0.max_size">10</property> <property name="c3p0.min_size">1</property> <property name="c3p0.timeout">5000</property> <property name="c3p0.max_statements">100</property> <property name="c3p0.idle_test_period">3000</property> <property name="c3p0.acquire_increment">1</property> <property name="c3p0.validate">false</property> <!-- 指定hibernate管理的映射文件 <mapping resource="test/s2sh/bean/Person.hbm.xml" />--> <mapping resource="test/s2sh/bean/Person.hbm.xml" /> </session-factory> </hibernate-configuration>
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <constant name="struts.i18n.encoding" value="UTF-8" /> <constant name="struts.custom.i18n.resources" value="message"></constant> <constant name="struts.ui.theme" value="simple" /> <constant name="struts.multipart.saveDir" value="c:\"></constant> <constant name="struts.multipart.maxSize" value="2097152000" /> <package name="s2sh" extends="struts-default"> <action name="savePerson" class="savePersonAction"> <result name="success" type="redirect">/Save_success.jsp</result> <result name="input">/Save.jsp</result> </action> </package> </struts>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" /> <tx:method name="find*" read-only="true" /> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="job1" expression="execution(* abu.****.service.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="job1" /> </aop:config> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <bean id="personDAO" class="test.s2sh.dao.impl.PersonDAOImpl" scope="prototype"> <property name="hibernateTemplate" ref="hibernateTemplate"></property> </bean> <bean id="personService" class="test.s2sh.service.impl.PersonServiceImpl" scope="prototype"> <property name="genericDao" ref="personDAO" ></property> </bean> <bean id="savePersonAction" class="test.s2sh.action.person.SavePersonAction" scope="prototype"> <property name="service" ref="personService"></property> </bean> </beans>
Save.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Save Person</title> </head> <body> <s:fielderror></s:fielderror> <s:form action="savePerson"> <s:textfield name="p.name" label="name"></s:textfield> <s:textfield name="p.age" label="age"></s:textfield> <s:submit></s:submit> </s:form> </body> </html>
Save_success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Save OK</title> </head> <body> 保存成功 <br> </body> </html>
在 Java 5 之前,该语言不支持编写既类型安全又 泛型的代码,您必须只能选择其中之一。在本文中,您已经看到一个结合使用 Java 5 泛型与 Spring 和 Hibernate(以及 AOP)等工具来提高生产率的示例。泛型类型安全 DAO 类相当容易编写 —— 您只需要单个接口、一些命名查询和为 Spring 配置添加的 10 行代码 —— 而且可以极大地减少错误并节省时间。
几乎本文的所有代码都是可重用的。尽管您的 DAO 类可能包含此处没有实现的查询和操作类型(比如,批操作),但使用我所展示的技术,您至少应该能够实现其中的一部分。
需要完整代码的 可以在下面评论留下 电子邮箱 .现在代码上传了 可以点下面链接下载该代码
http://download.****.net/source/2711051
传承IT精华,为中国的IT事业做点儿小贡献