项目分享:通过使用SSH框架的公司-学员关系管理系统(CRM)
----------------------------------------------------------------------------------------------
[版权申明:本文系作者原创,转载请注明出处]
文章出处:http://blog.csdn.net/sdksdk0/article/details/52506671
作者:朱培 ID:sdksdk0 邮箱: zhupei@tianfang1314.cn
--------------------------------------------------------------------------------------------
最近在做的是一个通过Struts2、Hibernate、spring框架整合做的一个CRM系统,整体开发比较简单,就是细节的地方处理还是要花费一定的功夫,我主要负责的是人事管理(包括部门管理、职务管理、员工基本信息管理)、教学管理(班级管理、课程类别管理)、系统设置(修改密码、登录、退出)。整体功能比较简单,适合一般性开发。涉及的技术要点就是通过HQL来进行数据的增删改查、部门-职务级联、分页、通过struts进行文件上传下载等。只不过有个别的地方还是花费了几个小时。
首先是对整体的开发环境的搭建:
通过分模块开发来处理,框架的使用时分为了struts和spring,不同的模块的struts的命名也不同,spring也一样,同时hibernate放到相应的实体bean的文件夹下面。
在开发中我们都有很多地方用到了Dao和DaoImpl,所以我们可以将其抽取出来,作为一个基本的Dao,然后让其他用到的类去集成这个基础的dao即可,这样大大减少了开发的代码。
在BaseDao中我们可以写好常用的几种方法:
//通用dao接口 public interface BaseDao<T> { //保存 public void save(T t); //更新 public void update(T t); //删除 public void delete(T t); //保存或更新 public void SaveOrUpdate(T t); //查询所有 public List<T> findAll(); //条件查询 public List<T> findAll(String condition,Object... params); //离线查询 public List<T> findAll(DetachedCriteria detachedCriteria); //分页 public List<T> findAllByPage(int startIndex,int pageSize); //分页的总记录数 public int getTotalRecode(); //查找 T findById(Serializable serializable); }
然后我们需要一个DaoImpl去实现这个公共dao的方法:
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T>{ private Class daoImplClass; public BaseDaoImpl() { //运行时,通过反射获得 泛型信息的实际内容 /// * 运行时,this表示当前运行类(及子类的实例对象) //1 获得被参数化类型 ,例如:BaseDaoImpl<CrmPost> ParameterizedType paramType = (ParameterizedType) this.getClass().getGenericSuperclass(); //2 获得实例参数 daoImplClass = (Class) paramType.getActualTypeArguments()[0]; } @Override public void save(T t) { this.getHibernateTemplate().save(t); } @Override public void update(T t) { this.getHibernateTemplate().update(t); } @Override public void delete(T t) { this.getHibernateTemplate().delete(t); } @Override public void SaveOrUpdate(T t) { this.getHibernateTemplate().saveOrUpdate(t); } @Override public List<T> findAll() { return this.getHibernateTemplate().find("from " + daoImplClass.getName()); } @Override public List<T> findAll(String condition, Object... params) { String hql = "from " + daoImplClass.getName() + " where 1=1 " + condition; return this.getHibernateTemplate().find(hql,params); } public List<T> findAll(DetachedCriteria detachedCriteria) { return this.getHibernateTemplate().findByCriteria(detachedCriteria); } @Override public List<T> findAllByPage(int startIndex, int pageSize) { String hql = "from " + daoImplClass.getName(); return this.getHibernateTemplate().execute(new PageHibernateCallBack(hql, startIndex, pageSize)); } @Override public int getTotalRecode() { List<Long> list = this.getHibernateTemplate().find("select count(*) from " + daoImplClass.getName()); return list.get(0).intValue(); } @Override public T findById(Serializable oid) { List<T> allT=this.getHibernateTemplate().find(" from "+daoImplClass.getName()+" where id=? ",oid); if(allT !=null && allT.size()==1){ return allT.get(0); } return null; } /** * 通过编写回调实现分页 */ class PageHibernateCallBack implements HibernateCallback<List<T>> { private String hql; //查询hql语句 private Object[] params; //对应实际参数 private int firstResult; //分页开始索引 private int maxResults; //分页每页显示个数 public PageHibernateCallBack(String hql, int firstResult, int maxResults ,Object... params) { super(); this.hql = hql; this.params = params; this.firstResult = firstResult; this.maxResults = maxResults; } @Override public List<T> doInHibernate(Session session) throws HibernateException, SQLException { // 1 创建query Query queryObject = session.createQuery(hql); // 2 封装参数 if (params != null) { for (int i = 0; i < params.length; i++) { queryObject.setParameter(i, params[i]); } } // 3 分页 if (firstResult >= 0) { queryObject.setFirstResult(firstResult); } if (maxResults > 0) { queryObject.setMaxResults(maxResults); } //4 查询所有 return queryObject.list(); } } }
接下来我们就可以愉快的使用这些封装好的方法了
例如我们的员工信息管理模块中:
页面效果如下:
public interface LessontypeDao extends BaseDao<CrmLessontype>{ }
实现方法:
public class LessontypeDaoImpl extends BaseDaoImpl<CrmLessontype> implements LessontypeDao{ }
然后就是service进行处理:
public interface LessontypeService { PageBean<CrmLessontype> findAllPage(int pageNum,int pageSize); List<CrmLessontype> findAll(); CrmLessontype findById(String lessonTypeId); void addOrEditLessontype(CrmLessontype model); }
实现方法:
这里使用了一个分页查询的功能。
public class LessontypeServiceImpl implements LessontypeService { private LessontypeDao lessontypeDao; public void setLessontypeDao(LessontypeDao lessontypeDao) { this.lessontypeDao = lessontypeDao; } @Override public PageBean<CrmLessontype> findAllPage(int pageNum, int pageSize) { //1 查询数据库,获得总记录数 int totalRecord = lessontypeDao.getTotalRecode(); //2 分页数据 PageBean<CrmLessontype> pageBean = new PageBean<CrmLessontype>(pageNum, pageSize, totalRecord); //3 查询分页结果 pageBean.setData(lessontypeDao.findAllByPage(pageBean.getStartIndex(), pageSize)); return pageBean; } @Override public List<CrmLessontype> findAll() { return lessontypeDao.findAll(); } @Override public CrmLessontype findById(String lessonTypeId) { return lessontypeDao.findById(lessonTypeId); } @Override public void addOrEditLessontype(CrmLessontype model) { lessontypeDao.SaveOrUpdate(model); } }
接下来就需要把我们的service交由spring来管理了,在applicationContext-lessontype.xml中
<bean id="lessontypeService" class="cn.tf.lessontype.service.impl.LessontypeServiceImpl"> <property name="lessontypeDao" ref="lessontypeDao"></property> </bean> <bean id="lessontypeDao" class="cn.tf.lessontype.dao.impl.LessontypeDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean>
然后我们可以看到前台页面是:
<table width="97%" border="1" > <tr class="henglan" style="font-weight:bold;"> <td width="14%" align="center">名称</td> <td width="33%" align="center">简介</td> <td width="13%" align="center">总学时</td> <td width="18%" align="center">收费标准</td> <td width="11%" align="center">编辑</td> </tr> <s:iterator value="pageBean.data"> <tr class="tabtd1"> <td align="center"><s:property value="lessonName"/> </td> <td align="center"><s:property value="remark"/></td> <td align="center"><s:property value="total"/></td> <td align="center"><s:property value="lessonCost"/></td> <td width="11%" align="center"> <s:a namespace="/" action="lessontypeAction_addOrEditUI"> <s:param name="lessonTypeId" value="lessonTypeId"></s:param> <img src="${pageContext.request.contextPath}/images/button/modify.gif" class="img"/> </s:a> </td> </tr> </s:iterator> </table> <table border="0" cellspacing="0" cellpadding="0" align="center"> <tr> <td align="right"> <p:page url="${pageContext.request.contextPath}/lessontypeAction_findAll" data="${pageBean}" /> </td> </tr> </table>
所以这里我们可以使用的action。所以我们需要在struts中进行配置:
<package name="lessontype" namespace="/" extends="common"> <action name="lessontypeAction_*" class="cn.tf.lessontype.action.LessontypeAction" method="{1}"> <result name="findAll" >/WEB-INF/pages/lessontype/listLessontype.jsp</result> <result name="addOrEditUI">/WEB-INF/pages/lessontype/addOrEditCourseType.jsp</result> <result name="addOrEdit" type="redirectAction">lessontypeAction_findAll</result> </action> </package>
action要跳转的类,说到这里,我们还可以对action进行一些封装,毕竟使用action也是非常多的:
public class BaseAction<T> extends ActionSupport implements ModelDriven<T>{ public BaseAction(){ try { ParameterizedType parameterizedType=(ParameterizedType) this.getClass().getGenericSuperclass(); Class<T> crmClass=(Class<T>) parameterizedType.getActualTypeArguments()[0]; t=crmClass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private T t; @Override public T getModel() { return t; } //注入使用到service //分页数据 private int pageNum; private int pageSize=2; public void setPageNum(int pageNum) { this.pageNum = pageNum; } //简化值栈操作 public void set(String key,Object o){ ActionContext.getContext().getValueStack().set(key,o); } public void push(Object o){ ActionContext.getContext().getValueStack().push(o); } public void put(String key,Object value){ ActionContext.getContext().put(key,value); } public void putSession(String key,Object value){ ActionContext.getContext().getSession().put(key, value); } }
然后我们就来愉快的引用一下吧:
public class LessontypeAction extends BaseAction<CrmLessontype> { private CrmLessontype crmLessontype = new CrmLessontype(); @Override public CrmLessontype getModel() { return this.crmLessontype; } //2 service private LessontypeService lessontypeService; public void setLessontypeService(LessontypeService lessontypeService) { this.lessontypeService = lessontypeService; } //3 分页数据 private int pageNum; public void setPageNum(int pageNum) { this.pageNum = pageNum; } private int pageSize = 5 ; /** * 查询所有--分页 * @return */ public String findAll(){ PageBean<CrmLessontype> pageBean = this.lessontypeService.findAllPage(pageNum, pageSize); this.set("pageBean", pageBean); return "findAll"; } //打开添加或修改页面 public String addOrEditUI(){ CrmLessontype findLessontype=this.lessontypeService.findById(this.getModel().getLessonTypeId()); this.push(findLessontype); return "addOrEditUI"; } public String addOrEdit(){ this.lessontypeService.addOrEditLessontype(this.getModel()); return "addOrEdit"; } }
这样的话整个开发流程就完成了。接下来的就是很多类似的操作了。整体来说是很简单的,当然,虽然简单还是需要花费时间的哈!
例如我在修改员工信息整理的时候,就遇到了这个密码更新的问题。
我最开始是在这里把密码显示出来的,然后一更新,坏了,把加密后的数据又加密一次存进去了,因为我这里这个密码是通过md5加密处理了,所以回显出来也没有多大的意义,毕竟md5密码不能解密!既然不显示那么我密码肯定是没有修改吧,但是在hibernate中,使用SaveOrUpdate来更新,然后突然,啪的一下密码变成空了,我想坏了,不能这么干,因为如果这里不显示的话,这个员工的实体还是每个选项都要赋值的,所以我在最后面用了另外一个方法:使用bulkUpdate来操作就可以了。
这个地方的话我和修改用户密码一起组合起来写了,所以用了一个if ..else..
@Override public void update(CrmStaff crmStaff) { String staffCode=crmStaff.getStaffCode(); if(staffCode!=null){ //修改密码 String loginPwd=crmStaff.getLoginPwd(); String staffId=crmStaff.getStaffId(); String hql1="update CrmStaff c set c.loginPwd=? where c.staffId=?"; this.getHibernateTemplate().bulkUpdate(hql1,loginPwd,staffId); }else{ String loginName=crmStaff.getLoginName(); String staffName=crmStaff.getStaffName(); String gender=crmStaff.getGender(); String postId=crmStaff.getCrmPost().getPostId(); Date onDutyDate=crmStaff.getOnDutyDate(); String staffId=crmStaff.getStaffId(); String hql="update CrmStaff c set c.loginName=? ,c.staffName=?,c.gender=?,c.crmPost.postId=?, c.onDutyDate=? where c.staffId=? "; this.getHibernateTemplate().bulkUpdate(hql, loginName,staffName,gender,postId,onDutyDate,staffId); } }
哎,虽然麻烦了一些,好歹功能最后被我实现了,要是哪位小伙伴有更好的想法,欢迎留言交流哦!希望更懂的小伙伴们可以不吝赐教哦!项目源码我已经放到我的github中啦!
项目总结:这是一个非常好的SSH框架的项目,非常简单,整个过程更多的是需要细心和耐心,对于一些相似的功能如果想要复制黏贴的话千万要记得修改相应的地方,不然的话...就会”蹦,傻卡拉卡“ 系统就炸掉了,哈哈!还有就是通过js来打开'window.showModalDialog弹出模态窗口的时候貌似只兼容火狐浏览器。至于解决窗口的嵌套我们可以使用
if(top.location!= self.location){
top.location= self.location; //赋值成功之后,将马上跳转
}
来解决。好了,今天的项目分享就到这里啦!明天又是新的一天啦!
项目源码:https://github.com/sdksdk0/CRM