继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

纳税服务系统六(【Ueditor、异步信息交互】

Java3y
关注TA
已关注
手记 297
粉丝 1.5万
获赞 6488
原标题:纳税服务系统六(信息发布管理模块)【Ueditor、异步信息交互、抽取BaseService、条件查询、分页】

tags: 纳税服务系统项目


需求分析

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

一些普通的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方法。

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

数据回显

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

这里写图片描述

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

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

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

  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

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP