SSH综合练习-仓库管理系统-第二天

it2025-03-07  80

SSH综合练习-仓库管理系统-第二天

 

今天的主要内容:

货物入库 页面信息自动补全回显功能:(学习目标:练习Ajax交互) 根据货物简记码来自动查询回显已有货物(Ajax回显) 根据货物名来自动查询补全已有货物(自动补全插件) 货物入库功能:(学习目标:练习多表插入) 保存货物信息的同时记录入库的历史记录。 更新货物信息的同时记录入库的历史记录。 库存管理功能(分页+多条件) 分页数据Bean的设计 要编写Pagination Bean 分页后台的代码编码 QBC方案—Hibernate做法 SQL拼接方案—传统做法(选做) 分页工具条的编写(了解) 分页代码重构优化(抽取分页代码) 历史记录查询(课后作业,涉及到多表) 出库功能(课后作业,自己思考逻辑,和入库逻辑差不多) 公共代码封装打包(了解)

 

课程目标:

Ajax的使用和多表的插入,强化复习 jQueryUI的插件的使用方法。自动补全查询。 业务条件+分页条件的综合查询

 

 

货物入库功能

根据简记码查询货物(精确匹配)

点击【入库】

 

业务分析:

简记码是为了方便用户快速定位已存在的货物而设计的。需要分析两个方面:

关于简记码回显的使用。

当用户输入简记码时,通过Ajax请求,将用户输入的简记码发送到服务器,服务器判断简记码是否存在。

如果 存在,则说明货物也存在,则查询出货物信息,回显给表单。当点击"入库"的时候,进行 更新货物的数量即可。 如果 不存在,则说明数据库中没有对应的货物,这是一个新的货物,需要 手动输入货物的完整信息。当点击"入库"的时候,进行 保存货物的所有信息

 

2. 关于同一个按钮功能是走更新还是保存,服务器端如何判断调用什么方法呢?

通过主键id来判断。因此,则需要在form表单中放置一个隐藏域id,如果货物存在,则id有值,则后台只更新货物数量即可;如果货物不存在,则id没值,则插入保存一个新的货物。

 

开发思路:

业务一:校验数据库简记码是否存在

改造页面表单为struts标签(因为要回显一些信息) 编写页面的Ajax请求代码(事件代码) 编写后台服务器端代码,货物的数据封装为json返回到前台(fireBug调试) 完善页面的Ajax请求代码,完善回调函数,进行页面数据的回显。

 

第一步:改造页面表单为struts标签,修改jsps/save/save.jsp

<s:form action="goods_instore" namespace="/" method="post" name="select">

 

</s:form>

save.jsp页面

<tr>

                            <td>

                                简记码:

                            </td>

                            <td>

                                <s:textfield name="nm" cssClass="tx"/>

                                <!-- 隐藏域:一会来识别是更新还是插入:是一个存在货物还是新的货物 -->

                                <s:hidden name="id" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                货物名称:

                            </td>

                            <td>

                                <s:textfield name="name" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                计量单位:

                            </td>

                            <td>

                                <s:textfield name="unit" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                入库数量:

                            </td>

                            <td>

                                <s:textfield name="amount" cssClass="tx"/>

                            </td>

                        </tr>

 

<tr>

                            <td>

                                选择仓库:

                            </td>

                            <td>

                                <select class="tx" style="width:120px;" name="store.id" id="store_id">

                                    

                                </select>

                                (此信息从数据库中加载)

                            </td>

                        </tr>

提示:注意关联属性对象的数据是如何封装的。

测试:改造完页面后,测试页面是否正常。

 

第二步:编写页面的Ajax请求代码(事件代码):

用户输入简记码 ,使用失去焦点的事件blur(离焦事件),发起Ajax请求。验证简记码在数据库中是否存在。

//简记码绑定一个离焦事件

        $("input[name='nm']").blur(function(){

            

            //请求服务器,获取货物信息

            $.post("${pageContext.request.contextPath}/goods_findByNmAjax.action",{"nm":$(this).val()},function(data){

                //data:返回的json对象,一个(简记码和货物是一对一关系)

             alert(data)                

            });

        });

 

第三步:编写后台服务器端代码(Action类),货物的数据封装为json返回到前台(fireBug调试)

在服务器端接收到的简记码,进行逻辑处理,将结果转换为json返回。

创建GoodsAction.java 代码

//货物的表现层

public class GoodsAction extends BaseAction<Goods>{

    //注入service

    private IGoodsService goodsService;

    public void setGoodsService(IGoodsService goodsService) {

        this.goodsService = goodsService;

    }

    

    //根据简记码查询货物(ajax

    public String findByNmAjax(){

        Goods goods = goodsService.findGoodsByNm(model.getNm());

        //goods对象转换成json字符串

        //压入栈顶

        pushValueStackRoot(goods);

        return "json";

    }

}

 

第四步:编写业务层代码

编写IGoodsService 接口

//货物的业务层接口

public interface IGoodsService {

 

    /**

     * 根据简记码查询货物

     * @param nm

     * @return

     */

    public Goods findGoodsByNm(String nm);

}

实现类 GoodsServiceImpl

//货物的业务层实现

public class GoodsServiceImpl extends BaseService implements IGoodsService{

    //注入dao

    private IGenericDao<Goods, String> goodsDao;

    public void setGoodsDao(IGenericDao<Goods, String> goodsDao) {

        this.goodsDao = goodsDao;

    }

 

    public Goods findGoodsByNm(String nm) {

        //qbc

        DetachedCriteria criteria = DetachedCriteria.forClass(Goods.class)

                .add(Restrictions.eq("nm", nm));

        List<Goods> list = goodsDao.findByCriteria(criteria);

        

        return list.isEmpty()?null:list.get(0);

    }

}

 

第五步:配置struts.xml

<!-- 货物管理 -->

        <action name="goods_*" class="goodsAction" method="{1}">

            <!-- json无需配置结果集了 -->

        </action>

代码优化:

通过配置代码发现,返回json的结果集配置一样,那么可以将其抽取为全局的结果集配置:

<package name="default" namespace="/" extends="json-default">

 

    <!-- 全局结果集 -->

    <global-results>

        <!-- json结果集类型 -->

            <result name="json" type="json"></result>

    </global-results>

</package>

提示:后面只要需要转换为json,直接返回json结果集即可。

 

 

第六步:配置applicationContext.xml

<!--通用的DAO类 -->

    <bean id="goodsDao" class="cn.itcast.storemanager.dao.impl.GenericDaoImpl">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    

    <!-- service -->

    <bean id="goodsService" class="cn.itcast.storemanager.service.impl.GoodsServiceImpl">

        <property name="goodsDao" ref="goodsDao"/>

    </bean>

    

    <!-- action -->

    <bean id="goodsAction" class="cn.itcast.storemanager.web.action.GoodsAction" scope="prototype">

        <property name="goodsService" ref="goodsService"/>

    </bean>

 

第七步:测试:

在数据库中手动插入一个货物,然后使用firebug进行抓包测试。

