查看原文
其他

纳税服务系统六(信息发布管理模块)【Ueditor、异步信息交互、抽取BaseService、条件查询、分页】

Java3y Java3y 2021-01-12

需求分析

我们现在来到了纳税服务系统的信息发布管理模块,首先我们跟着原型图来进行需求分析把:

一些普通的CRUD,值得一做的就是状态之间的切换了。停用和发布切换。


值得注意的是:在信息内容中,它可以带格式地复制内容,然后上传到我们的服务器中。

流程图:


编写JavaBean与配置文件

javaBean

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方法。

  //通过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参数的方法就行了。


数据回显

前面我们已经完成了条件查询的功能,可以根据用户给出的条件进行查询数据。但是呢,还是有一些小毛病的。我们来看看:

这里写图片描述

当我们查询数据时候,对查询出来的数据进行操作。操作完毕后,它回到的不是我们查询后的数据,而是我们的初始化数据。这明显是不合适的,当用户操作完后,我们应该返回的还是条件查询出来的数据

还有一点的就是:我们的分页还没写……因此,下面主要解决这两个问题。

首先,我们来分析一下为什么我们操作完毕后,得到的是初始化的数据。我们按照用户的操作来看看到底哪里出了问题。

  1. 用户按条件查询数据,显示查询后的数据

  2. 用户点击编辑/删除对查询后的数据操作,交给Action处理

  3. Action返回给显示页面jsp

  4. JSP页面提交请求到Action中,Action进行处理

  5. 最后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";
   }


处理2.0

上面我们的确解决了查询后数据回显的情况,但是如果我们的查询条件是中文的话,会怎么样??

这里写图片描述

变成了乱码了…..在解决它之前,我们又来分析一下为什么出现乱码了….

  • 我们知道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


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存