这是一个物流管理系统,项目是由maven构建的,用到了ssh框架,前台页面是由jquery easyUi来完成的,它使页面实现了静态化,因为大部分请求都是后台返回前台json字符串,前台根据这些json字符串进行页面处理,
项目底层代码构建
因为dao层代码有很多都是重复的,所以我们对他们进行了提取,用到了反射和泛型
接口IBasDao.java
package com.yinhe.bos.dao;
import java.io.Serializable;
import java.util.List;
import com.yinhe.bos.utils.PageUtils;
public interface IBaseDao<T> {
//增
public void save(T javabean);
public void saveOrUpdate(T javabean);
//删
public void del(T entity);
//改
public void update(T entity);
//根据id查询
public T get(Serializable id);
//查询所有数据
public List<T> find();
/**
*
* @param queryname
* @param objects
*/
public void executeUpdate(String queryname,Object...objects);
//分页查询
public void findByPage(PageUtils pageUtils);
}
实现类BaseDaoImpl.java
package com.yinhe.bos.dao.impl;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import javax.annotation.Resource;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import com.yinhe.bos.dao.IBaseDao;
import com.yinhe.bos.utils.PageUtils;
public class BaseDaoImpl<T> extends HibernateDaoSupport implements IBaseDao<T> {
private Class<T> clazz;
//在tomcat启动加载spring容器时调用
//早构造方法中通过反射获取clazz对象
public IBaseDaoImpl() {
// TODO Auto-generated constructor stub
//获取父类对应的class对象
//this代表继承的子类对象
ParameterizedType superClazz=(ParameterizedType)this.getClass().getGenericSuperclass();
//获取父类所有的泛型类数组
Type[] type=superClazz.getActualTypeArguments();
clazz=(Class<T>) type[0];
}
@Resource
public void initHibernateT(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
//this.setSessionFactory(sessionFactory);
}
@Override
public void save(T javabean) {
// TODO Auto-generated method stub
this.getHibernateTemplate().save(javabean);
}
@Override
public void saveOrUpdate(T javabean) {
// TODO Auto-generated method stub
this.getHibernateTemplate().saveOrUpdate(javabean);
}
public void del(T entity){
}
public void update(T entity){
this.getHibernateTemplate().update(entity);
}
public T get(Serializable id){
return this.getHibernateTemplate().get(clazz, id);
}
public List<T> find(){
return (List<T>) this.getHibernateTemplate().find("from "+clazz.getSimpleName());
}
public void executeUpdate(String queryname,Object...objects){
Session session=this.getSessionFactory().getCurrentSession();
Query query=session.getNamedQuery(queryname);
for (int i = 0; i < objects.length; i++) {
query.setParameter(i, objects[i]);
}
query.executeUpdate();
}
public void findByPage(PageUtils pageUtils){
DetachedCriteria criteria=pageUtils.getCriteria();
List list=this.getHibernateTemplate().findByCriteria(criteria.setProjection(Projections.count("id")));
pageUtils.setTotal(((Long)list.get(0)).intValue());
criteria.setProjection(null);
criteria.setResultTransformer(Criteria.ROOT_ENTITY);
pageUtils.setRows(this.getHibernateTemplate().findByCriteria(criteria,pageUtils.getStart(),pageUtils.getPageSize()));
}
}
其他dao层接口实现BaseDao接口,dao层实现类继承BaseDaoImpl类就可以,省下了很多代码。
action层的抽取
BaseAction.java
package com.yinhe.bos.web.controller;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import org.hibernate.criterion.DetachedCriteria;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.yinhe.bos.domain.Staff;
import com.yinhe.bos.utils.PageUtils;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
public class BaseAction<T> extends ActionSupport implements ModelDriven<T>{
//提取分页查询
protected int page;
protected int rows;
protected PageUtils pageUtils=new PageUtils();
protected T model;
@Override
public T getModel() {
// TODO Auto-generated method stub
return model;
}
public BaseAction() {
// TODO Auto-generated constructor stub
ParameterizedType superClazz=(ParameterizedType)this.getClass().getGenericSuperclass();
//获得BaseAction上声明的泛型数组
Type[] types=superClazz.getActualTypeArguments();
Class<T> clazz=(Class<T>) types[0];
pageUtils.setCriteria(DetachedCriteria.forClass(clazz));
try {
model=clazz.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void objectJson(Object o,String[]strs) throws IOException{
JsonConfig jsonConfig=new JsonConfig();
jsonConfig.setExcludes(strs);
ServletActionContext.getResponse().setContentType("text/json;charset=utf-8");
//为什么关闭懒加载
/*
* jsonConfig排除只是给当前对象o设置的,转换json的时候,subarea里的region要重新从数据库里查,而查出来的region里又有
* subarea,这个是没有设置排除的,所以会无限循环
*/
String a=JSONObject.fromObject(o, jsonConfig).toString();
/* JSONObject jo=JSONObject.fromObject(pageUtils);
String a=jo.toString();*/
ServletActionContext.getResponse().getWriter().print(a);
}
protected void arrayJson(List list,String[]strs) throws IOException{
JsonConfig jsonConfig=new JsonConfig();
jsonConfig.setExcludes(strs);
ServletActionContext.getResponse().setContentType("text/json;charset=utf-8");
String a=JSONArray.fromObject(list, jsonConfig).toString();
/* JSONObject jo=JSONObject.fromObject(pageUtils);
String a=jo.toString();*/
ServletActionContext.getResponse().getWriter().print(a);
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
pageUtils.setPageSize(rows);
pageUtils.setStart((page-1)*rows);
}
}
其他action继承BaseAction类即可,这样就不用再每个action类里创建一个model对象了
一,登录
用ajax发送请求,根据后台返回数据来判断用户名密码是否正确,整个项目用到了登录验证拦截器,struts2实现拦截器的方式有三种,1,实现Interceptor接口;2,继承AbstractInterceptor类;3,继承MethodFilterInterceptor类,这里我们用第三种,它有一个特点就是可以排除对action中指定方法的拦截或只拦截指定额方法,
<!-- 配置拦截器 -->
<interceptors>
<!-- 配置自定义拦截器 -->
<interceptor name="myInterceptor" class="com.caokaiyuan.interceptor.MyInterceptor"></interceptor>
<!-- 配置拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/><!-- 加载默认拦截器 -->
<!-- 加载自定义拦截器 -->
<interceptor-ref name="myInterceptor">
<!-- 配置要拦截的方法;user,client为方法名 -->
<param name="includeMethods">user,client</param>
<!-- 配置不需要拦截的方法;login为方法名 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
登录成功后来到主页,如下图
主页分为上中左下四个区域,是由easyUI里的layout页面布局实现的,左边部分是个菜单树,由jquery ztree插件来完成的,
二,取派员设置
实现了增删改和分页查询,增加用到了easyUi中的validatebox校验,只有当数据全部输入正确后才可以提交,
分页查询数据显示用到了easyUi中的datagrid,它类似于一个表格,但功能很强大,通过发送ajax请求获取json数据创建datagrid
// 取派员信息表格
$('#grid').datagrid( {
iconCls : 'icon-forward',
fit : true,
border : false,
rownumbers : true,
striped : true,
pageList: [5,20,30],
pagination : true,
toolbar : toolbar,
url : "${pageContext.request.contextPath }/staffAction_select.action",
idField : 'id',
columns : columns,
onDblClickRow : doDblClickRow
});
对分页查询所需数据进行封装
PageUtil.java
package com.yinhe.bos.utils;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import com.yinhe.bos.domain.Staff;
public class PageUtils {
private int total;
private List rows;
private int start;
private int pageSize;
private DetachedCriteria criteria;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List getRows() {
return rows;
}
public void setRows(List rows) {
this.rows = rows;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public DetachedCriteria getCriteria() {
return criteria;
}
public void setCriteria(DetachedCriteria criteria) {
this.criteria = criteria;
}
}
之后根据前台所发请求和参数,进行查询,返回相应的json数据即可
修改,首先要进行数据回显,一般回显,我们要去数据库查询,然后根据查询到的数据进行回显,然而我们用了datagrid,它可以帮我们进行数据的回显
回显后,修改数据交给后台处理即可。
删除其实是假删除,在数据库生成一个字段,值为1表示当前取派员存在,为0则表示不存在,即删除。
三,区域设置,实现功能有将excel表导入数据库和分页查询,
导入用到了apache POI技术和pinyin4j,POI上篇博客有介绍,这里不再多说,excel表里只有省市区邮箱,而没有简码和城市编码,(简码就是市和区的拼音首字母,城市简码是市的拼音),所以这里就用到了pinyin4j,可以进行中文字符和拼音之间的转换。拼音输出格式可以定制。分页查询和上面取派员分页查询思路一样。
四,管理分区
实现了查询,增加和导出,区域和分区是一对多的关系,
条件查询
第一步:提供一个工具方法,可以将指定的form表单中所有的输入项转为json数据,用于参数提交
//定义一个工具方法,用于将指定的form表单中所有的输入项转为json数据{key:value,key:value}
$.fn.serializeJson=function(){
var serializeObj={};
var array=this.serializeArray();
$(array).each(function(){
if(serializeObj[this.name]){
if($.isArray(serializeObj[this.name])){
serializeObj[this.name].push(this.value);
}else{
serializeObj[this.name]=[serializeObj[this.name],this.value];
}
}else{
serializeObj[this.name]=this.value;
}
});
return serializeObj;
第二步:为查询窗口中查询按钮绑定事件
$("#btn").click(function(){
//将指定的form表单中所有的输入项转为json数据{key:value,key:value}
var p = $("#searchForm").serializeJson();
console.info(p);
//调用数据表格的load方法,重新发送一次ajax请求,并且提交参数
$("#grid").datagrid("load",p);
//关闭查询窗口
$("#searchWindow").window("close");
});
根据前台传递的参数,后台action中添加对应的模糊查询字段即可,因为这是qbc多表查询,所以要给表取别名,查询完后发现返回的数据格式有问题,这是因为
实体间有定义关联关系的(一对多,多对一等)
对已定义关联关系的,我们可以使用createAlias()来创建属性的别名,然后引用别名进行条件查询,如:
Criteria criteria = session.createCriteria(Parent.class);
criteria.createAlias("children", "c").add(Expression.eq( "c.name" , childName));
这样进行查询时就可得到关联查询后的Parent对象结果集。
要注意的是,如果在createAlias()之后,又使用了setProjection(如查询总记录数),这样得到的查询结果集中,每个对象并不是Parent类型,而是一个对象数组(Object[]),里边有一个Parent对象和一个Child对象,需要根据object的类名去判断是哪个对象。
如果还要恢复默认的结果集状态,需调用
criteria.setResultTransformer(Criteria.ROOT_ENTITY);
这样返回的数据就没有问题了,
数据导出也是用到了POI技术,上几遍博客有具体介绍
最后一个功能是添加分区,
用到了easyUi里的下拉框,
<input class="easyui-combobox" name="region.id" data-options="valueField:'id',textField:'name',url:'${pageContext.request.contextPath }/regionAction_find',mode:'remote'" />
界面加载完,去请求input里的url,通过返回json字符串来进行下拉框内数据的显示,当用户在下拉框进行输入后,也会向后台请求这个url,并且发送一个参数q,就是用户输入的内容,我们就用这个来进行模糊查询,重新返回json字符串,实现搜索功能
用户将数据填写完整后,点击保存发送请求,后台进行数据库插入操作。
注意:如果连个对象有关联,那么对象转换成json字符串就会出现死循环问题,解决方法:
1、页面不需要展示关联数据时
解决:将关联对象属性排除掉
2、页面需要展示关联数据时
解决:将关联对象改为立即加载,并且将关联对象中的属性排除
功能没有实现完,但收获依然很大,以上就是主要功能简述,记录下来以便以后回顾。