tags: 纳税服务系统项目
需求分析
我们现在来到了纳税服务系统的信息发布管理模块,首先我们跟着原型图来进行需求分析把:
一些普通的CRUD,值得一做的就是状态之间的切换了。停用和发布切换。
值得注意的是:在信息内容中,它可以带格式地复制内容,然后上传到我们的服务器中。
流程图:
package zhongfucheng.info.entity;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
public class Info implements java.io.Serializable {
private String infoId;
private String type;
private String source;
private String title;
private String content;
private String memo;
private String creator;
private Timestamp createTime;
private String state;
public static String INFO_STATE_PUBLIC = "1";//发布
public static String INFO_STATE_STOP = "0";//停用
public static String INFO_TYPE_TZGG = "tzgg";
public static String INFO_TYPE_ZCSD = "zcsd";
public static String INFO_TYPE_NSZD = "nszd";
public static Map<String, String> INFO_TYPE_MAP = new HashMap<String, String>();
static {
INFO_TYPE_MAP.put(INFO_TYPE_TZGG, "通知公告");
INFO_TYPE_MAP.put(INFO_TYPE_ZCSD, "政策速递");
INFO_TYPE_MAP.put(INFO_TYPE_NSZD, "纳税指导");
}
public Info() {
}
public Info(String title) {
this.title = title;
}
public Info(String type, String source, String title, String content, String memo, String creator, Timestamp createTime, String state) {
this.type = type;
this.source = source;
this.title = title;
this.content = content;
this.memo = memo;
this.creator = creator;
this.createTime = createTime;
this.state = state;
}
public String getInfoId() {
return this.infoId;
}
public void setInfoId(String infoId) {
this.infoId = infoId;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public String getSource() {
return this.source;
}
public void setSource(String source) {
this.source = source;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
public String getMemo() {
return this.memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public String getCreator() {
return this.creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Timestamp getCreateTime() {
return this.createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
}
配置文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="zhongfucheng.info.entity.Info" table="info">
<id name="infoId" type="java.lang.String">
<column name="info_id" length="32"/>
<generator class="uuid.hex" />
</id>
<property name="type" type="java.lang.String">
<column name="type" length="10" />
</property>
<property name="source" type="java.lang.String">
<column name="source" length="50" />
</property>
<property name="title" type="java.lang.String">
<column name="title" length="100" not-null="true" />
</property>
<property name="content" type="text">
<column name="content" />
</property>
<property name="memo" type="java.lang.String">
<column name="memo" length="200" />
</property>
<property name="creator" type="java.lang.String">
<column name="creator" length="10" />
</property>
<property name="createTime" type="java.sql.Timestamp">
<column name="create_time" length="19" />
</property>
<property name="state" type="java.lang.String">
<column name="state" length="1" />
</property>
</class>
</hibernate-mapping>
常规增删改查
这现在对我们来说没有什么难度了,改之前写过的User模块就行了。
编写dao、编写service、编写action、编写配置文件
将配置文件加载到总配置文件中。
导入前端的JSP页面
弄完之后,简单的增删改查我们已经实现了。。
接下来就是处理一些不是常用增删改查的东西了。
创建人与创建时间我们在添加的时候怎么写呢???在需求上,不是让我们填的,而是写死的。
<tr>
<td class="tdBg" width="200px">创建人:</td>
<td>
</td>
<td class="tdBg" width="200px">创建时间:</td>
<td>
</td>
</tr>
创建人我们在Session中找到对应的用户,给出对应的值。显示出来后,在提交的时候还要通过隐藏域把数据带过去
<tr>
<td class="tdBg" width="200px">创建人:</td>
<td>
<s:property value="#session.SYS_USER.name"/>
<s:hidden value="#session.SYS_USER.name" name="info.creator"/>
</td>
<td class="tdBg" width="200px">创建时间:</td>
<td>
<s:date name="info.createTime" format="yyyy-MM-dd HH:MM"/>
<s:hidden name="info.createTime"/>
</td>
</tr>
创建时间,我们可以直接在InfoAction中,new出Info对象,给出对应的值。在JSP页面就可以回显出来了。
当然了,我们也要通过隐藏域把数据带过去。
public String addUI() {
ActionContext.getContext().getContextMap().put("infoTypeMap", Info.INFO_TYPE_MAP);
info = new Info();
info.setCreateTime(new Timestamp(new Date().getTime()));
return "addUI";
}
!
富文本框编辑器
我们想要在那个大文本框中,把复制的内容是带有格式的,图片也可以复制过去。普通的textarea是搞不掂的,我们需要借助别的组件。。我们用的是Ueditor组件
使用步骤:- 导入ueditor/jsp/lib目录中的“commons-codec-1.9.jar”、“json.jar”、“ueditor-1.1.1.jar”这几个jar包到项目的web-inf/lib目录中。
- 配置 ueditor 中图片上传前缀和路径;打开“ueditor/jsp/config.json”
"imageUrlPrefix": "http://localhost:8080", /* 图片访问路径前缀 */
"imagePathFormat": "/upload/ueditor/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
- 在需要用到ueditor的Jsp页面用配置信息:
<script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/ueditor.all.min.js"> </script>
<script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/lang/zh-cn/zh-cn.js"></script>
<script>
//配置ueditor的根路径
var UEDITOR_HOME_URL = "${basePath}js/ueditor/";
var ue = UE.getEditor('editor');
</script>
- 最后在我们的文本框中给出我们写的id就行了,也就是
var ue = UE.getEditor('editor');
中的editor
<td colspan="3"><s:textarea id="editor" name="info.content" cssStyle="width:90%;height:160px;" /></td>
富文本框的配置我们大多数可以在这里修改:
效果:
很奇怪的是,如果单单访问info模块的话,使用是完全没有问题的。但是在总系统进入到info模块时,富文本框就点击不了:输入会显示输入个数,但是显示不了内容。编辑的时候同样看不到内容。
于是在网上搜了一下:把以下的代码加入到要用到富文本框的JSP页面下就解决掉问题了:
<script>
setTimeout(function(){uParse('div',
{
'highlightJsUrl':'/ueditor/third-party/SyntaxHighlighter/shCore.js',
'highlightCssUrl':'/ueditor/third-party/SyntaxHighlighter/shCoreDefault.css'})
},300);
</script>
参考博文:http://blog.csdn.net/eightwhells/article/details/13314069
异步信息交互最后地,我们剩下停用与发布这个功能还没完成...
其实就是一个异步信息交互的实现,当用户点击超链接为停用的时候,就到后台把数据更新,把Info的state变成为0,然后将超链接改成发布。
绑定事件使用opertor前缀+id定位到我们的span节点中。这肯定是独一无二的。
位于iterator内,直接写state判断就行了。
<span id="operator_<s:property value='infoId'/>">
<s:if test="state==1">
<a href="javascript:doPublic('<s:property value='infoId'/>',0)">停用</a>
</s:if>
<s:else>
<a href="javascript:doPublic('<s:property value='infoId'/>',1)">发布</a>
</s:else>
</span>
ajax进行交互
注意在拼接字符串的时候,不要有空格.........
error:如果出错了,可以提示用户。
function doPublic (infoId,state){
$.ajax(
{
url: "${basePath}info/info_doPublic.action",
data: { "info.infoId": infoId,"info.state": state},
type: "post",
success: function (backData) {
if ("更新成功" == backData) {
if (state == 0) {//如果用户点击的是停用
//将超链接改成发布
$("#operator_"+infoId).html("<a href='javascript:doPublic(\""+infoId+"\",1)'>发布</a>");
//将显示状态改成是停用
$("#show_" + infoId).html("停用");
}else{//用户点击的是发布
//将超链接改成停用
$("#operator_"+infoId).html("<a href='javascript:doPublic(\""+infoId +"\",0)'>停用</a>");
//将显示状态改成是发布
$("#show_" + infoId).html("发布");
}
}else {
alert("更新失败,稍后重试");
}
},
//如果失败了,就提示给用户,不要让用户继续操作了
error:function () {
alert("更新失败,稍后重试");
}
}
);
}
</script>
Action处理
得到用户的id,查询出Info对象的信息,再设置Info对象的属性。
public void doPublic() {
try {
if (info != null) {
//得到用户带过来的id查询出该对象
Info objectById = infoServiceImpl.findObjectById(info.getInfoId());
//设置它的状态
objectById.setState(info.getState());
//调用service更新数据库
infoServiceImpl.update(objectById);
//返回数据给浏览器
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/html charset=utf-8");
response.getOutputStream().write("更新成功".getBytes("UTF-8"));
}
} catch (IOException e) {
e.printStackTrace();
}
}
抽取BaseService
到目前为止,我们已经写了三个模块的开发了。我们已经抽取过了BaseAction、BaseDao,我们这次来看看我们的Service接口。
- UserService
/**
* created by ozc on 2017/5/23.
*/
public interface UserService {
//新增
void save(User user);
//更新
void update(User user);
//根据id删除
void delete(Serializable id);
//根据id查找
User findObjectById(Serializable id);
//查找列表
List<User> findObjects() throws ServiceException;
//导出用户列表
void exportExcel(List<User> userList, ServletOutputStream outputStream);
//导入用户列表
void importExcel(File userExcel, String userExcelFileName);
/**
* 根据帐号和用户id查询用户
*
* @param id 用户ID
* @param account 用户帐号
* @return 用户列表
*/
List<User> findAccount(String id, String account);
void saveUserAndRole(User user, String[] userRoleIds);
//通过用户id得到该用户的角色
List<UserRole> findRoleById(String id);
void deleteUserRoleById(String[] userRoleIds);
List<User> findUserByAccountAndPassword(String account, String password);
}
- InfoService
/**
* created by ozc on 2017/5/23.
*/
public interface InfoService {
//新增
public void save(Info info);
//更新
public void update(Info info);
//根据id删除
public void delete(Serializable id);
//根据id查找
public Info findObjectById(Serializable id);
//查找列表
public List<Info> findObjects() ;
}
- RoleService
/**
* Created by ozc on 2017/5/26.
*/
public interface RoleService {
//新增
void save(Role role);
//更新
void update(Role role);
//根据id删除O
void delete(Serializable id);
//根据id查找
Role findObjectById(Serializable id);
//查找列表
List<Role> findObjects() ;
}
我们可以发现,三个Service接口中都存在着增删改查的方法,这明显就是重复的代码。因此,我们需要将他们进行抽取成一个BaseService。
抽取BaseService在core模块中添加service包,抽取BaseService
package zhongfucheng.core.service;
import java.io.Serializable;
import java.util.List;
/**
* Created by ozc on 2017/6/7.
*/
interface BaseService<T> {
//新增
void save(T entity);
//更新
void update(T entity);
//根据id删除
void delete(Serializable id);
//根据id查找
T findObjectById(Serializable id);
//查找列表
List<T> findObjects();
}
- 抽取BaseServiceImpl
我们的Sercive是调用dao层的对象来实现方法的,因为这个Service是代表整个项目的Service,于是应该使用BaseDao
package zhongfucheng.core.service.impl;
import zhongfucheng.core.dao.BaseDao;
import zhongfucheng.core.service.BaseService;
import java.io.Serializable;
import java.util.List;
/**
* Created by ozc on 2017/6/7.
*/
public abstract class BaseServiceImpl <T> implements BaseService <T>{
//通过BaseDao来操作数据库
private BaseDao<T> baseDao;
@Override
public void save(T entity) {
baseDao.save(entity);
}
@Override
public void update(T entity) {
baseDao.update(entity);
}
@Override
public void delete(Serializable id) {
baseDao.delete(id);
}
@Override
public T findObjectById(Serializable id) {
return baseDao.findObjectById(id);
}
@Override
public List<T> findObjects() {
return baseDao.findObjects();
}
}
以Info模块举例子
- InfoService
InfoService继承了BaseService接口,于是就有了增删改查的方法。同时把泛型T的类型确定下来。
/**
* created by ozc on 2017/5/23.
*/
public interface InfoService extends BaseService<Info> {
}
- InfoServiceImpl
继承了InfoService,有了增删该查的方法,然而具体的操作是BaseServiceImpl中实现的。我们继承它,并给出泛型T对应的类型。
@Service
public class InfoServiceImpl extends BaseServiceImpl<Info> implements InfoService {
}
现在的问题是什么呢???我们在BaseServiceImpl中使用了BaseDao这个变量来对数据库进行操作。可是在BaseServiceImpl中是没有BaseDao这个变量的。
首先,要明确的是,我们不能在BaseServiceImpl中注入BaseDao,因为BaseServiceImpl本身就是一个抽象类。那我们怎么对BaseDao进行实例化呢???
我们可以这样做:
- 在InfoServiceImpl本身就需要注入InfoDao,来对数据库的操作。
- 而我们这一次不使用属性输入,使用set方法注入
- 在注入的同时,在BaseServiceImpl中给BaseDao设置set方法。
- 那么我们在注入的时候,就可以调用BaseDao的set方法,把我们要注入的对象给过去。
-
最后,我们在BaseServiceImpl中就有了baseDao这个变量了。
- InfoServiceImpl得到InfoDao对象,并把InfoDao对象设置到BaseServiceImpl中。
@Service
public class InfoServiceImpl extends BaseServiceImpl<Info> implements InfoService {
private InfoDao infoDao;
@Resource
public void setInfoDao(InfoDao infoDao) {
super.setBaseDao(infoDao);
this.infoDao = infoDao;
}
}
- BaseServiceImpl为BaseDao设置set方法。
```java
//通过BaseDao来操作数据库
private BaseDao<T> baseDao;
public void setBaseDao(BaseDao<T> baseDao) {
this.baseDao = baseDao;
}
条件查询
我们来实现下面的功能:
其实也是一个查询,只不过查询多了一个条件罢了。按照传统的方式我们可以这样做:
- 在BaseDao中声明一个方法,接收的是SQL和参数列表
//根据条件查询列表
List<T> findObjects(String sql, List<Object> objectList);
- 在BaseDaoImpl实现它
@Override
public List<T> findObjects(String sql, List<Object> objectList) {
Query query = getSession().createQuery(sql);
if (objectList != null) {
int i =0;
for (Object o : objectList) {
query.setParameter(i, o);
i++;
}
return query.list();
}
return query.list();
}
- BaseService定义它:
//根据条件查询列表
List<T> findObjects(String sql, List<Object> objectList);
- BaseServiceImpl中使用baseDao来调用它
@Override
public List<T> findObjects(String sql, List<Object> objectList) {
return baseDao.findObjects(sql, objectList);
}
- Action中处理请求
我们还是用着listUI这个方法,因为它仅仅是参数可能不同。
- 如果用户使用的是条件查询,那么它应该有Info对象带过来。
- 如果不是条件查询,就没有Info对象
-
根据Info对象设置是否要设置参数来查询【在HQL语句中添加新字段】。所以这个方法通用。
public String listUI() { //查询语句 String hql = "FROM Info i "; List<Object> objectList = new ArrayList<>(); //根据info是否为null来判断是否是条件查询。如果info为空,那么是查询所有。 if (info != null) { if (StringUtils.isNotBlank(info.getTitle())) { hql += "where i.title like ?"; objectList.add("%" + info.getTitle() + "%"); } } infoList = infoServiceImpl.findObjects(hql,objectList); ActionContext.getContext().getContextMap().put("infoTypeMap", Info.INFO_TYPE_MAP); return "listUI"; }
看回我们Action中的代码,我们可以看出一些不够优雅的地方:
于是,我们想要用一个工具类来把上面的代码进行优化。
针对上面的问题,我们发现手写拼接SQL很容易出错。那我们可以在工具类里面拼接,使用的时候调用方法获取就行啦。查询的对象写死了,我们要可以处理任何的查询。。
我们能够找到如下的规律:
FROM Info
WHERE title like ? and state = ?
order by createTime,state
条件查询(QueryHelper):
1、查询条件语句hql:
from 子句:必定出现;而且只出现一次
where 子句:可选;但关键字where 出现一次;可添加多个查询条件
order by子句:可选;但关键字order by 出现一次;可添加多个排序属性
2、查询条件值集合:
出现时机:在添加查询条件的时候,?对应的查询条件值
- 工具类代码:
package zhongfucheng.core.utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ozc on 2017/6/7.
*/
public class QueryHelper {
private String fromClause = "";
private String whereClause = "";
private String orderbyClause = "";
private List<Object> objectList;
public static String ORDER_BY_ASC = "asc";
public static String ORDER_BY_DESC = "desc";
//FROM子句只出现一次
/**
* 构建FROM字句,并设置查询哪张表
* @param aClass 用户想要操作的类型
* @param alias 别名
*/
public QueryHelper(Class aClass, String alias) {
fromClause = " FROM " + aClass.getSimpleName() + " " + alias;
}
//WHERE字句可以添加多个条件,但WHERE关键字只出现一次
/**
* 构建WHERE字句
* @param condition
* @param objects
* @return
*/
public QueryHelper addCondition(String condition, Object... objects) {
//如果已经有字符了,那么就说明已经有WHERE关键字了
if (whereClause.length() > 0) {
whereClause += " AND " + condition;
} else {
whereClause += " WHERE" + condition;
}
//在添加查询条件的时候,?对应的查询条件值
if (objects == null) {
objectList = new ArrayList<>();
}
for (Object object : objects) {
objectList.add(object);
}
return this;
}
/**
*
* @param property 要排序的属性
* @param order 是升序还是降序
* @return
*/
public QueryHelper orderBy(String property, String order) {
//如果已经有字符了,那么就说明已经有ORDER关键字了
if (orderbyClause.length() > 0) {
orderbyClause += " , " + property +" " + order;
} else {
orderbyClause += " ORDER BY " + property+" " + order;
}
return this;
}
/**
* 返回HQL语句
*/
public String returnHQL() {
return fromClause + whereClause + orderbyClause;
}
/**
* 得到参数列表
* @return
*/
public List<Object> getObjectList() {
return objectList;
}
}
- Action处理:
public String listUI() {
QueryHelper queryHelper = new QueryHelper(Info.class, "i");
//根据info是否为null来判断是否是条件查询。如果info为空,那么是查询所有。
if (info != null) {
if (StringUtils.isNotBlank(info.getTitle())) {
queryHelper.addCondition(" i.title like ? ", "%" + info.getTitle() + "%");
}
}
queryHelper.orderBy("i.createTime", QueryHelper.ORDER_BY_DESC);
infoList = infoServiceImpl.findObjects(queryHelper);
//infoList = infoServiceImpl.findObjects(hql,objectList);
ActionContext.getContext().getContextMap().put("infoTypeMap", Info.INFO_TYPE_MAP);
return "listUI";
}
- 最后在dao、service层中添加一个queryHelper参数的方法就行了。
数据回显
前面我们已经完成了条件查询的功能,可以根据用户给出的条件进行查询数据。但是呢,还是有一些小毛病的。我们来看看:
当我们查询数据时候,对查询出来的数据进行操作。操作完毕后,它回到的不是我们查询后的数据,而是我们的初始化数据。这明显是不合适的,当用户操作完后,我们应该返回的还是条件查询出来的数据。
还有一点的就是:我们的分页还没写......因此,下面主要解决这两个问题。
首先,我们来分析一下为什么我们操作完毕后,得到的是初始化的数据。我们按照用户的操作来看看到底哪里出了问题。
- 用户按条件查询数据,显示查询后的数据
- 用户点击编辑/删除对查询后的数据操作,交给Action处理
- Action返回给显示页面jsp
- JSP页面提交请求到Action中,Action进行处理
- 最后Action重定向到listUI
那么在这个过程,我们遇到什么问题呢???
处理1.0
在Action中使用一个变量封装着查询条件
/************获取查询条件的值*************************/
private String selectCondition;
public String getSelectCondition() {
return selectCondition;
}
public void setSelectCondition(String selectCondition) {
this.selectCondition = selectCondition;
}
当请求到Action时,我们将查询条件的值取出来,发给对应的JSP页面
public String editUI() {
//得到所有的信息类型
ActionContext.getContext().getContextMap().put("infoTypeMap", Info.INFO_TYPE_MAP);
//外边已经传了id过来了,我们要找到id对应的Info
if (info != null && info.getInfoId() != null) {
//把查询条件发给JSP页面
ActionContext.getContext().getContextMap().put("selectCondition", info.getTitle());
//直接获取出来,后面JSP会根据Info有getter就能读取对应的信息!
info = infoServiceImpl.findObjectById(info.getInfoId());
}
return "editUI";
}
JSP页面把发送过来的值存储好,通过隐藏域发送给Action
<%--把查询条件带过去给Action--%>
<s:hidden name="selectCondition"/>
重新抵达到Action的时候,由于Struts2有自动封装的功能,所以可以把查询条件封装到selectCondition变量中。最后操作完重定向到listUI界面
由于是重定向,所以我们需要在struts配置文件中把我们的查询条件带过去:
<result name="list" type="redirectAction">
<param name="actionName">info_listUI</param>
<!--重定向回去的时候,把查询条件带上-->
<param name="info.title">${selectCondition}</param>
</result>
当然啦,在删除的时候,把查询条件记录下来就行了。
//删除
public String delete() {
selectCondition = info.getTitle();
String id = info.getInfoId();
infoServiceImpl.delete(id);
return "list";
}
- 效果:
上面我们的确解决了查询后数据回显的情况,但是如果我们的查询条件是中文的话,会怎么样??
变成了乱码了.....在解决它之前,我们又来分析一下为什么出现乱码了....
- 我们知道Struts2使用post提交表单的数据,内部会自动帮我们转化编码的。也就是说,我们的乱码肯定不是在表单传输的过程中搞乱的。
-
那就是在重定向的时候,中文参数的数据搞乱了。
- 我们在配置文件上,要传递参数的时候,设置编码:
<!--传输数据的时候需要编码-->
<param name="encode">true</param>
- 在Action中读取这个数据的时候,我们解码就行了.
if (info != null) {
if (StringUtils.isNotBlank(info.getTitle())) {
selectCondition = URLDecoder.decode(info.getTitle(),"UTF-8");
info.setTitle(selectCondition);
queryHelper.addCondition(" i.title like ? ", "%" + info.getTitle() + "%");
}
}
分页
分页对我们来说也不是陌生的事情了,我曾经在写JDBC博文的时候就讲解过分页了:http://blog.csdn.net/hon_3y/article/details/53790092
分页的复用代码http://blog.csdn.net/hon_3y/article/details/70051541
我们这一次还是使用回我们的分页复用代码,具体不同的需求,在上面修改即可了。
在dao和daoImpl中添加方法- dao方法
/**
*
* @param queryHelper 查询助手,条件查询都交给查询助手来干
* @param currentPage 当前页数
* @return
*/
PageResult getPageResult(QueryHelper queryHelper, int currentPage);
- 在查询助手queryHelper中添加一个查询总记录数的sql语句
/**
*
* @return 返回查询总记录数的sql语句
*/
public String getTotalRecordSql() {
return "SELECT COUNT(*) " + fromClause + whereClause;
}
-
daoImpl实现:
- 先查询总记录数
- 初始化Page对象
- 查询分页数据,将分页数据设置到Page对象中
- 返回Page对象
public PageResult getPageResult(QueryHelper queryHelper, int currentPage) {
//查询总记录数
Query queryCount = getSession().createQuery(queryHelper.getTotalRecordSql());
if (queryHelper.getObjectList() != null) {
int i =0;
for (Object o : queryHelper.getObjectList()) {
queryCount.setParameter(i, o);
i++;
}
}
Long totalRecord = (Long) queryCount.uniqueResult();
//初始化PageResult对象
PageResult pageResult = new PageResult(currentPage, totalRecord);
//查询具体模块的数据【有查询条件的也可以处理】
Query query = getSession().createQuery(queryHelper.returnHQL());
if (queryHelper.getObjectList() != null) {
int i =0;
for (Object o : queryHelper.getObjectList()) {
query.setParameter(i, o);
i++;
}
}
//设置分页开始和末尾
query.setFirstResult(pageResult.getStartIndex());
query.setMaxResults(pageResult.getLineSize());
List dataList = query.list();
//将条件查询出来的数据设置到Page对象中
pageResult.setList(dataList);
return pageResult;
}
在service和serviceImpl中添加方法
-
baseService
PageResult getPageResult(QueryHelper queryHelper, int currentPage);
- baseServiceImpl
public PageResult getPageResult(QueryHelper queryHelper, int currentPage) {
return baseDao.getPageResult(queryHelper, currentPage);
}
在Action中调用service的方法
设置我们需要用到的分页属性。
private int currentPageCount;
private PageResult pageResult;
public int getCurrentPageCount() {
return currentPageCount;
}
public void setCurrentPageCount(int currentPageCount) {
this.currentPageCount = currentPageCount;
}
public PageResult getPageResult() {
return pageResult;
}
public void setPageResult(PageResult pageResult) {
this.pageResult = pageResult;
}
判断我们的当前页是否为0【如果为0,就代表着刚初始化值,我们设置为1】,调用service的方法得到分页对象
//当前页数没有值,那么赋值为1
if (currentPageCount == 0) {
currentPageCount = 1;
}
pageResult = infoServiceImpl.getPageResult(queryHelper,currentPageCount);
在JSP页面中,我们遍历分页对象的集合就可以获取分页的数据了。
<s:iterator value="pageResult.list" status="st">
效果:
抽取属性
我们的分页属性和查询条件数据不单单只有Info模块用的,于是我们将分页数据抽取到BaseAction中
/************分页属性*************************/
protected int currentPageCount;
protected PageResult pageResult;
public int getCurrentPageCount() {
return currentPageCount;
}
public void setCurrentPageCount(int currentPageCount) {
this.currentPageCount = currentPageCount;
}
public PageResult getPageResult() {
return pageResult;
}
public void setPageResult(PageResult pageResult) {
this.pageResult = pageResult;
}
/************获取查询条件的值*************************/
protected String selectCondition;
public String getSelectCondition() {
return selectCondition;
}
public void setSelectCondition(String selectCondition) {
this.selectCondition = selectCondition;
}
修改其他的模块,也能够拥有条件查询和分页查询这两个功能:以用户模块为例。
-
将查询的对象设置为User,根据用户名的名字来进行条件查询。
//抛出Action异常 public String listUI() throws ServiceException, UnsupportedEncodingException { QueryHelper queryHelper = new QueryHelper(User.class, "u"); //根据info是否为null来判断是否是条件查询。如果info为空,那么是查询所有。 if (user != null) { if (org.apache.commons.lang.StringUtils.isNotBlank(user.getName())) { selectCondition = URLDecoder.decode(user.getName(),"UTF-8"); user.setName(selectCondition); queryHelper.addCondition(" u.name like ? ", "%" + user.getName() + "%"); } } //当前页数没有值,那么赋值为1 if (currentPageCount == 0) { currentPageCount = 1; } pageResult = userServiceImpl.getPageResult(queryHelper,currentPageCount); return "listUI"; }
- 在JSP页面遍历的是分页对象,导入我们的分页下标下表JSP
<s:iterator value="pageResult.list">
<jsp:include page="/common/pageNavigator.jsp"/>
处理查询后数据回显的问题:
- 在跳转到编辑页面之前,把查询条件记录下来。不然就会被覆盖掉了。
public String editUI() {
//把所有的角色查询出来,带过去给JSP页面显示
ActionContext.getContext().getContextMap().put("roleList", roleServiceImpl.findObjects());
//外边已经传了id过来了,我们要找到id对应的User
if (user != null &&user.getId() != null ) {
//得到查询条件
selectCondition = user.getName();
//直接获取出来,后面JSP会根据User有getter就能读取对应的信息!
user = userServiceImpl.findObjectById(user.getId());
//通过用户的id得到所拥有UserRole
List<UserRole> roles = userServiceImpl.findRoleById(user.getId());
//把用户拥有角色的id填充到数组中,数组最后回显到JSP页面
int i=0;
userRoleIds = new String[roles.size()];
for (UserRole role : roles) {
userRoleIds[i++] = role.getUserRoleId().getRole().getRoleId();
}
}
return "editUI";
}
- 在显示编辑页面的JSP上,把查询条件带过去给Action
<%--把查询条件通过隐藏域带过去给Action--%>
<s:hidden name="selectCondition"></s:hidden>
- 最后,当编辑完,重定向的时候,也要将查询条件带过去。预防着中文的问题,我们对其进行编码
<action name="user_*" class="zhongfucheng.user.action.UserAction" method="{1}">
<result name="{1}" >/WEB-INF/jsp/user/{1}.jsp</result>
<!--返回列表展示页面,重定向到列表展示-->
<result name="list" type="redirectAction">
<param name="actionName">user_listUI</param>
<param name="user.name">${selectCondition}</param>
<param name="encode">true</param>
</result>
</action>
- 在删除的时候,也要把查询条件记录下来。
//删除
public String delete() {
if (user != null && user.getId() != null) {
//记载着查询条件
selectCondition = user.getName();
userServiceImpl.delete(user.getId());
}
return "list";
}
总结
- 如果页面上的数据是写死的,那么我们就考虑一下是不是可以在域对象把数据带过去了。
- 使用Ueditor来做富文本编辑器
- 在页面上定位一个标签,我们可以使用特殊的前缀+上我们的Id。
- 由于Service的代码重复性太高了,我们也将Service进行抽取。抽取成一个BaseService接口
- BaseServiceImpl实现BaseService接口,但他要使用BaseDao对象来对实现的方法进行调用
- 此时,BaseServiceImpl是一个抽象类,它本身不能实例化了。那怎么将BaseDao实例化呢??
- 当我们的InfoServiceImpl继承继承着BaseServiceImpl时,本身就需要用到InfoDao来对该模块的业务进行调用。
- 在InfoServiceImpl对InfoDao实例话是很容易的,可以使用自动装配,set方法注入等等。这次我们使用set方法注入!
- set方法有什么好处??能够在InfoServiceImpl注入InfoDao对象的同时,还能进行其他操作。比如:调用父类的set方法!!!!!!!
- 我们只要在BaseServiceImpl提供一个setBaseDao方法,子类再把自身的Dao传递进去。那么我们的BaseDao就被实例化了!
- 由于我们查询条件的不确定性,要对查询条件字符串进行拼接。这样不安全和很容易出错。我们就封装了一个查询助手对象。专门用于查询的。
- 在条件查询的过程中,如果我们不将查询条件保留下来。那么这个条件就会丢失。等我们操作完之后,它返回的列表页面是没有带查询条件的。
- 例如:我们的编辑操作就经历了好几个步骤:请求交由editUI()处理,如果我们不把查询条件记录下来。它就被原本的属性给覆盖掉了。
- 接着跳转到编辑页面,如果我们不将查询条件通过隐藏域交给Action,那么查询条件在页面上就丢失了。
- 最后,我们重定向到list页面时,要么通过URL添加参数来把条件重定向到list()方法上,要么就使用Struts2的参数传递。其中,如果带中文的话,记得要编码啊!
- 对于分页数据我们已经做得很多了。最后将我们Action中通过的数据封装到BaseAction中就行了。
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y