Ajax加载出错:延迟加载错误(一对多)

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.itcast.storemanager.domain.Goods.histories, no session or session was closed

解决方法: 对于不需要转换返回的数据,用@JSON排除集合属性Histories

    //排除histories历史集合中的属性转换json

    @JSON(serialize=false)

    public Set getHistories() {

        return this.histories;

    }

Ajax加载仍然出错:延迟加载错误(多对一)

由于save.jsp页面中使用:

$("#store_id").val(data.store.id);//从货物关联到仓库

所以要求货物中关联仓库。

所以报错

Caused by: org.hibernate.LazyInitializationExcep

Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session

解决方法: 货物关联的仓库的数据是需要的,不能排除,需要加载

两种方式:

将store改为立即加载(hbm---class标签lazy属性改为false)

修改:Goods.hbm.xml文件:

<many-to-one name="store" class="cn.itcast.storemanager.domain.Store" fetch="select" lazy="false">

<column name="storeid" length="32" />

</many-to-one>

配置 OpenSessionInView来解决,这里注意使用OpenSessionInView过滤器一定要放置到struts2的过滤器的前面。

在web.xml容器中添加:

    <!-- openSessionInView处理器,必须配置在stuts的过滤器前面 -->

    <filter>

        <filter-name>OpenSessionInView</filter-name>

        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>OpenSessionInView</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

 

使用火狐的firebug调试,测试结果(js对象):

 

第八步:完善页面的Ajax请求代码,完善回调函数,进行页面数据的回显。

编写回调函数内容,将页面的字段元素赋值。

//简记码绑定一个离焦事件

        $("input[name='nm']").blur(function(){

            //请求服务器,获取货物信息

            $.post("${pageContext.request.contextPath}/goods_findByNmAjax.action",{"nm":$(this).val()},function(data){

                //data:返回的json对象,一个(简记码和货物是一对一关系)

                if(data ==null){

                    //没有匹配的货物,清除表单

                    $("input[name='name']").val("");

                    $("input[name='unit']").val("");

                    $("#store_id").val("");

                    //隐藏域

                    $("input[name='id']").val("");//id属性很重要,用来判断是修改已有的货物还是新增一个货物

                    //解禁

                    $("input[name='name']").removeAttr("disabled");

                    $("input[name='unit']").removeAttr("disabled");

                    $("#store_id").removeAttr("disabled");

                

                }else{

                    //填充

                    $("input[name='name']").val(data.name);

                    $("input[name='unit']").val(data.unit);

                    $("#store_id").val(data.store.id);//从货物关联到仓库

                    //隐藏域

                    $("input[name='id']").val(data.id);//id属性很重要,用来判断是修改已有的货物还是新增一个货物

                    

                    //禁用表单元素

                    $("input[name='name']").attr("disabled",true);

                    $("input[name='unit']").attr("disabled",true);

                    $("#store_id").attr("disabled",true);

                }

            });

        });

 

 

 

根据货物名称查询 (模糊匹配,自动补全)

目标效果参考:百度

联想提示。。。

jQuery UI的autocomplete插件介绍和引入

autocomplete插件是jQuery官方提供了一些免费开源的插件集中的一个插件。

下载jquery-ui-1.9.2.custom.zip

将插件集解压到硬盘:

查看插件的功能:点击index.html,可以看到效果

 

下面我们开始开发实现:

第一步:引入jquery ui开发的js和css

只引用插件相关的组件js

缺点:组建js必须要很熟悉才能导入。--不推荐

 

另外一种方式:全部引入,推荐,我们就采用这种,具体方法见下面:

 

插件的引入方式(两步):

1)导入相关文件:jQuery核心、插件的js和CSS样式

2)在页面中引入jQuery、插件js、插件的css

 

导入jqueryui插件的js和css

<!-- 引入jquery库 -->

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.8.3.js"></script>

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jqueryui/jquery-ui-1.9.2.custom.js"></script>

<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/js/jqueryui/smoothness/jquery-ui-1.9.2.custom.css"></link>

 

插件如何使用?

可以参考自带的示例或api文档。

该补全插件主要有两种应用:本地数据自动补全和动态数据补全。

 

第二步:如果开发呢?

点击:autocomplete.html文件,里面有开发案例

 

 

应用一:本地数据自动补全

方法是:对source属性绑定一个数组。

//对货物名称进行自动补全功能

        //方案一:-----数据源是固定的,如果数据源非常大,那么这里要加载所有的大量数据

        $( "input[name='name']" ).autocomplete({

         source: [ "c++", "java", "php", "coldfusion", "javascript", "asp", "ruby" ]

        });

针对货物名称的输入框:

<tr>

                            <td>

                                货物名称:

                            </td>

                            <td>

                                <s:textfield name="name" cssClass="tx"/>

                            </td>

                        </tr>

页面效果:

缺点:不适用于大规模数据,页面要加载全部的数据。

 

应用二:动态数据补全 (远程数据加载实时补全)

根据用户输入内容,实时查询,实现数据的补全。

 

方法是:对source绑定一个function(request,response),通过request.term获取输入的值,通过response包装要显示的值。

第一步:编写save.jsp

//方案二:-----自定义数据源,从数据库中查询,动态加载数据

        $( "input[name='name']" ).autocomplete({

            //request.term:文本框输入的值

            //response(json数组结果)

         source: function( request, response ) {

                    

                 $.post("${pageContext.request.contextPath}/goods_findByNameLikeAjax.action",{"name":request.term},function(data){

                     //data:json数组

                     response(data);

                 });

                 

         },

        });

 

第三步:编写GoodsAction的findByNameLikeAjax方法

    //根据名称模糊匹配(ajax

    public String findListByNameAjax(){

        

        List<Goods> list = goodsService.findGoodsByNameLike(model.getName());

        

        pushValueStackRoot(list);

        

        return "json";

    }

 

第四步:业务层

接口IGoodsService类

    /**

     * 根据名称模糊查询货物列表

     * @param name

     * @return

     */

    public List<Goods> findGoodsByNameLike(String name);

实现类GoodsServiceImpl类

s    public List<Goods> findGoodsByNameLike(String name) {

        //qbc

        DetachedCriteria criteria = DetachedCriteria.forClass(Goods.class)

                .add(Restrictions.like("name", "%"+name+"%"));

        return goodsDao.findByCriteria(criteria);

    }

 

第五步:配置struts.xml 结果集返回 (略,使用全局结果集)

<package name="default" namespace="/" extends="json-default">

    <!-- 全局结果集 -->

    <global-results>

        <!-- json结果集类型 -->

            <result name="json" type="json">                 

            </result>

    </global-results>

</package>

 

第六步:页面测试:

问题:下拉列表中没有显示值。

查看火狐浏览器,看到返回的结果:

分析:

如果使用简单的数组数据是没有问题的:

原因:数据格式不对。我们用的是复杂的json数据格式。

复杂数据怎么办?

点击:autocomplete.html文件

选择"source"

 

需要在数组的元素对象中指定label或者value才能显示。如果只指定一个,那么另外一个的值默认会等于这个值,即:你指定lable:"阿司匹林",如果没有value的话那么值也是"阿司匹林"。

那么如何指定label呢?

就让生成json的时候有这个getter属性就行了。

 

操作:

两种方法:

方法一:修改Goods实体类 ,添加getLabel方法 :(采用方案)

    //增加label属性来显示下拉列表,增加label为key的getter方法

    public String getLabel(){

        return name + "("+ store.getName() +")";

    }

    

    public String getValue() {

        return this.name;

    }

测试页面:ok。

此时查看火狐浏览器

 

 

【继续优化一】:

根据选择的label来显示其他货物的信息。

分析:需要添加选中列表的事件。

查看API的event章节:

我们找到select事件:

说明:select是要指定更改的事件名字,ui.item是从列表中选中的js对象(我们这里是goods数据对象)。

 

 

完善save.jsp页面代码:

//方案二:-----自定义数据源,从数据库中查询,动态加载数据

        $( "input[name='name']" ).autocomplete({

            //形式参数,不是servletapi

            //request:可以用来获取文本框输入的值,request.term,可以进行异步请求查询,返回data

            //response:用来包装返回的数据的(接受普通数组或json数组),用法将数据放入即可response(data)

//例如:

            //request.term:文本框输入的值

            //response(json数组结果)

         source: function( request, response ) {

                    

                 $.post("${pageContext.request.contextPath}/goods_findByNameLikeAjax.action",{"name":request.term},function(data){

                     //data:json数组

                     response(data);

                 });

                 

         },

         //选择的事件

            select:function(event,ui){

                    //填充其他字段

                    //ui.item是当前选中的js对象,这里是货物goods的js数据内容

                    $("input[name='id']").val(ui.item.id);

                    $("input[name='nm']").val(ui.item.nm);

                    //$("input[name='name']").val(ui.item.name);//可选

                    $("input[name='unit']").val(ui.item.unit);

                    $("#store_id").val(ui.item.store.id);

 

 

            },

        });

这里注意:要填充id的值。

 

【继续优化二】:

在查询前将要填充的字段置空。否则显示的值不对

最终完整代码:

//方案二:-----自定义数据源,从数据库中查询,动态加载数据

        $( "input[name='name']" ).autocomplete({

//形式参数,不是servletapi

            //request:可以用来获取文本框输入的值,request.term,可以进行异步请求查询,返回data

            //response:用来包装返回的数据的(接受普通数组或json数组),用法将数据放入即可response(data)

//例如:

            //request.term:文本框输入的值

            //response(json数组结果)

         source: function( request, response ) {

                 //清除表单

                 $("input[name='nm']").val("");

                    $("input[name='unit']").val("");

                    $("#store_id").val("");

                    //隐藏域

                    $("input[name='id']").val("");

                      

//解禁

                    $("input[name='unit']").removeAttr("disabled");

                $("#store_id").removeAttr("disabled");

                    

                 $.post("${pageContext.request.contextPath}/goods_findByNameLikeAjax.action",{"name":request.term},function(data){

                     //data:json数组

                     response(data);

                 });

                 

         },

         //选择的事件

            select:function(event,ui){

                    //填充其他字段

                    //ui.item是当前选中的js对象,这里是货物goods的js数据内容

                    $("input[name='id']").val(ui.item.id);

                    $("input[name='nm']").val(ui.item.nm);

                    //$("input[name='name']").val(ui.item.name);//可选

                    $("input[name='unit']").val(ui.item.unit);

                    $("#store_id").val(ui.item.store.id);

                      

//禁用表单元素

                    $("input[name='unit']").attr("disabled",true);

                    $("#store_id").attr("disabled",true);

            },

        });

 

 

货物入库功能(服务器端实现 )

要点:

逻辑上:到底是更新还是保存?根据隐藏域的id来判断。 数据操作上:多表插入的(关联属性使用,历史记录)、快照更新

 

分析:提交入库表单,需要实现商品入库逻辑(更新或保存)和操作历史记录

 

第一步:save.jsp页面,表单提交

<s:form action="goods_instore" namespace="/" method="post" name="select">

                    <table width="100%" border="0" cellpadding="0" cellspacing="0" class="tx" align="center">

                        <colgroup>

                            <col width="20%" align="right">

                            <col width="*%" align="left">

                        </colgroup>

                        <tr>

                            <td bgcolor="a0c0c0" style="padding-left:10px;" colspan="2" align="left">

                                <b>货物入库登记:</b>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                简记码:

                            </td>

                            <td>

                                <s:textfield name="nm" cssClass="tx"/>

                                <!-- 隐藏域:一会来识别是更新还是插入:是一个存在货物还是新的货物 -->

                                <s:hidden name="id" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                货物名称:

                            </td>

                            <td>

                                <s:textfield name="name" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                计量单位:

                            </td>

                            <td>

                                <s:textfield name="unit" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                入库数量:

                            </td>

                            <td>

                                <s:textfield name="amount" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                选择仓库:

                            </td>

                            <td>

<!-- name改成store.id,用来使用模型驱动,直接为Goods对象中的store属性的id属性赋值 -->

                                <select class="tx" style="width:120px;" name="store.id" id="store_id">

                                    

                                </select>

                                (此信息从数据库中加载)

                            </td>

                        </tr>

                        <tr>

                            <td colspan="2" align="center" style="padding-top:10px;">

                                <input class="tx" style="width:120px;margin-right:30px;" type="button" onclick="document.forms[0].submit();" value="入库">

                                <input class="tx" style="width:120px;margin-right:30px;" type="reset" value="取消">

                            </td>

                        </tr>

                    </table>

                </s:form>

在save.jsp中,使用ajax对隐藏域id赋值,用来判断执行的是向货物表中存放数据,还是通过简记码更新货物表的数量

//简记码绑定一个离焦事件

        $("input[name='nm']").blur(function(){

            //请求服务器,获取货物信息

            $.post("${pageContext.request.contextPath}/goods_findByNmAjax.action",{"nm":$(this).val()},function(data){

                //data:返回的json对象,一个(简记码和货物是一对一关系)

                if(data ==null){

                    //没有匹配的货物,清除表单

                    $("input[name='name']").val("");

                    $("input[name='unit']").val("");

                    $("#store_id").val("");

                    //隐藏域

                    $("input[name='id']").val("");

                    //解禁

                    $("input[name='name']").removeAttr("disabled");

                    $("input[name='unit']").removeAttr("disabled");

                    $("#store_id").removeAttr("disabled");

                

                }else{

                    //填充

                    $("input[name='name']").val(data.name);

                    $("input[name='unit']").val(data.unit);

                    $("#store_id").val(data.store.id);//从货物关联到仓库

                    //隐藏域

                    $("input[name='id']").val(data.id);

                    

                    //禁用表单元素

                    $("input[name='name']").attr("disabled",true);

                    $("input[name='unit']").attr("disabled",true);

                    $("#store_id").attr("disabled",true);

                }

            });

        });

第二步:在GoodsAction 添加save方法

    //入库操作

    public String instore(){

        //将数据传递给业务层

        goodsService.saveStore(model);

        //从业务角度,继续录入,跳回入库页面

        return "savejsp";

    }

