XXX系统开发小结(SSH+Jquery EasyUI)

一、项目总体介绍

前一段时间的工作中,笔者大概用了两三个月开发了一个Web管理信息系统,使用的框架集为Struts2.3.1+Spring3.0+Hibernate3+Jquery EasyUI1.3.5,系统业务逻辑并不复杂,完成数据的采集(以问卷的形式)、计算处理和形成报告发布。EasyUI是一个很优秀的JS UI框架,使用简单方便,效果也还可以,是UI效果和带宽速度之间的一个折中之选。系统中还有新闻发布模块,用到了富文本编辑器,在比较了很多插件之后,选择了kindeditor,原因很简单,因为它有网上现成的和EasyUI结合的例子。另外生产图表时用到了JFreechart插件。系统开发运行环境为windows7下Myeclipse8.5+JDK1.6+Tomcat6.0+SqlServer2008R2。

二、关于EasyUI

EasyUI很方便使用,一般只需要在一个页面中导入一些文件之后,其他的页面都是嵌在当前页买中。EasyUI提供了很多组件,如layout、panel、tabs、datagrid等,只需要初始化这些组件,并提供相应的数据即可。每个组件都有两种方式声明或者创建,即使用标记元素的形式和使用js的形式。具体引用参见项目源码或者EasyUI官网提供的API。

EasyUI官网:http://www.jeasyui.com/index.php

EasyUI官方API:http://www.jeasyui.com/documentation/index.php

三、关于系统架构

系统应用了常规的“三层架构”,即视图展示层、业务逻辑层和数据处理层。其中EasyUI+Struts2的Action负责页面展示、数据抓取和推送、视图流程控制,Service层负责业务逻辑的实现,Dao层与Hibernate结合,负责数据的处理,与数据库打交道。而Spring则扮演一个管家的角色,其功能有IOC容器、依赖注入、集成其他插件等,负责将整个系统融合起来,合理调度。

下面对Action、Service、Dao的设计简单小结一下。其基本的流程是Action调用Service,Service调用Dao,且各层都使用Spring的依赖注入来实例化各层属性。

1、  Action层

Action负责接收前台提交的请求,获取请求数据,然后调用Service完成业务处理,然后将请求转发到下一流程(可能返回到某个页面或者另一个Action),并将相应的数据随之一并转发。

本项目中首先写了一个BaseAction,继承了Struts2的ActionSupport。因为EasyUI接收的数据格式都是json格式,所以这里用到了一个json插件fastjson并且提供了一个writeJSON(Object o)方法,用来在需要的时候,将数据写到前台页面,供EasyUI的组件读取。

其他的Action只需继承此Action即可。项目中获取请求参数时采用了模型驱动的方式,即Action实现ModelDriven接口,该接口接受一个泛型T,这里T一般是一个数据模型类,这样前台就可以直接写某个类的属性,后台便能抓取到它们并自动封装到一个对象中。

项目中异常处理在Action层中完成,Service和Dao只负责捕捉,并往上一层抛,在Action才处理。这也并不是绝对的,看具体的要求。

2、  Service

Service层完成具体的业务逻辑。如根据不同的条件调用不同的Dao等。

3、Dao

Dao结合Hibernate与具体的数据库打交道,即完成数据的持久化,或者加载持久化数据对象。这里Dao的设计采用了泛型和接口的结合,首先写了一个带泛型的公用接口BaseDaoI<T>,接口中定义了一些常用的,通用的方法,这些方法接受的或者返回的类型都是泛型。然后实现了该接口,其泛型信息依然保留。这样一来,具体的和某一表相关联的Dao,只需在编写接口时继承该通用接口,并将泛型替换为实体类型,然后编写实现时去实现刚才的接口,并继承通用接口的实现,即可。这样的好处是,通用的功能,都在公用接口中定义了,而且利用泛型,完成了类型的良好过度和检查,而不必用Object来强制转换。

举个例子,以用户实体相关操作为例。

(1)、公用接口

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * @author Administrator
 * 
 * @param <T>
 */
public interface BaseDaoI<T> {
	/**
	 * 保存实体
	 * 
	 * @param o
	 * @throws Exception
	 */
	void save(T o) throws Exception;

	/**
	 * 删除实体
	 * 
	 * @param o
	 * @throws Exception
	 */
	void delete(T o) throws Exception;
	/**
	 * 以sql形式删除数据
	 * @param sql 删除sql
	 * @throws Exception
	 */
	void delete(String sql) throws Exception;

	/**
	 * 更新实体
	 * 
	 * @param o
	 * @throws Exception
	 */
	void update(T o) throws Exception;

	/**
	 * 保存或更新
	 * 
	 * @param o
	 * @throws Exception
	 */
	void saveOrUpdate(T o) throws Exception;

	/**
	 * @param c
	 * @param id
	 * @return
	 * @throws Exception
	 */
	T get(Class<T> c,Serializable id)throws Exception;
	/**
	 * 不带参数的hql查询
	 * 
	 * @param hql
	 *            完整的hql语句
	 * @return 结果集合
	 * @throws Exception
	 */
	List<T> get(String hql) throws Exception;

	/**
	 * 带参数的hql查询,该方式使用问号占位符,并按顺序传递参数(不建议使用)
	 * 
	 * @param hql
	 *            带参数的hql语句
	 * @param params
	 *            顺序良好的参数列表
	 * @return 结果集合
	 * @throws Exception
	 */
	List<T> get(String hql, Object[] params) throws Exception;

	/**
	 * 带参数的hql查询,该方式使用【:参数名】占位符,并使用Map传递参数(建议使用)
	 * 
	 * @param hql
	 *            带参数的hql语句
	 * @param params
	 *            Map参数列表
	 * @return 结果集合
	 * @throws Exception
	 */
	List<T> get(String hql, Map<String, Object> params) throws Exception;

	/**
	 * 带参数的分页查询语句,可查询指定条数的记录
	 * 
	 * @param hql
	 *            查询hql
	 * @param params
	 *            参数列表
	 * @param starRow
	 *            查询起始行(Hibernate行数从0开始)
	 * @param size
	 *            查询数量
	 * @return 结果集合
	 * @throws Exception
	 */
	List<T> get(String hql, Map<String, Object> params, int starRow, int size)
			throws Exception;

	/**
	 * 不带参数的分页查询语句,可查询指定条数的记录
	 * 
	 * @param hql
	 *            查询hql
	 * @param starRow
	 *            查询起始行(Hibernate行数从0开始)
	 * @param size
	 *            查询数量
	 * @return 结果集合
	 * @throws Exception
	 */
	List<T> get(String hql, int starRow, int size) throws Exception;

	/**
	 * 不带参数查询记录总数
	 * @param hql 查询hql(例如"select count(*) from XXXX") 
	 * @return 记录总数
	 * @throws Exception
	 */
	Long getTotalAmount(String hql) throws Exception;

	/**
	 * 带参数查询记录总数
	 * @param hql 查询hql (例如"select count(*) from XXXX where ...")
	 * @param params 参数列表
	 * @return 记录总数
	 * @throws Exception
	 */
	Long getTotalAmount(String hql, Map<String, Object> params) throws Exception;
	/**
	 * 执行特定的hql语句
	 * @param hql
	 * @return
	 * @throws Exception
	 */
	int executeHql(String hql)throws Exception;
}

(2)、公用接口的实现

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.pmi.dao.BaseDaoI;

public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDaoI<T> {

	public void save(T o) throws Exception {
		getHibernateTemplate().save(o);
	}

	public List<T> get(String hql) throws Exception {
		List<T> list = getHibernateTemplate().find(hql);
		return list;
	}

	public List<T> get(String hql, Object[] params) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		Query q = session.createQuery(hql);
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				q.setParameter(i, params[i]);
			}
		}
		List<T> list = q.list();
		return list;
	}

	public List<T> get(String hql, Map<String, Object> params) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		Query q = session.createQuery(hql);
		for (String key : params.keySet()) {
			q.setParameter(key, params.get(key));
		}
		List<T> list = q.list();
		return list;
	}

	public void delete(T o) throws Exception {
		getHibernateTemplate().delete(o);
	}

	public void saveOrUpdate(T o) throws Exception {
		getHibernateTemplate().saveOrUpdate(o);
	}

	public void update(T o) throws Exception {
		getHibernateTemplate().update(o);
	}

	public List<T> get(String hql, Map<String, Object> params, int starRow,
			int size) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		Query q = session.createQuery(hql);
		for (String key : params.keySet()) {
			q.setParameter(key, params.get(key));
		}
		q.setFirstResult(starRow);
		q.setMaxResults(size);
		List<T> list = q.list();
		return list;
	}

	public List<T> get(String hql, int starRow, int size) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		Query q = session.createQuery(hql);
		q.setFirstResult(starRow);
		q.setMaxResults(size);
		List<T> list = q.list();
		return list;
	}

	public Long getTotalAmount(String hql) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		Query q = session.createQuery(hql);
		Long count = (Long) q.uniqueResult();
		return count;
	}

	public Long getTotalAmount(String hql, Map<String, Object> params)
			throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		Query q = session.createQuery(hql);
		for (String key : params.keySet()) {
			q.setParameter(key, params.get(key));
		}
		Long count = (Long) q.uniqueResult();
		return count;
	}

	public void delete(String sql) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
				.getCurrentSession();
		SQLQuery q = session.createSQLQuery(sql);
		q.executeUpdate();
	}

	public T get(Class<T> c, Serializable id) throws Exception {
		return getHibernateTemplate().get(c, id);
	}

	public int executeHql(String hql) throws Exception {
		Session session = getHibernateTemplate().getSessionFactory()
			.getCurrentSession();
		Query q = session.createQuery(hql);
		int i = q.executeUpdate();
		return i;
	}

}

(3)、User实体接口

import com.pmi.model.database.TbYhxx;

public interface UserDaoI extends BaseDaoI<TbYhxx> {

}

(4)、User实体接口实现

</pre><pre name="code" class="java">import com.pmi.dao.UserDaoI;
import com.pmi.model.database.TbYhxx;

public class UserDaoImpl extends BaseDaoImpl<TbYhxx> implements UserDaoI {
	……(独有的方法)
}

4、权限控制

设计思路:项目采用基于角色的权限控制,即用户的权限是以角色来区分的。用户权限包括菜单资源权限、增删改查操作权限。用户登录时,系统根据用户的角色,加载用户所具有的菜单资源和操作权限,匹配的操作才能被完成。

菜单资源权限的实现:这个逻辑比较简单,为角色分配的菜单存放在数据表中,用户登录时,根据角色加载菜单项即可。

操作权限的实现:操作权限是指具体的增删改查操作,比如查看新闻、添加新闻、删除用户等操作。这里采用Struts2的拦截器实现操作权限控制,针对Action层过滤。拦截到请求的action和方法之后,首先判断需不需要进行权限检查,因为不是所有的操作都需要权限控制,比如查看新闻。默认所有的aciton请求都需要检查,如果有例外,通过struts.xml中通过ignoreActions配置。检查流程为,先检查是否登录,如果未登录,阻止请求,并给出提示,若已登录,则检查当前请求的操作权限与用户所具有的是否匹配,匹配则放行,否则阻止请求,给出提示。

5、实体模型

一般在SSH应用中,一个数据表对应一个实体模型,这个Model既完成持久化的相关工作,也是往视图层传递的数据载体,这样比较简单,但同时也带来一定的弊端,比如前台页面中,除了需要该Model对应的数据表中的字段之外,还需要一些额外的参数(分页参数、自定义其他参数)作为属性,又比如在使用Hibernate映射时,一般外键会映射成持有外键表对应Model的对象,而在前台页面,可能并不需要这个对象的所有信息而只是需要一些特定的属性值,这时如果都写在一个Model时,在完成持久化时比较不清晰,对于开发维护人员来说可能会造成逻辑混乱,这里采用的是双Model机制,即一张表对应两个Model实体,一个是完全的数据表字段作为属性,另一个则是与之对应的专门与视图层关联的Model复制体,而这个复制体可能有更多的视图层需要的信息,同时在Service层完成这两个Model的数据复制,利用Spring的BeanUtils类提供的静态copyProperties()方法。以用户信息表为例,用户信息表有如下字段

Model1:Hibernate反向映射自动生成的

public class TbYhxx implements java.io.Serializable {

	// Fields

	private Integer yhId;
	private TbDyxx tbDyxx;
	private TbYbxx tbYbxx;
	private TbJsxx tbJsxx;
	private String yhName;
	private String yhPwd;
	private String yhXm;
	private String phone;
	private String email;
	private Integer yhState;
	private Integer extend1;
	private String extend2;
	private String extend3;
	private Set tbKzwjtbs = new HashSet(0);
	private Set tbWjtbs = new HashSet(0);
	private Set tbGgxxes = new HashSet(0);
	private Set tbFxbgs = new HashSet(0);
	private Set tbYbkzs = new HashSet(0);
	private Set tbTsxxes = new HashSet(0);
	private Set tbTjxxes = new HashSet(0);
	private Set tbZhzses = new HashSet(0);
	private Set tbXwggs = new HashSet(0);
	private Set tbTjjgs = new HashSet(0);

	......setters and getters
}

Model2:与视图层关联的,前台页面相关联的

public class User extends BaseModel {
	// Fields
	// datagrid需要的字段名
	private Integer yhId;
	private Integer dyId;
	private String dyName;
	private Integer qyId;
	private String qyName;
	private Integer jsId;
	private String jsName;
	private String yhName;
	private String yhPwd;
	//用户修改密码信息时使用
	private String newPwd;
	private String yhXm;
	private String phone;
	private String email;
	private Integer yhState;
	private Integer extend1;
	private String extend2;
	private String extend3;

	........setters and getters
}

从例子中可以看出,这两个Model还是有一定区别的,且是有实际意义的。

6、日志记录

日志信息可以检测系统的日常运营信息,如用户操作和异常或者错误场景,一个设计良好的管理信息系统应该具有日志记录功能。本系统采用的是常用的log4j插件作为日志工具。log4j使用相对简单,导入jar包后,增加一个配置文件即可。本项目的配置见下文。

log4j.properties:

log4j.rootCategory=INFO, stdout , R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[PMI] %p [%d] %C.%M(%L) | %m%n

log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=D\:/pmi_log/pmi_daily.log
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=[PMI] %p \: %m - %c | %t | %d %n



XXX系统开发小结(SSH+Jquery EasyUI),古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。