第三步:业务层

(1)IGoodsService代码

/**

     * 入库

     * @param goods

     */

    public void saveStore(Goods goods);

GoodsServiceImpl类代码,操作入库的同时,操作历史,所以需要goodsDao和historyDao

//入库:有两个逻辑,一个插入,一个更新

    public void saveStore(Goods goods) {

        //货物id

        String id = goods.getId();

        //当前登录人

        Userinfo loginUser = ServletUtils.getLoginUserFromSession();

        //根据id判断是插入还是更新

        if(StringUtils.isBlank(id)){

            //插入:字段有没有都填充

            goodsDao.save(goods);//持久态oid,抢占id

            

            //插入历史记录(一般没有更新):瞬时态--》持久态

            History history = new History();

            history.setAmount(goods.getAmount());//操作的数量

            history.setRemain(goods.getAmount());//操作后的剩余数量

            //将其封装常量

//            history.setType("1");//操作类型1:入库,2:出库

            history.setType(DataConstant.OPER_TYPE_IN);//操作类型1:入库,2:出库

            history.setGoods(goods);//货物外键:只要对象有oid即可

            //注意:当前用户没有登录的情况下,空指针。解决方案;登录拦截器

            history.setUser(loginUser.getName());//操作人:一般当前登陆人,

//            history.setDatetime(new Date().toLocaleString());//操作时间

            history.setDatetime(DateUtils.getCurrentDateString());//操作时间

            historyDao.save(history);

            

        }else{

            //更新:

            //hibernate两种方式:update方法(更新所有字段),快照更新(部分字段)

//            goodsDao.update(goods);

            //快照更新

            Goods persistGoods = goodsDao.findById(Goods.class, id);

            //更改一级缓存

            persistGoods.setAmount(persistGoods.getAmount()+goods.getAmount());

            //等待快照更新

            

            //插入历史记录(一般没有更新):瞬时态--》持久态

            History history = new History();

            history.setAmount(goods.getAmount());//操作的数量

            history.setRemain(persistGoods.getAmount());//操作后的剩余数量

//            history.setType("1");//操作类型1:入库,2:出库

            history.setType(DataConstant.OPER_TYPE_IN);//操作类型1:入库,2:出库

            history.setGoods(persistGoods);//货物外键:只要对象有oid即可

            history.setUser(loginUser.getName());//操作人:一般当前登陆人

//            history.setDatetime(new Date().toLocaleString());//操作时间

            history.setDatetime(DateUtils.getCurrentDateString());//操作时间

            historyDao.save(history);

        }

        

    }

第四步:封装优化操作,简化代码的行数,将公用的方法提取出来:

Dataconstant.java,用来封装常量,统一维护常量

 

public class DataConstant {

 

    //入库常量

    public static final String OPER_TYPE_IN="1";

    //出库常量

    public static final String OPER_TYPE_OUT="2";

    

}

 

DateUtils.java,日期类封装,获取当前日期,将日期类型转换成String类型

//操作日期的:

//将日期转换成各种各样的字符串:日期,时间

//网上

public class DateUtils {

    

    //获取到当前日期的字符串表示形式

    public static String getCurrentDateString(){

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        return df.format(new Date());

    }

 

}

 

 

第五步:配置struts.xml 入库后跳转的页面

<!-- 货物管理 -->

        <action name="goods_*" class="goodsAction" method="{1}">

            <!-- json无需配置结果集了 -->

            <!--入库 -->

            <result name="savejsp" type="redirect">/jsps/save/save.jsp</result>

        </action>

第六步:配置applicationContex.xml 在 GoodsService注入HistoryDAO

<!--通用的DAO类 -->

    <bean id="historyDao" class="cn.itcast.storemanager.dao.impl.GenericDaoImpl">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    

    <!-- service -->

    <bean id="goodsService" class="cn.itcast.storemanager.service.impl.GoodsServiceImpl">

        <property name="goodsDao" ref="goodsDao"/>

        <property name="historyDao" ref="historyDao"></property>

    </bean>

注意:

多表的插入(对象状态,持久态对象不能关联瞬时态对象),在多的一端需要关联持久对象 保存或更新的判断(使用页面的隐藏域id) 一个Service可以多个dao的注入

 

 

库存管理功能

分页数据Bean的设计思路

分析几种查询(从查询的角度来说):

条件查询:多个条件组合查询,需要将用户输入的条件,根据判断,拼接条件(sql:where条件的SQL语句。qbc:离线条件拼装) 分页查询:需要得到要查询记录的索引和要查询的条数(mysql:limit 起始索引,最大记录数); 或者需要得到查询记录的起始和结束的行数(Oracle)。 条件查询+分页查询:需要在分页查询过程中记录原来的查询条件(url?+条件),原理见下图:

关键点(设计思想):查询条件(业务条件和分页条件)在客户端和服务器端来回传递。--封装一个bean对象(分页bean)用于存储条件。 和返回结果使用。

 

分页数据Bean的设计实现

第一步:库存管理,修改main.jsp

<s:a action="goods_listPage" namespace="/" target="content" id="left1002" >

                                            [库存管理]

</s:a>

 

第二步:改造页面(form部分):

jsps/store/remain.jsp

(1)修改标签

<s:form action="goods_listPage" namespace="/" method="post" name="select">

                    <table width="100%" border="0" cellpadding="0" cellspacing="0" class="tx" align="center">

                        <colgroup>

                            <col width="20%" align="right">

                            <col width="*%" align="left">

                        </colgroup>

                        <tr>

                            <td bgcolor="a0c0c0" style="padding-left:10px;" colspan="2" align="left">

                                <b>查询条件:</b>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                简记码:

                            </td>

                            <td>

                                <s:textfield name="nm" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                货物名称:

                            </td>

                            <td>

                                <s:textfield name="name" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                选择仓库:

                            </td>

                            <td>

                                <select class="tx" style="width:120px;" name="store.id" id="store_id">

                                    <option value="">--请选择--</option>

                                </select>

                            </td>

                        </tr>

                        <tr>

                            <td colspan="2" align="right" style="padding-top:10px;">

                                <input class="tx" style="width:120px;margin-right:30px;" type="button" onclick="document.forms[0].submit();" value="查询">

                            </td>

                        </tr>

                    </table>

                </s:form>

添加ajax

<!-- 引入jquery库 -->

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.8.3.js"></script>

<script type="text/javascript">

    $(function(){

        //$.post(请求url,请求参数,回调函数,返回的类型);

        $.post("${pageContext.request.contextPath}/store_listAjax.action",function(data){

            //data:转换后的对象:json对象数组-dom对象

            $(data).each(function(){

                //this每一个对象json

                //this.id

                var option=$("<option value='"+this.id+"'>"+this.name+"</option>");

                //添加到下拉列表中

                $("#store_id").append(option);

            });

//回显下拉选择,手动给select列表选中一个选中的值,从隐藏域中获取选中的值。

            //alert($("#storeId").val());

            $("#store_id").val($("#storeId").val());

        });

        

    

    });

 

</script>

 

(3)通过传递store.id用来完成下拉框的回显

<table border="0" class="tx" width="100%">

        <tr>

            <td>当前位置&gt;&gt;首页&gt;&gt;货物库存</td>

            <input type="hidden" id="storeId" value="${store.id }">

        </tr>

    </table>

 

 

 

 

 

第三步:设计服务器分页查询数据Bean

新建一个包cn.itcast.storemanager.page,创建类Pagination.java存放数据分页Bean。

 

思考:

Pagination要通用,所以引入泛型。

//分页bean

//封装请求和响应的相关数据

// * 业务层:要:业务条件+分页条件

// 业务层:提供:数据列表+分页结果(总记录数)

public class Pagination<T> {

 

}

 

传递参数:客户端会传过来哪些条件?

当前页:查询第几页,比如第二页,如果第一次,默认应该是第一页。 每页最多显示的记录数:该值可以是用户自定义的,也可以是系统默认的,我们这里默认为3。 查询的业务条件:用户在页面填写的参数条件,可以是单条件,也可以是组合的多条件。

 

返回数据:服务端可以返回哪些数据呢?

数据列表:根据客户端传过来的组合条件和页码查询出的数据list。 总页数:给客户端显示有多少页,或者根据这个总页数来判断客户端实际要显示多少个分页数字按钮。 总的记录数:可以给客户端显示,也用来计算总页数。

// 分页bean

// 封装请求和响应的相关数据

// 业务层:要:业务条件+分页条件

// 业务层:提供:数据列表+分页结果(总记录数)

public class Pagination<T> {

    //--------请求的条件

    private int page = 1;//当前页码,默认第一次页码是1

    private int pageSize = 3;//每页最大记录数,我们的这个业务没有,默认给3

    //业务条件

//    private T t;//页面查询的业务条件,经常可能不是数据库的字段(实体类的属性

// 获取所有参数的方法ServletActionContext.getRequest().getParameterMap()

    private Map<String, String[]> parameterMap;//最灵活,可以封装任何的参数条件

    

    //--------响应的结果

    //不管用什么技术,所有的分页组合条件查询,都需要查询两次:一次总记录数,一次查询数据列表

    //结果数据列表

    private List<T> resultList;//数据库查询的结果集列表

    private long totalCount;//总记录数,数据库查询的总记录数

    private long totalPage;//总页码数(计算出来的,不是查询出来)

 

}

提示:需要添加getter和setter方法。

分析:

一般参数条件用map更灵活,可以放置任何条件。泛型参数类型的设计根据request.getParameterMap来设计,后台通过这个方法取到的数据直接封装到这里就行了。

 

代码完善:

 

计算总页数:

总页数=(总的记录数+每页最多显示的记录数-1)/每页最多显示的记录数。

public void setTotalCount(long totalCount) {

        //计算一些东西

        //计算总页码数:

        totalPage=(totalCount+pageSize-1)/pageSize;

        

        //计算页面的页码中"显示"的起始页码和结束页码

        //一般显示的页码叫好的效果是最多显示10个页码

        //算法是前5后4,不足补10

        //计算显示的起始页码(根据当前页码计算):当前页码-5

        begin = page-5;

        if(begin<1){//页码修复

            begin=1;

        }

        //计算显示的结束页码(根据开始页码计算):开始页码+9

        end=begin+9;

        if(end>totalPage){//页码修复

            end=totalPage;

        }

        //起始页面重新计算(根据结束页码计算):结束页码-9

        begin=end-9;

        if(begin<1){

            begin=1;

        }

        System.out.println(begin +"和" +end);

 

        this.totalCount = totalCount;

    }

每页第一条记录的索引:(新增方法)

//设置两个getter属性

    //起始索引

    public int getFirstResult(){

        //计算起始索引

        return (page-1)*pageSize;

    }

每页要查询的最大记录数:(新增方法)

//最大记录数

    public int getMaxResults(){

        return pageSize;

    }

 

第四步:编写Action代码,准备分页参数

在GoodsAction 中添加listPage方法

//分页列表综合查询

    //思路:

    /*

     * 将业务条件和分页条件都封装到一个分页bean中,

     * 将分页bean传递给业务层,处理逻辑(拼接sql,调用dao查询数据)

     * 业务层:要:业务条件+分页条件

     * 业务层:提供:数据列表+分页结果(总记录数)

     * 所有数据,全部都封装分页bean中。

     * 表现层:将条件装到bean,将已经有结果的bean,给页面(值栈),在页面显示

     *

     *

     */

    public String listPage(){

        //1.将条件装到分页bean中

        //实例化一个分页bean

        Pagination<Goods> pagination = new Pagination<Goods>();

        //封装业务条件

        pagination.setParameterMap(ServletActionContext.getRequest().getParameterMap());

        

        //封装页码和最大记录数?page=2&pageSize=3

        //思路:获取这两个参数:1。可以从参数中直接获取2。使用struts值栈来封装数据

        //获取值可以使用:方案一:ServletActionContext.getRequest().getParameter("page");

        //获取值可以使用:方案二:struts2的属性驱动

        //如果第一次查询,则会出现page为0的情况

        if(page>0){

            pagination.setPage(page);

        }

        if(pageSize>=1){

            pagination.setPageSize(pageSize);

        }

        

        //2.将分页bean传递给业务层,注意:业务层查询出来的数据,再封装回分页bean

        //对象引用的知识点

        //pagination=goodsService.findGoodsListPage(pagination);

        goodsService.findGoodsListPage(pagination);

        

        //3.将分页bean

        //放入栈顶

        result =pagenation ;    //action的属性

        //跳转到列表页面

        return "remainjsp";

        

        

    }

    

    //值栈封装属性的值,使用属性驱动获取页面传递的当前页(page)和当前页最多存放的记录数(pageSize)

    //action的初始化的,struts拦截器会自动调用同名属性(page--->SetPage(2))

    private int page;//int默认是0

    private int pageSize;

    public void setPage(int page) {

        this.page = page;

    }

    public void setPageSize(int pageSize) {

        this.pageSize = pageSize;

    }

 

 

分析:Action类使用Pagination用来传递参数和获取返回结果

引用传值Pagination

 

 

第五步:分页代码Service、Dao实现 (方案一:先使用QBC查询)

分析:

任何分页查询技术在Dao层都必须查询两次:即:当前页的数据和总的记录数

因此,我们的业务层service需要调用两次dao,dao中需要对应两个方法来满足需要(查总记录数和查当前页数据)。

 

(1)接口IGoodsService 代码

/**

     * 分页条件综合查询

     * @param pagination

     */

    public void findGoodsListPage(Pagination<Goods> pagination);

 

(2)实现类GoodsServiceImpl类代码

//分页条件综合查询:

    //从分页bean中取出条件,拼接sql条件,调用dao查询数据库,返回结果,封装回分页bean

    public void findGoodsListPage(Pagination<Goods> pagination) {

        //1.....条件的取出和拼接

        //QBC方案

        DetachedCriteria criteria = DetachedCriteria.forClass(Goods.class);//根查询,主查询对象

        

        //先获取业务条件

        Map<String, String[]> parameterMap = pagination.getParameterMap();

        //添加业务条件:每个业务都不一样

        //简记码

//        String nm=parameterMap.get("nm")==null?null:parameterMap.get("nm")[0];

        String nm = getValueFromParameterMap(parameterMap, "nm");

        if(StringUtils.isNotBlank(nm)){

            criteria.add(Restrictions.eq("nm", nm));

        }

        //货物名称

        String name = getValueFromParameterMap(parameterMap, "name");

        if(StringUtils.isNotBlank(name)){

            criteria.add(Restrictions.like("name","%"+name+"%"));

        }

        

        //仓库:注意参数名称,

        String storeId = getValueFromParameterMap(parameterMap, "store.id");

        if(StringUtils.isNotBlank(storeId)){

            //外键的条件---单表

            //两种方式

            //1.直接指定关联对象的id--外键:底层原理是第二种方式

            criteria.add(Restrictions.eq("store.id", storeId));

            

            //2。直接传对象,自动将对象主键作为外键

//            Store store = new Store();

//            store.setId(storeId);

//            criteria.add(Restrictions.eq("store", store));

        }

        

        findListPage(pagination, criteria,goodsDao);        

        

    }

 

在BaseService类中,抽出来的取参数值方法:

//业务层的父类:用来复用公用代码

//抽象类

public abstract class BaseService<T,ID extends Serializable> {

 

    //从参数map中获取参数值

    protected String getValueFromParameterMap(Map<String, String[]> parameterMap,String key){

        return parameterMap.get(key)==null?null:parameterMap.get(key)[0];

    }

    

    //分页条件查询

    protected void findListPage(Pagination<T> pagination,

            DetachedCriteria criteria,IGenericDao<T, ID> dao) {

        //2.....调用dao查询数据和结果的封装

        //2.1查询总记录数

        long totalCount = dao.findCountByCriteria(criteria);

        //直接封装到分页bean

        pagination.setTotalCount(totalCount);

          

        

        //2.2计算后,查询数据列表(分页查询)

//            int firstResult=(pagination.getPage()-1)*pagination.getPageSize();//属于分页的业务,在分页bean中计算

        //分析:结果集封装策略发生了变化,要做:重置结果集封装策略

        //将投影设置为null

        criteria.setProjection(null);

        //将封装策略手动更改为ROOT_ENTITY

        criteria.setResultTransformer(criteria.ROOT_ENTITY);

        

        List<T> resultList = dao.findListPageByCriteria(criteria, pagination.getFirstResult(), pagination.getMaxResults());

        //直接封装到分页bean中

        pagination.setResultList(resultList);

    }

}

 

第六步:分页代码Service、Dao实现 (方案一:先使用QBC查询)

 

(1)修改IGenericDao接口:添加两个方法

/**

     * 查询记录数

     * @param criteria

     * @return

     */

    public long findCountByCriteria(DetachedCriteria criteria);

    

    /**

     * 条件分页查询列表

     * @param criteria

     * @param firstResult

     * @param maxResults

     * @return

     */

    public List<T> findListPageByCriteria(DetachedCriteria criteria, int firstResult, int maxResults);

    

 

实现类GenericDaoImpl类

//查询总记录数

    public long findCountByCriteria(DetachedCriteria criteria) {

        //添加记录数查询的投影

        //投影查询,会改变默认的结果集封装策略为PROJECTION,而原来默认是ROOT_ENTITY

        criteria.setProjection(Projections.rowCount());

        //只能有一个元素:记录数

        List<Long> list = getHibernateTemplate().findByCriteria(criteria);

        return list.isEmpty()?0:list.get(0);

    }

 

    //查询当前分页的数据集合

    public List<T> findListPageByCriteria(DetachedCriteria criteria,

            int firstResult, int maxResults) {

        return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);

    }

 

第七步:struts.xml文件的配置:

<!-- 货物管理 -->

        <action name="goods_*" class="goodsAction" method="{1}">

            <!-- json无需配置结果集了 -->

            <!--入库 -->

            <result name="savejsp" type="redirect">/jsps/save/save.jsp</result>

            <!-- 列表页面 -->

            <result name="remainjsp">/jsps/store/remain.jsp</result>

        </action>

编写JSP显示分页查询结果数据

分两部分实现:

分页数据列表的显示 分页工具条的显示

表格分页数据显示

在remain.jsp中遍历结果集

<table class="store">

                                    <tr style="background:#D2E9FF;text-align: center;">

                                        <td>简记码</td>

                                        <td>名称</td>

                                        <td>计量单位</td>

                                        <td>库存数量</td>

                                        <td>所在仓库</td>

                                        <td>操作</td>        

                                    </tr>

                                    

                                    <s:iterator value="result.resultList" >

                                        <tr>

                                            <td><s:property value="nm"/> </td>

                                            <td><s:property value="name"/></td>

                                            <td><s:property value="unit"/></td>

                                            <td><s:property value="amount"/></td>

                                            <td><s:property value="store.name"/></td>    

                                            <td>

                                                <a href="<c:url value='/jsps/save/save.jsp'/>">入库</a>

                                                <a href="<c:url value='/jsps/out/out.jsp'/>">出库</a>

                                                <a href="<c:url value='/jsps/his/his.jsp'/>">历史记录</a>

                                            </td>        

                                        </tr>

                                    </s:iterator>

                                    

                                </table>

 

 

测试:数据是否能正常显示。

 

 

分页流程梳理 :

页面提交请求 (页码、 每页记录数、 查询条件 ) 设计分页数据Bean 接收参数 Pagination 编写Action,将Pagination传递给Service业务层,让业务层进行查询和封装Pagination(引用传递,不需要返回)。 业务层service从Pagination中拿到请求参数,封装DetachedCriteria或拼接SQL语句,进行查询出总记录数和当页的数据集合,再将结果封装回Pagination。 此时,数据层Dao对应两次查询(总记录数、 当前页数据 ) 将查询结果传递JSP页面 (表格数据显示、 分页工具条显示 )

 

分页工具条显示(了解)

页码效果分析:(前五后四)

第一步:在Pagination.java中定义分页的逻辑

 

(1)在Pagination 添加开始页码begin和结束页码end:

//工具条使用的结果

    private long begin;//起始页码数

    private long end;//结束的页码数

        

    public long getBegin() {

        return begin;

    }

    public void setBegin(long begin) {

        this.begin = begin;

    }

    public long getEnd() {

        return end;

    }

    public void setEnd(long end) {

        this.end = end;

    }

在Pagination.java中找个位置计算获取begin和end的值:

 

public void setTotalCount(long totalCount) {

        //计算一些东西

        //计算总页码数:

        totalPage=(totalCount+pageSize-1)/pageSize;

        

        //计算页面的页码中"显示"的起始页码和结束页码

        //一般显示的页码叫好的效果是最多显示10个页码

        //算法是前5后4,不足补10

        //计算显示的起始页码(根据当前页码计算):当前页码-5

        begin = page-5;

        if(begin<1){//页码修复

            begin=1;

        }

        //计算显示的结束页码(根据开始页码计算):开始页码+9

        end=begin+9;

        if(end>totalPage){//页码修复

            end=totalPage;

        }

        //起始页面重新计算(根据结束页码计算):结束页码-9

        begin=end-9;

        if(begin<1){

            begin=1;

        }

        System.out.println(begin +"和" +end);

 

        this.totalCount = totalCount;

    }

 

在Pagination.java中添加计算页面获取查询条件参数字符串方法,点击首页、上一页、下一页、末页、具体页需要传递查询条件。

//获取条件的字符串标识形式:map解析为字符串&name=xxx&age=xxx

    public String getParameterStr(){

        String paramterStr="";

        Set<String> keySet = parameterMap.keySet();

        for (String key : keySet) {

            //排除page和pageSize参数

            if(!key.equals("page")&&!key.equals("pageSize")){

                String[] values = parameterMap.get(key);

                if(values!=null &&StringUtils.isNotBlank(values[0])){

                    paramterStr+="&"+key+"="+values[0];

                }

            }

        }

        

        return paramterStr;

    }

 

 

页面显示分页工具条

第二步:在remain.jsp中添加分页条,由于分页操作属于超链接,需要使用Get请求的方式传递值

<div align="right">

                        <!-- 显示页码 -->

                        <!-- 首页和上一页

                        当前页码是否大于1显示

                         -->

                        <s:if test="result.page>1">

                            <a href="${pageContext.request.contextPath }/goods_listPage.action?page=1${result.parameterStr}">首页</a>

                            <a href="${pageContext.request.contextPath }/goods_listPage.action?page=${result.page-1}${result.parameterStr}">上一页</a>

                        </s:if>

                        <!-- 中间页码 -->

                        <s:iterator begin="result.begin" end="result.end" var="num">

                            <a href="${pageContext.request.contextPath }/goods_listPage.action?page=${num}${result.parameterStr}">[${num}]</a>

                        </s:iterator>

                        <!-- 下一页和尾页 -->

                        <s:if test="result.page<result.totalPage">

                            <a href="${pageContext.request.contextPath }/goods_listPage.action?page=${result.page+1}${result.parameterStr}">下一页</a>

                            <a href="${pageContext.request.contextPath }/goods_listPage.action?page=${result.totalPage}${result.parameterStr}">尾页</a>

                        </s:if>

                        <input type="text" size="2" name="page"/>

                        <input type="button" value="go" size="2" />

                    </div>

 

在remain.jsp中,代码完善和效果优化:中间页面的显示添加样式

<!-- 中间页码遍历 -->

                        <s:iterator begin="result.begin" end="result.end" var="num">

                            <a href="${pageContext.request.contextPath }/goods_listPage.action?page=${num}${result.parameterStr}">

                                <%--[${num}]--%>

                                <s:if test="#num==1">

                                    <span style="color:red">[${num}]</span>

                                </s:if>

                                <s:else>

                                    <span style="color:blue">[${num}]</span>

                                </s:else>

                            </a>

                        </s:iterator>

 

第三步:查询条件乱码问题:

输入货物名称,会产生乱码

原因:因为页码上请求参数使用 ?拼接,中文会使用get方式提交,参数会出现中文乱码。

解决方案:

(1) 修改或新增tomcat中的server.xml 的编码为:URIEncoding="UTF-8"

原因:tomcat的编码iso8859-1,导致从tomcat获取数据的时候,编码不一致(程序用utf-8)

tomcat8:底层编码已经变成utf-8,但是我们使用的是tomcat7

 

(2) 手动编码, GenericEncodingFilter (过滤器可参考课前资料)

GenericEncodingFilter.java 代码:

/**

* 解决get和post请求 全部乱码

*

*/

public class GenericEncodingFilter implements Filter {

 

    

    public void doFilter(ServletRequest request, ServletResponse response,

            FilterChain chain) throws IOException, ServletException {

        // 转型为与协议相关对象

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        // 对request包装增强

        HttpServletRequest myrequest = new MyRequest(httpServletRequest);

        chain.doFilter(myrequest, response);

    }

 

    public void init(FilterConfig filterConfig) throws ServletException {

        

    }

 

    public void destroy() {

    

    }

}

 

// 自定义request对象

class MyRequest extends HttpServletRequestWrapper {

 

    private HttpServletRequest request;

 

    private boolean hasEncode;

 

    public MyRequest(HttpServletRequest request) {

        super(request);// super必须写

        this.request = request;

    }

 

    // 对需要增强方法 进行覆盖

    public Map getParameterMap() {

        // 先获得请求方式

        String method = request.getMethod();

        if (method.equalsIgnoreCase("post")) {

            // post请求

            try {

                // 处理post乱码

                request.setCharacterEncoding("utf-8");

                return request.getParameterMap();

            } catch (UnsupportedEncodingException e) {

                e.printStackTrace();

            }

        } else if (method.equalsIgnoreCase("get")) {

            // get请求

            Map<String, String[]> parameterMap = request.getParameterMap();

            if (!hasEncode) { // 确保get手动编码逻辑只运行一次

                for (String parameterName : parameterMap.keySet()) {

                    String[] values = parameterMap.get(parameterName);

                    if (values != null) {

                        for (int i = 0; i < values.length; i++) {

                            try {

                                // 处理get乱码

                                values[i] = new String(values[i]

                                        .getBytes("ISO-8859-1"), "utf-8");

                            } catch (UnsupportedEncodingException e) {

                                e.printStackTrace();

                            }

                        }

                    }

                }

                hasEncode = true;

            }

            return parameterMap;

        }

 

        return super.getParameterMap();

    }

 

    @Override

    public String getParameter(String name) {

        Map<String, String[]> parameterMap = getParameterMap();

        String[] values = parameterMap.get(name);

        if (values == null) {

            return null;

        }

        return values[0]; // 取回参数的第一个值

    }

 

    public String[] getParameterValues(String name) {

        Map<String, String[]> parameterMap = getParameterMap();

        String[] values = parameterMap.get(name);

        return values;

    }

 

}

在web.xml设置过滤器的配置

<!-- 乱码过滤器 -->

    <filter>

        <filter-name>GenericEncodingFilter</filter-name>

        <filter-class>cn.itcast.storemanager.web.filter.GenericEncodingFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>GenericEncodingFilter</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

注意:乱码过滤器要往前面放,放置到struts2的过滤器的前面。

注意:tomcat编码和过滤器编码两者不要同时使用。

 

分页逻辑小结

 

讲解两个问题:

 

(1)分页逻辑梳理:

 

 

 

(2)服务器端 分页代码逻辑

 

使用SQL拼接方式,实现库存查询(课后)

目标:将service和dao中增加或修改sql方式的实现代码。

 

(1)修改:业务Service实现层:

(2)通用数据层IGenericDao接口:

通用GenericDaoImpl类的实现

【1】查询总记录数

【2】使用sql语句查询符合条件的分页记录

分页代码重构优化

重构优化的思路:

Action封装分页相关的请求参数 PaginationBean ----> 抽取BaseAction类 Service 将参数封装DetachedCriter/ SQL ----> 不能优化 (根据业务) Service调用Dao 查询totalCount 和 pageData ----> 抽取BaseService类 Dao 查询totalCount和 pageData 代码就是通用代码---->GenericDaoImpl类

 

Action的数据封装

【1】BaseAction类:

//action的父类:用来存放action的重复代码的

//通用:泛型

public abstract class BaseAction<T> extends ActionSupport implements ModelDriven<T>{

    //实例化数据模型T,模型驱动必须实例化

//    private T t = new T();

    //子类可见

    protected T model;//没有初始化

 

    public T getModel() {

        return model;

    }

    

    //在默认的构造器初始化数据模型

    public BaseAction() {

        //在子类初始化的时候,默认会调用父类的构造器

        //反射机制:获取具体的类型

        //得到带有泛型的类型,如BaseAction<Userinfo>

        Type superclass = this.getClass().getGenericSuperclass();

        //转换为参数化类型

        ParameterizedType parameterizedType = (ParameterizedType) superclass;

        //获取泛型的第一个参数的类型类,如Userinfo

        Class<T> modelClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];

        

        //实例化数据模型类型

        try {

            model = modelClass.newInstance();

        } catch (InstantiationException e) {

            e.printStackTrace();

        } catch (IllegalAccessException e) {

            e.printStackTrace();

        }

 

    }

    

    //封装值栈的操作的方法

    //root栈:栈顶map,可以通过key获取value

    protected void setToValueStackRoot(String key,Object value){

        ActionContext.getContext().getValueStack().set(key, value);

    }

    

    //root栈:栈顶对象(匿名)

    protected void pushToValueStackRoot(Object value){

        ActionContext.getContext().getValueStack().push(value);

    }

    //map栈:--推荐,可以通过key获取value

    protected void putToValueStackMap(String key,Object value){

        ActionContext.getContext().put(key, value);

    }

    

    //root栈:action的属性

    protected Object result;

    public Object getResult() {//action在root栈,因此,result也在root栈

        return result;

    }

}

 

Service 代码

将通用分页查询代码,抽取BaseService

//业务层的父类:用来复用公用代码

//抽象类

public abstract class BaseService<T,ID extends Serializable> {

 

    //从参数map中获取参数值

    protected String getValueFromParameterMap(Map<String, String[]> parameterMap,String key){

        return parameterMap.get(key)==null?null:parameterMap.get(key)[0];

    }

    

    //分页条件查询

    protected void findListPage(Pagination<T> pagination,

            DetachedCriteria criteria,IGenericDao<T, ID> dao) {

        //2.....调用dao查询数据和结果的封装

        //2.1查询总记录数

        long totalCount = dao.findCountByCriteria(criteria);

        //直接封装到分页bean

        pagination.setTotalCount(totalCount);

          

        

        //2.2计算后,查询数据列表(分页查询)

//            int firstResult=(pagination.getPage()-1)*pagination.getPageSize();//属于分页的业务,在分页bean中计算

        //分析:结果集封装策略发生了变化,要做:重置结果集封装策略

        //将投影设置为null

        criteria.setProjection(null);

        //将封装策略手动更改为ROOT_ENTITY

        criteria.setResultTransformer(criteria.ROOT_ENTITY);

        

        List<T> resultList = dao.findListPageByCriteria(criteria, pagination.getFirstResult(), pagination.getMaxResults());

        //直接封装到分页bean中

        pagination.setResultList(resultList);

    }

}

 

3:Dao代码,封装了操作数据库通用的CRUD方法

//通用dao的实现

//hibernate模版类操作,继承daosupport

public class GenericDaoImpl<T,ID extends Serializable> extends HibernateDaoSupport implements IGenericDao<T, ID> {

 

    

    //保存对象

    public void save(Object domain) {

        getHibernateTemplate().save(domain);

    }

 

    //修改对象

    public void update(Object domain) {

        getHibernateTemplate().update(domain);

    }

 

    //删除对象

    public void delete(Object domain) {

        getHibernateTemplate().delete(domain);

    }

 

    

    //使用主键ID查询

    public T findById(Class<T> domainClass, ID id) {

        return getHibernateTemplate().get(domainClass, id);

    }

 

    

    //查询所有

    public List<T> findAll(Class<T> domainClass) {

        return getHibernateTemplate().loadAll(domainClass);

    }

 

    //命名查询

    public List<T> findByNamedQuery(String queryName, Object... values) {

        return getHibernateTemplate().findByNamedQuery(queryName, values);

    }

 

    //QBC

    public List<T> findByCriteria(DetachedCriteria criteria) {

        return getHibernateTemplate().findByCriteria(criteria);

    }

    

    //查询总记录数

    public long findCountByCriteria(DetachedCriteria criteria) {

        //添加记录数查询的投影

        //投影查询,会改变默认的结果集封装策略为PROJECTION,而原来默认是ROOT_ENTITY

        criteria.setProjection(Projections.rowCount());

        //只能有一个元素:记录数

        List<Long> list = getHibernateTemplate().findByCriteria(criteria);

        return list.isEmpty()?0:list.get(0);

    }

 

    //查询当前分页的数据集合

    public List<T> findListPageByCriteria(DetachedCriteria criteria,

            int firstResult, int maxResults) {

        return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);

    }

 

}

 

公共代码封装打包(了解)

企业开发小技巧(多学一招):

一般,企业中会将核心的或公共代码进行打包封装,然后让其他项目去引用(保护源码)。

目标:将公共代码 ,生成jar包, 后期直接导入去使用

 

准备工作:先将未打包的代码打包一份备份。

 

新建java项目storemanager-core,将原来项目中与业务无关的公用代码和依赖的jar都copy到这个工程中。

 

现在开始打包:

(1)选择项目,右键,点击Export。

(2)选择JAR file

配置JAR Export

导出的jar:在E盘目录下,发现storemanager-core.jar

原项目不需要这么多代码 ,只需要引用基础包

复制一个storemanager项目,命名为storemanager-new,删除公用的代码,即刚刚抽取的代码

发现报错了,我们导入storemanager-core.jar包

没有错误了,我们尝试发布一下!右键项目,点击Web,修改Web Context-root为storemanager-new,表示我们发布的项目为storemanager-new

访问页面:http://localhost:8080/storemanager-new

最后,访问页面,进行测试,一切OK。

【问题】

如何查询源代码呢?

重复刚才的步骤:导出源码包

(1)选择项目,右键,点击Export。

(2)选择JAR file

配置JAR Export(选择第三个,Export Java Source files and resources)

导出的源码jar:在E盘目录下,发现storemanager-core-resource.jar

测试,可以关联代码。

 

 

重点:

第一天的所有内容(必须) 第二天上午内容(必须) 分页(会写即可) 理解如何编程!(开发流程、代码抽取、复用、重构)

转载于:https://www.cnblogs.com/beyondcj/p/6271066.html

相关资源:SSH仓库管理系统
最新回复(0)