鸡啄米 - 注册送白菜网http://www.teaching4real.com/关注互联网、数码、软件开发和编程博一把白菜论坛手机的IT博客RainbowSoft Studio Z-Blog 1.8 Walle Build 100427zh-CNCopyright © 2011-2017 鸡啄米. 版权所有.Wed, 07 Mar 2018 08:53:09 +0800SSH电商项目实战之十:最新送白菜网类基本模块的搭建a@b.com (鸡啄米)http://www.jizhuomi.com/software/770.htmlWed, 07 Mar 2018 08:41:55 +0800http://www.jizhuomi.com/software/770.html      前面我们完成了与最新送白菜网类别相关的业务逻辑,接下来我们开始做具体最新送白菜网部分。

  1. 数据库建表并映射Model

  首先我们在数据库中新建一张表,然后使用逆向工程将表映射成Model类,表如下:

SQL代码
  1. /*=============================*/    
  2. /* Table: 最新送白菜网表结构            */    
  3. /*=============================*/    
  4. create table product    
  5. (    
  6.    /* 最新送白菜网编号,自动增长 */    
  7.    id                  int primary key not null auto_increment,    
  8.    /* 最新送白菜网名称 */    
  9.    name                varchar(20),    
  10.    /* 最新送白菜网价格 */    
  11.    price               decimal(8,2),    
  12.    /* 最新送白菜网图片 */    
  13.    pic                 varchar(200),    
  14.    /* 最新送白菜网简单介绍 */    
  15.    remark              longtext,    
  16.    /* 最新送白菜网详细介绍 */    
  17.    xremark             longtext,    
  18.    /* 最新送白菜网生产日期 */    
  19.    date                timestamp default CURRENT_TIMESTAMP,    
  20.    /* 是否为推荐最新送白菜网,推荐最新送白菜网才有可能显示在商城首页 */    
  21.    commend             bool,    
  22.    /* 是否为有效最新送白菜网,有效最新送白菜网才有可能显示在商城首页 */    
  23.    open                bool,    
  24.    /* 最新送白菜网所在的类别编号*/    
  25.    cid                  int,    
  26.    constraint cid_FK foreign key(cid) references category(id)    
  27. );    

  使用逆向工程映射为Model类就不赘述了,前面有提到如何使用逆向工程生成Model。

  2. 完成最新送白菜网类的Service层和Action的架构

  2.1 最新送白菜网类的Service层架构

  与前面category一样,product也得有个service来操作与最新送白菜网相关的业务逻辑,所以我们得写一个ProductService和ProductServiceImpl的架构出来,具体如下:

Java代码
  1. //ProductService接口继承BaseService<Product>    
  2. public interface ProductService extends BaseService<Product> {    
  3.         
  4. }    
  5.     
  6. //ProductServiceImpl实现类继承BaseServiceImpl<Product>,并实现上面的ProductService接口    
  7. @Service("productService")    
  8. public class ProductServiceImpl extends BaseServiceImpl<Product> implements ProductService {    
  9.     
  10. }    

  2.2 最新送白菜网类的Action架构

  首先得完善一下BaseAction中关于Service层的注解

Java代码
  1. @Controller("baseAction")    
  2. @Scope("prototype")    
  3. public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {    
  4.     
  5.     @Resource    
  6.     protected ProductService productService;    
  7.     
  8.         //其他代码省略,还是原来的代码……      
  9. }  

  然后我们写一个ProductAction继承该方法:

Java代码
  1. public class ProductAction extends BaseAction<Product> {    
  2.         
  3. }    

  至此,关于最新送白菜网的后台架构就基本搭建好了,接下来就是完善里面的具体功能和业务逻辑了。

  3. 完成前台的基本结构

  前台的基本结构和最新送白菜网类的一样,我们看一下已经完成的最新送白菜网类的前台都有哪些文件:

SSH电商项目实战之十:最新送白菜网类基本模块的搭建

  我们先根据其最新送白菜网类的前台文件,拷贝一份到product文件夹中,然后我们再做相应的修改。先来分析一下流程:首先index.jsp到aindex.jsp显示左侧菜单栏,当点击类别管理时,进入category/query.jsp页面右侧显示所有最新送白菜网类别信息,搜索和删除功能均在此页面,不需要弹出新的窗口,添加弹出save.jsp窗口,更新弹出update.jsp窗口。当点击最新送白菜网管理的时候,进入product/query.jsp页面右侧显示所有最新送白菜网信息,搜索和删除功能均在此页面完成,添加和更新分别弹出save.jsp和update.jsp。接下来我们把各个页面的框架搭建好,然后往相应的部分填东西即可。

  首先在aindex.jsp中添加如下代码:

SSH电商项目实战之十:最新送白菜网类基本模块的搭建

  接下来,我们完成query.jsp的框架:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <style type="text/css">    
  7.         body {    
  8.             margin: 1px;    
  9.         }    
  10.         .searchbox {    
  11.           margin: -3;    
  12.         }    
  13.     </style>    
  14.     <script type="text/javascript">    
  15.         $(function(){    
  16.             $('#dg').datagrid({       
  17.                 //url地址改为请求productAction中的queryJoinCategory方法    
  18.                 url:'product_queryJoinCategory.action',    
  19.                 loadMsg:'Loading......',    
  20.                 queryParams:{name:''},//这里参数改成name,参数值为空,表示我们要显示所有最新送白菜网,后台是根据最新送白菜网name属性查询的    
  21.                 //width:300,    
  22.                 fitColumns:true,    
  23.                 striped:true,    
  24.                 nowrap:true,    
  25.                 singleSelect:false,    
  26.                 pagination:true,    
  27.                 pageSize:5,    
  28.                 pageList:[5,10,15,20],    
  29.                 idField:'id',//指定id为标识字段,在删除,更新的时候有用,如果配置此字段,在翻页时,换页不会影响选中的项    
  30.                     
  31.                 //toolbar定义添加、删除、更新按钮以及搜索框    
  32.                 toolbar: [{    
  33.                     iconCls: 'icon-add',    
  34.                     text:'添加最新送白菜网',    
  35.                     handler: function(){    
  36.                         //添加触发代码    
  37.                     }    
  38.                  },'-',{    
  39.                     iconCls: 'icon-edit',    
  40.                     text:'更新最新送白菜网',    
  41.                     handler: function(){    
  42.                                             //添加触发代码    
  43.                     }    
  44.                  },'-',{    
  45.                     iconCls: 'icon-remove',    
  46.                      text:'删除最新送白菜网',    
  47.                     handler: function(){    
  48.                         //添加触发代码                        
  49.                     }    
  50.                 },'-',{ //查询按钮不是LinkButton,它有语法,但是也支持解析HTML标签    
  51.                     text:"<input id='ss' name='serach' />"    
  52.                 }],    
  53.                 rowStyler: function(index,row){    
  54.                     console.info("index" + index + "," + row)    
  55.                     if(index % 2 == 0) {    
  56.                         return 'background-color:#fff;';    
  57.                     } else {    
  58.                         return 'background-color:#c4e1e1;';    
  59.                     }    
  60.                         
  61.                  },    
  62.                 frozenColumns:[[    
  63.                      {field:'checkbox',checkbox:true},    
  64.                     {field:'id',title:'最新送白菜网编号',width:100}       
  65.                  ]],    
  66.                 columns:[[                         
  67.                     {field:'name',title:'最新送白菜网名称',width:100},        
  68.                     {field:'price',title:'最新送白菜网价格',width:100},    
  69.                     {field:'remark',title:'简单描述',width:100},    
  70.                     {field:'xremark',title:'详细描述',width:100},    
  71.                     {field:'date',title:'上架时间',width:100},    
  72.                     {field:'commend',title:'推荐最新送白菜网',width:100,      
  73.                         formatter: function(value,row,index){    
  74.                             if(value) {    
  75.                                 return "<input type='checkbox' checked='checked' disabled='true'";    
  76.                             } else {    
  77.                                 return "<input type='checkbox' disabled='true'";    
  78.                             }    
  79.                          }    
  80.                     },    
  81.                     {field:'open',title:'有效最新送白菜网',width:100,      
  82.                         formatter: function(value,row,index){    
  83.                             if(value) {    
  84.                                 return "<input type='checkbox' checked='checked' disabled='true'";    
  85.                             } else {    
  86.                                 return "<input type='checkbox' disabled='true'";    
  87.                             }    
  88.                         }    
  89.                      },    
  90.                     {field:'category.type',title:'所属最新送白菜网类别',width:200, //category.type是最新送白菜网类别    
  91.                         formatter: function(value,row,index){    
  92.                             if(row.category != null && row.category.type != null) {    
  93.                                 return row.category.type; //如果最新送白菜网类别不为空,返回最新送白菜网类别    
  94.                             } else {    
  95.                                 return "此最新送白菜网暂时未分类";    
  96.                             }    
  97.                          }      
  98.                     }    
  99.                 ]]        
  100.             });     
  101.             //把普通的文本框转化为查询搜索文本框    
  102.             $('#ss').searchbox({     
  103.                 //触发查询事件    
  104.                  searcher:function(value,name){ //value表示输入的值    
  105.                     //添加触发代码    
  106.                 },     
  107.                 prompt:'请输入搜索关键字'     
  108.             });     
  109.         });    
  110.     </script>    
  111.   </head>    
  112.       
  113.   <body>    
  114.     <table id="dg"></table>    
  115.         
  116.   </body>    
  117. </html>    

  接下来我们完成productAction中的queryJoinCategory方法,在这之前,先要完成service部分,我们都是先从底层慢慢往上开发的:

Java代码
  1. //ProductService接口    
  2. public interface ProductService extends BaseService<Product> {    
  3.         
  4.     //查询最新送白菜网信息,级联类别    
  5.     public List<Product> queryJoinCategory(String type, int page, int size); //使用最新送白菜网的名称查询    
  6.     //根据关键字查询总记录数    
  7.     public Long getCount(String type);    
  8. }    
  9.     
  10. @SuppressWarnings("unchecked")    
  11. @Service("productService")    
  12. public class ProductServiceImpl extends BaseServiceImpl<Product> implements ProductService {    
  13.     
  14.     @Override    
  15.     public List<Product> queryJoinCategory(String name, int page, int size) {    
  16.         String hql = "from Product p left join fetch p.category where p.name like :name";    
  17.         return getSession().createQuery(hql)    
  18.                 .setString("name""%" + name + "%")    
  19.                 .setFirstResult((page-1) * size) //从第几个开始显示    
  20.                 .setMaxResults(size) //显示几个    
  21.                 .list();    
  22.     }    
  23.         
  24.     @Override    
  25.     public Long getCount(String name) {    
  26.         String hql = "select count(p) from Product p where p.name like :name";    
  27.         return (Long) getSession().createQuery(hql)    
  28.             .setString("name""%" + name + "%")    
  29.             .uniqueResult(); //返回一条记录:总记录数    
  30.     }    
  31.     
  32. }    

  下面可以完成productAction中的queryJoinCategory方法了:

Java代码
  1. @Controller("productAction")    
  2. @Scope("prototype")    
  3. public class ProductAction extends BaseAction<Product> {    
  4.         
  5.     public String queryJoinCategory() {    
  6.         System.out.println("name:" + model.getName());    
  7.         System.out.println("page:" + page);    
  8.         System.out.println("rows:" + rows);    
  9.             
  10.         //用来存储分页的数据    
  11.         pageMap = new HashMap<String, Object>();    
  12.             
  13.         //根据关键字和分页的参数查询相应的数据    
  14.         List<Product> productList = productService.queryJoinCategory(model.getName(), page, rows);    
  15.         pageMap.put("rows", productList); //存储为JSON格式    
  16.         //根据关键字查询总记录数    
  17.         Long total = productService.getCount(model.getName());    
  18. //      System.out.println(total);    
  19.         pageMap.put("total", total); //存储为JSON格式    
  20.         return "jsonMap";    
  21.     }    
  22.     
  23. }    

  接下来在struts.xml中进行配置,跟之前的最新送白菜网类一样的流程,到这里可以看出,开发好了一个,下面一个就快了:

XML/HTML代码
  1. <action name="product_*" class="productAction" method="{1}">    
  2.     <result name="jsonMap" type="json">    
  3.         <param name="root">pageMap</param>    
  4.         <param name="excludeProperties">    
  5.             <!-- rows[0].category.account -->    
  6.             <!-- 把所有account过滤掉,否则会出现懒加载问题,该部分下面截图 -->             
  7.         </param>    
  8.     </result>    
  9. </action>    

SSH电商项目实战之十:最新送白菜网类基本模块的搭建

  这样后台程序写好了,然后开启tomcat,测试一下,当我们点击左侧菜单栏的最新送白菜网管理时,会弹出右边如下窗口:

SSH电商项目实战之十:最新送白菜网类基本模块的搭建

  这样我们就完成了最新送白菜网管理窗口的框架了。

 

]]>
软件开发http://www.teaching4real.com/software/770.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=770http://www.jizhuomi.com/cmd.asp?act=tb&id=770&key=2398712e
SSH电商项目实战之九:添加和更新最新送白菜网类别功能的实现a@b.com (鸡啄米)http://www.jizhuomi.com/software/769.htmlTue, 24 Oct 2017 14:14:05 +0800http://www.jizhuomi.com/software/769.html  上一节我们做完了查询和删除最新送白菜网的功能,这一节我们做一下添加和更新最新送白菜网的功能。

  1. 添加最新送白菜网类别

  1.1 添加类别的UI设计

  我们先说一下思路:首先当用户点击“添加最新送白菜网”时,我们应该弹出一个“添加最新送白菜网”的UI窗口(注意这里不是跳转到新的jsp,EasyUI只有一个页面),弹出这个“添加最新送白菜网”的窗口后,应该锁住它父类的所有窗口(即点击其他地方无效,只能操作添加最新送白菜网的窗口),等用户填好了信息后,在新弹出来的窗口上点击“添加”后,将请求发送给struts2,然后struts2拿到请求你参数,从数据库中执行添加动作,这样后台操作完成,同时前台那边要刷新一下当前页面,重新显示所有最新送白菜网。

  我们查看EasyUI的文档,发现新建一个窗口有两种方法,要么使用标签创建,要么使用js创建,我们这里使用js创建,但是需要一个<div>盒子,如下:

SSH电商项目实战之九:添加和更新最新送白菜网类别功能的实现

  另外,我们创建的新的窗口不需要最小化,最大化,但是要锁屏。所以这些属性都在div中设置好,这里要注意的就是锁屏的功能,因为<div>放的地方不同,锁住的屏幕范围也不同,我们要锁住全屏,所以要把<div>放到aindex.jsp中,应为aindex.jsp中产生了query.jsp的内容(包括添加按钮),query.jsp中产生了save.jsp的内容(就是我们要显示的添加窗口UI),所以弹出窗口后,我们要把aindex.jsp的范围都锁住,所以<div>应该放到aindex.jsp中,具体实现如下:

  在aindex.jsp的<body>中新添加一个<div>

XML/HTML代码
  1. <div id="win" data-options="collapsible:false,minimizable:false,maximizable:false,modal:true"></div>     

  然后我们完善query.jsp中添加类别的部分:

JavaScript代码
  1. {    
  2.     iconCls: 'icon-add',    
  3.     text:'添加类别',    
  4.     handler: function(){    
  5.         parent.$("#win").window({ //因为<div>放在了aindex.jsp中,所以这里创建窗口要先调用parent    
  6.             title:"添加类别",    
  7.             width:350,    
  8.             height:150,    
  9.             content:'<iframe src="send_category_save.action" frameborder="0" width="100%" height="100%"/>'    
  10.         });    
  11.     }    
  12. }    

  从上面的添加类别代码中可以看出,添加后的UI,我们引入WEB-INF/category目录下的save.jsp文件中的内容,接下来我们完成save.jsp:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>      
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">      
  3. <html>      
  4.   <head>      
  5.     <%@ include file="/public/head.jspf" %>      
  6.     <style type="text/css">      
  7.         form div {      
  8.             margin:5px;      
  9.         }      
  10.     </style>      
  11.     <script type="text/javascript">      
  12.         $(function(){      
  13.             $("input[name=type]").validatebox({ //这里是“类别名称”的验证功能,如果用户没填好就提交的话,会有提示      
  14.                 required:true,      
  15.                 missingMessage:'请输入类别名称' //提示的内容      
  16.             });           
  17.       
  18.                     //对管理员的下拉列表框进行远程加载      
  19.                     $("#cc").combobox({         
  20.                         //将请求发送给accountAction中的query方法处理,这里需要将处理好的数据返回到这边来显示了 ,所以后台需要将数据打包成json格式发过来      
  21.                         url:'account_query.action',        
  22.                         valueField:'id',          
  23.                         textField:'login', //我们下拉列表中显示的是管理员的登录名      
  24.                         panelHeight:'auto', //自适应高度      
  25.                         panelWidth:120,//下拉列表是两个组件组成的      
  26.                         width:120, //要同时设置两个宽度才行      
  27.                         editable:false //下拉框不允许编辑      
  28.                      });      
  29.       
  30.             //窗体弹出默认是禁用验证,因为刚弹出的窗口,用户还没填就显示的话,太丑      
  31.             $("#ff").form("disableValidation");      
  32.             //注册button的事件。即当用户点击“添加”的时候做的事      
  33.             $("#btn").click(function(){      
  34.                 //开启验证      
  35.                 $("#ff").form("enableValidation");      
  36.                 //如果验证成功,则提交数据      
  37.                 if($("#ff").form("validate")) {      
  38.                     //调用submit方法提交数据      
  39.                     $("#ff").form('submit', {      
  40.                         url: 'category_save.action', //将请求提交给categoryAction中的save方法处理      
  41.                         success: function(){ //成功后      
  42.                             //如果成功了,关闭当前窗口      
  43.                             parent.$("#win").window("close");      
  44.                             //刷新页面,刚刚添加的就显示出来了。      
  45.                                                         //获取aindex-->iframe-->datagrid      
  46.                             parent.$("iframe[title='类别管理']").get(0).contentWindow.$("#dg").datagrid("reload");      
  47.                         }      
  48.                     });      
  49.                 }      
  50.             });      
  51.         });      
  52.     </script>      
  53.   </head>      
  54.         
  55.   <body>      
  56.     <form id="ff" method="post">         
  57.         <div>         
  58.             <label for="name">最新送白菜网名称:</label> <input type="text" name="type" />         
  59.         </div>         
  60.             <div>      
  61.                 <label>所属管理员:</label>       
  62.                 <input id="cc" name="account.id"/>      
  63.             </div>      
  64.         <div>         
  65.             <label for="hot">热点:</label>         
  66.                 是<input type="radio" name="hot" value="true" />       
  67.                 否 <input type="radio" name="hot" value="true" />      
  68.         </div>        
  69.         <div>      
  70.             <a id="btn" href="#" class="easyui-linkbutton" data-options="iconCls:'icon-add'">添加</a>        
  71.         </div>        
  72.     </form>         
  73.   </body>      
  74. </html>

  前台的显示以及发送请求都做完了,接下来就是做后台的程序了。

  1.2 添加类别的逻辑实现

  前台会把数据发送给categoryAction中的save方法去执行,所以我们去写Action就行了,因为后台只需要将类别添加进数据库,不需要向前台返回数据,所以比较简单,直接写好action就行了:

Java代码
  1. @Controller("categoryAction")    
  2. @Scope("prototype")    
  3. public class CategoryAction extends BaseAction<Category> {    
  4.         
  5.     //省略其他代码……    
  6.     public void save() {    
  7.         System.out.println(model);    
  8.         categoryService.save(model);    
  9.     }    
  10.     
  11. }    

  这样数据就存入数据库了,完了后,前台那边刷新显示,就能将新添加的最新送白菜网类别显示出来了,我们看一下

SSH电商项目实战之九:添加和更新最新送白菜网类别功能的实现

SSH电商项目实战之九:添加和更新最新送白菜网类别功能的实现

SSH电商项目实战之九:添加和更新最新送白菜网类别功能的实现

  添加最新送白菜网类别这一块我们就做完了。下面做更新最新送白菜网类别那块。

  2. 更新最新送白菜网类别

  2.1 更新类别的UI设计

  更新最新送白菜网类别的思路和上面的添加的基本相同,首先也是弹出一个UI窗口,然后用户填好数据发送给后台,后台更新数据库,前台再刷新显示。我们仍然采用上面的方法创建一个UI窗口。<div>盒子不用改动什么,我们需要做的就是在query.jsp中完善更“更新类别”部分的代码:

JavaScript代码
  1. {    
  2.     iconCls: 'icon-edit',    
  3.     text:'更新类别',    
  4.     handler: function(){    
  5.         //判断是否有选中行记录,使用getSelections获取选中的所有行    
  6.         var rows = $("#dg").datagrid("getSelections");    
  7.         if(rows.length == 0) {    
  8.             //弹出提示信息    
  9.             $.messager.show({ //语法类似于java中的静态方法,直接对象调用    
  10.                 title:'错误提示',    
  11.                 msg:'至少要选择一条记录',    
  12.                 timeout:2000,    
  13.                 showType:'slide',    
  14.             });    
  15.         }else if(rows.length != 1) {    
  16.             //弹出提示信息    
  17.             $.messager.show({ //语法类似于java中的静态方法,直接对象调用    
  18.                 title:'错误提示',    
  19.                 msg:'每次只能更新一条记录',    
  20.                 timeout:2000,    
  21.                 showType:'slide',    
  22.             });    
  23.         } else{    
  24.             //1. 弹出更新的页面    
  25.             parent.$("#win").window({    
  26.                 title:"添加类别",    
  27.                 width:350,    
  28.                 height:250,    
  29.                 content:'<iframe src="send_category_update.action" frameborder="0" width="100%" height="100%"/>'    
  30.             });    
  31.             //2.     
  32.         }    
  33.     }    
  34. }    

  我们分析一下上面的js代码:首先获取用户勾选要更新的行,如果没有选中则提示用户至少需要选中一项纪录去更新,如果选中的不止一条纪录,也得提示用户每次只能更新一条纪录。当这些判断都结束后,保证了用户勾选了一条纪录,那么我们开始创建新的UI窗口了,这里还是跟上面一样,引入WEB-INF/category目录下的update.jsp页面的内容,我们来看下update.jsp页面内容:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <style type="text/css">    
  7.         form div {    
  8.             margin:5px;    
  9.         }    
  10.     </style>    
  11.     <script type="text/javascript">    
  12.         $(function(){    
  13.             //iframe中的datagrid对象    
  14.             var dg = parent.$("iframe[title='类别管理']").get(0).contentWindow.$("#dg");    
  15.                 
  16.             //对管理员的下拉列表框进行远程加载    
  17.             $("#cc").combobox({       
  18.                 //将请求发送给accountAction中的query方法处理,这里需要将处理好的数据返回到这边来显示了 ,所以后台需要将数据打包成json格式发过来    
  19.                 url:'account_query.action',      
  20.                 valueField:'id',        
  21.                 textField:'login', //我们下拉列表中显示的是管理员的登录名    
  22.                 panelHeight:'auto', //自适应高度    
  23.                 panelWidth:120,//下拉列表是两个组件组成的    
  24.                 width:120, //要同时设置两个宽度才行    
  25.                 editable:false //下拉框不允许编辑    
  26.              });      
  27.                 
  28.             // 完成数据的回显,更新时,用户肯定先选择了要更新的那一行,首先我们得拿到那一行    
  29.             var rows = dg.datagrid("getSelections");    
  30.             //将拿到的那一行对应的数据字段加载到表单里,实现回显    
  31.             $("#ff").form('load',{    
  32.                 id:rows[0].id,    
  33.                 type:rows[0].type,    
  34.                     hot:rows[0].hot,    
  35.                 'account.id':rows[0].account.id //EasyUI不支持account.id这种点操作,所以要加个引号    
  36.             });    
  37.     
  38.             //回显完了数据后,设置一下验证功能    
  39.             $("input[name=type]").validatebox({    
  40.                 required:true,    
  41.                 missingMessage:'请输入类别名称'    
  42.             });         
  43.             //窗体弹出默认时禁用验证    
  44.             $("#ff").form("disableValidation");    
  45.             //注册button的事件    
  46.             $("#btn").click(function(){    
  47.                 //开启验证    
  48.                 $("#ff").form("enableValidation");    
  49.                 //如果验证成功,则提交数据    
  50.                 if($("#ff").form("validate")) {    
  51.                     //调用submit方法提交数据    
  52.                     $("#ff").form('submit', {    
  53.                         url: 'category_update.action', //提交时将请求传给categoryAction的update方法执行    
  54.                         success: function(){    
  55.                             //如果成功了,关闭当前窗口,并刷新页面    
  56.                             parent.$("#win").window("close");    
  57.                             dg.datagrid("reload");    
  58.                         }    
  59.                     });    
  60.                 }    
  61.             });    
  62.         });    
  63.     </script>    
  64.   </head>    
  65.       
  66.   <body>    
  67.     <form id="ff" method="post">       
  68.         <div>       
  69.             <label for="name">类别名称:</label> <input type="text" name="type" />       
  70.         </div>       
  71.         <div>       
  72.             <label for="hot">热点:</label>       
  73.                 是<input type="radio" name="hot" value="true" />     
  74.                 否<input type="radio" name="hot" value="false" />    
  75.         </div>      
  76.         <div>       
  77.             <label for="account">所属管理员:</label>    
  78.              <!-- 下拉列表我们采用远程加载的方法加载管理员数据 -->    
  79.              <input id="cc" name="account.id" />    
  80.         </div>    
  81.         <div>    
  82.             <a id="btn" href="#" class="easyui-linkbutton" data-options="iconCls:'icon-edit'">更新</a>      
  83.             <input type="hidden" name="id" />    
  84.         </div>  `    
  85.     </form>       
  86.   </body>    
  87. </html>    

  更新与添加不同的地方在于,首先得数据回显,然后还有个下拉列表显示管理员数据,因为所属管理员也要更新。我们看看上面的代码,首先使用远程加载的方法加载管理员数据,先向后台发送一个请求,后台accountAction的query方法处理完后,管理员数据打包成json格式返回回来,这样就能拿到管理员数据了,拿到后,就可以进行数据的回显了。我们看一下后台的程序:

  2.2 更新类别的逻辑实现

Java代码
  1. @Controller("baseAction")    
  2. @Scope("prototype")    
  3. public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {    
  4.     
  5.     //用来装有将要被打包成json格式返回给前台的数据,下面要实现get方法    
  6.     protected List<T> jsonList = null;    
  7.     //省略其他方法……    
  8.         
  9. }    
  10.     
  11. //AccountAction    
  12. @Controller("accountAction")    
  13. @Scope("prototype")    
  14. public class AccountAction extends BaseAction<Account> {    
  15.         
  16.     public String query() {    
  17.         jsonList = accountService.query();    
  18.         return "jsonList";    
  19.     }    
  20.     
  21. }    

  接下来我们配置一下struts.xml文件:

XML/HTML代码
  1. <action name="account_*" class="accountAction" method="{1}">    
  2.     <result name="jsonList" type="json">    
  3.         <param name="root">jsonList</param>    
  4.         <param name="excludeProperties">    
  5.             <!-- [0].pass, [1].pass -->    
  6.             <!-- 正则表达式显示有bug,我下面截个图 -->    
  7.         </param>    
  8.     </result>    
  9. </action>    

SSH电商项目实战之九:添加和更新最新送白菜网类别功能的实现

       完成回显后,就是更新操作了,当然也有验证的功能,和添加一样的,更新操作将请求传给categoryAction的update方法执行,比较简单:

Java代码
  1. @Controller("categoryAction")    
  2. @Scope("prototype")    
  3. public class CategoryAction extends BaseAction<Category> {    
  4.     //省略其他方法……    
  5.     public void update() {    
  6.         System.out.println(model);    
  7.         categoryService.update(model);    
  8.     }    
  9. }    

  到此,我们完成了最新送白菜网类别的添加和更新操作。

 

]]>
软件开发http://www.teaching4real.com/software/769.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=769http://www.jizhuomi.com/cmd.asp?act=tb&id=769&key=ba59295b
SSH电商项目实战之八:查询和删除最新送白菜网类别功能的实现a@b.com (鸡啄米)http://www.jizhuomi.com/software/768.htmlWed, 11 Oct 2017 09:18:20 +0800http://www.jizhuomi.com/software/768.html  上一节我们完成了使用DataGrid显示所有最新送白菜网信息,这节我们开始添加几个功能:添加、更新、删除和查询。首先我们实现下前台的显示,然后再做后台获取数据。

  1. 添加、更新、删除和查询功能的前台实现

  DataGrid控件里有个toolbar属性,是添加工具栏的,我们可以在toolbar属性中添加这些按钮来实现相应的功能。先看一下官方文档对toolbar的定义:

SSH电商项目实战之八:查询和删除最新送白菜网类别功能的实现

  我们使用数组的方式定义工具栏,在query.jsp页面中新添加如下代码:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <style type="text/css">    
  7.         body {    
  8.             margin: 1px;    
  9.         }    
  10.                 .searchbox {    
  11.                         margin: -3;    
  12.                 }    
  13.     </style>    
  14.     <script type="text/javascript">    
  15.         $(function(){    
  16.             $('#dg').datagrid({       
  17.                 //url地址改为请求categoryAction    
  18.                 url:'category_queryJoinAccount.action',    
  19.     
  20.                 singleSelect:false, //如果为真,只允许单行显示,全选功能失效    
  21.                 //设置分页    
  22.                 pagination:true,    
  23.                 //设置每页显示的记录数,默认是10个    
  24.                 pageSize:5,    
  25.                 //设置可选的每页记录数,供用户选择,默认是10,20,30,40...    
  26.                 pageList:[5,10,15,20],    
  27.                 idField:'id',//指定id为标识字段,在删除,更新的时候有用,如果配置此字段,在翻页时,换页不会影响选中的项    
  28.     
  29.                      /*********************添加的代码***********************/    
  30.                 toolbar: [{    
  31.                     iconCls: 'icon-add',    
  32.                     text:'添加类别',    
  33.                     handler: function(){    
  34.                         alert('--加添类别--');    
  35.                     }    
  36.                 },'-',{    
  37.                     iconCls: 'icon-edit',    
  38.                     text:'更新类别',    
  39.                     handler: function(){    
  40.                         alert('--更新类别--');    
  41.                     }    
  42.                 },'-',{    
  43.                     iconCls: 'icon-remove',    
  44.                     text:'删除类别',    
  45.                     handler: function(){    
  46.                         //判断是否有选中行记录,使用getSelections获取选中的所有行    
  47.                         var rows = $("#dg").datagrid("getSelections");    
  48.                         //返回被选中的行,如果没有任何行被选中,则返回空数组    
  49.                         if(rows.length == 0) {    
  50.                             //弹出提示信息    
  51.                             $.messager.show({ //语法类似于java中的静态方法,直接对象调用    
  52.                                 title:'错误提示',    
  53.                                 msg:'至少要选择一条记录',    
  54.                                 timeout:2000,    
  55.                                 showType:'slide',    
  56.                             });    
  57.                         } else {    
  58.                             //提示是否确认删除,如果确认则执行删除的逻辑    
  59.                             $.messager.confirm('删除的确认对话框', '您确定要删除此项吗?', function(r){    
  60.                                 if (r){    
  61.                                 // 退出操作;    
  62.                                     alert("--删除操作--")    
  63.                                 }    
  64.                             });    
  65.                         }                           
  66.                     }    
  67.                  },'-',{ //查询按钮不是LinkButton,它有语法,但是也支持解析HTML标签    
  68.                      text:"<input id='ss' name='serach' />"    
  69.                  }],    
  70.                    
  71.                 //把普通的文本框转化为查询搜索文本框    
  72.                 $('#ss').searchbox({     
  73.                     //触发查询事件    
  74.                     searcher:function(value,name){ //value表示输入的值    
  75.                                            //查询操作    
  76.                     },     
  77.                     prompt:'请输入搜索关键字' //默认的显示    
  78.                 });     
  79.                /*********************************************************************/    
  80.     
  81.         });    
  82.     </script>    
  83.   </head>    
  84.       
  85.   <body>    
  86.     <table id="dg"></table>    
  87.   </body>    
  88. </html>    

  这样我们就搭好了添加、更新、删除和查询的前台框架了,现在可以在前台显示了,后台没有数据过来,只是弹出个提示框,不过显示功能已经完成,看一下效果:

SSH电商项目实战之八:查询和删除最新送白菜网类别功能的实现

  接下来我们逐个来完成相应的功能。

  2. DataGrid类别查询的实现

  查询的实现是最简单的,在搜素框中输入关键字,然后将关键字作为参数传给action,然后Service从数据库中拿出数据,打包成json格式传到前台来显示即可,这个过程跟前面显示所有最新送白菜网信息是一样的,我们只需要在上面jsp中添加搜索部分的代码即可,其他不用改变,添加的代码如下:

JavaScript代码
  1. //把普通的文本框转化为查询搜索文本框    
  2. $('#ss').searchbox({     
  3.     //触发查询事件    
  4.     searcher:function(value,name){ //value表示输入的值    
  5.         //alert(value + "," + name)    
  6.         //获取当前查询的关键字,通过DataGrid加载相应的信息,使用load加载和显示第一页的所有行。    
  7.         //如果指定了参数,它将取代'queryParams'属性。通常可以通过传递一些参数执行一次查询,通过调用这个方法会向上面url指定的action去发送请求,从服务器加载新数据。    
  8.         $('#dg').datagrid('load',{    
  9.             type: value    
  10.         });    
  11.     
  12.     },     
  13.     prompt:'请输入搜索关键字'     
  14. });     

  load方法可以加载显示第一页的所有行,它有个参数,如果指定了,就会去带上么的queryParams,否则默认传递上面的queryParams指定的参数,我们在这里将type设置成value的值,即用户输入的查询关键字,然后传到action,后台根据用户输入的value在数据库中查找,并返回给前台。执行结果如下:

SSH电商项目实战之八:查询和删除最新送白菜网类别功能的实现

  这样我便完成了搜索的功能了,比较简单。

  3. DataGrid类别删除的实现

  现在我们来实现删除功能,从上面的jsp中可以看出,删除前判断用户有没有选中某条记录,如果没有则给用户一个提示,如果有选中,则弹出窗口让用户确认,如果为真,则执行删除功能。有个细节要注意下,如果想要一次性删除多条记录,那么上面的singleSelect属性要设置成false。

  首先,我们把上面query.jsp中删除部分的代码补充完,见下面:

JavaScript代码
  1. {    
  2.     iconCls: 'icon-remove',    
  3.     text:'删除类别',    
  4.     handler: function(){    
  5.         //判断是否有选中行记录,使用getSelections获取选中的所有行    
  6.         var rows = $("#dg").datagrid("getSelections");    
  7.         //返回被选中的行,如果没有任何行被选中,则返回空数组    
  8.         if(rows.length == 0) {    
  9.             //弹出提示信息    
  10.             $.messager.show({ //语法类似于java中的静态方法,直接对象调用    
  11.                 title:'错误提示',    
  12.                 msg:'至少要选择一条记录',    
  13.                 timeout:2000,    
  14.                 showType:'slide',    
  15.             });    
  16.         } else {    
  17.             //提示是否确认删除,如果确认则执行删除的逻辑    
  18.             $.messager.confirm('删除的确认对话框''您确定要删除此项吗?'function(r){    
  19.                 if (r){    
  20.                     //1. 从获取的记录中获取相应的的id,拼接id的值,然后发送后台1,2,3,4    
  21.                     var ids = "";    
  22.                     for(var i = 0; i < rows.length; i ++) {    
  23.                         ids += rows[i].id + ",";    
  24.                     }    
  25.                     ids = ids.substr(0, ids.lastIndexOf(","));    
  26.                     //2. 发送ajax请求    
  27.                     $.post("category_deleteByIds.action",{ids:ids},function(result){    
  28.                         if(result == "true") {    
  29.                                                 //将刚刚选中的记录删除,要不然会影响后面更新的操作    
  30.                                                 $("#dg").datagrid("uncheckAll");    
  31.                             //刷新当前页,查询的时候我们用的是load,刷新第一页,reload是刷新当前页    
  32.                             $("#dg").datagrid("reload");//不带参数默认为上面的queryParams    
  33.                         } else {    
  34.                             $.messager.show({     
  35.                                 title:'删除异常',    
  36.                                 msg:'删除失败,请检查操作',    
  37.                                 timeout:2000,    
  38.                                 showType:'slide',    
  39.                             });    
  40.                         }    
  41.                     },"text");    
  42.                 }    
  43.             });    
  44.         }                           
  45.     }    
  46. }    

  如果用户选择删除,首先会弹出一个对话框,当用户确定要删除后,我们首先要获取用户所勾选的最新送白菜网的id,将这些id拼接成一个字符串,然后向后台发送ajax请求,$.post中的第一个参数是发送到那个action,第二个参数是发送的参数,第三个参数是回调函数,即删除成功后执行该函数里面的方法,该函数的参数result是从后台传过来的,第四个参数可有可无,是返回数据的类型。我们重点看看$.post中的内容,当后台返回一个"true"表示删除成功了,那么我们调用DataGrid里面的reload方法重新刷新页面,reload和前面查询时用的load是一样的,不同的地方在于reload刷新后停留在当前页面,而load则显示第一页。

  好了,前台页面部分写好了,接下来完成后台的相应方法,首先在categoryService中添加deleteByIds方法,并在其实现类categoryServceImpl中实现该方法:

Java代码
  1. //categoryService接口    
  2. public interface CategoryService extends BaseService<Category> {    
  3.     //查询类别信息,级联管理员    
  4.     public List<Category> queryJoinAccount(String type, int page, int size); //使用类别的名称查询    
  5.     //根据关键字查询总记录数    
  6.     public Long getCount(String type);    
  7.     //根据ids删除多条记录    
  8.     public void deleteByIds(String ids);    
  9. }    
  10.     
  11. //categoryServiceImpl实现类    
  12. @SuppressWarnings("unchecked")    
  13. @Service("categoryService")    
  14. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  15.     
  16.     //其他方法省略不写了……可以参照前面的相应章节内容    
  17.     
  18.     @Override    
  19.     public void deleteByIds(String ids) {    
  20.         String hql = "delete from Category c where c.id in (" + ids + ")";    
  21.         getSession().createQuery(hql).executeUpdate();    
  22.     }    
  23. }    

  写好了Service部分,接下来开始写Action部分了。因为我们要获取前台传进来的ids数据,所以在action中得有一个实现了get和set方法的变量来接收这个数据,另外,我们要将结果传给前台,前面章节中我们做级联查询的时候,使用的方法是struts把查询的结果数据打包成json格式传给前台,所以需要一个Map,然后将通过配置文件中的配置,将Map转换成json格式。这里我们传到前台的数据比较简单,入股删除成功我们传一个"true"即可,所以不用打包成json格式,我们通过流的方法去传送,道理和前面的一样,首相我们得有一个流的对象去保存这个"true"的字节,然后通过配置,将这个对象通过流传到前台。这两个对象我们还是写在BaseAction中,如下:

Java代码
  1. @Controller("baseAction")    
  2. @Scope("prototype")    
  3. public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {    
  4.     
  5.     //获取要删除的ids,要有get和set方法    
  6.     //流是用来想前台返回数据的,这个数据是让struts获取的,然后通过流的形式传到前台,所以实现get方法即可    
  7.     protected String ids;    
  8.     protected InputStream inputStream;    
  9.             
  10.         //下面省略……    
  11. }    

  对应的CategoryAction中的方法如下:

Java代码
  1. @Controller("categoryAction")    
  2. @Scope("prototype")    
  3. public class CategoryAction extends BaseAction<Category> {    
  4.         
  5.     public String queryJoinAccount() {    
  6.                 //略……    
  7.     }    
  8.         
  9.     public String deleteByIds() {    
  10.         System.out.println(ids);    
  11.         categoryService.deleteByIds(ids);    
  12.         //如果删除成功就会往下执行,我们将"true"以流的形式传给前台    
  13.         inputStream = new ByteArrayInputStream("true".getBytes()); //将"true"的字节存到流inputStream中    
  14.         return "stream";    
  15.     }    
  16. }    

  接下来看struts.xml中相应的配置:

XML/HTML代码
  1. <struts>    
  2.         
  3.     <constant name="struts.devMode" value="true" />    
  4.         
  5.     <package name="shop" extends="json-default"><!-- jason-default继承了struts-default -->    
  6.         
  7.         <global-results>    
  8.             <result name="aindex">/WEB-INF/main/aindex.jsp</result>    
  9.         </global-results>    
  10.     
  11.         <!-- class对应的是Spring中配置该Action的id值,因为要交给Spring管理 -->    
  12.         <action name="category_*" class="categoryAction" method="{1}">    
  13.             <result name="jsonMap" type="json">    
  14.                 <!-- 略 -->    
  15.             </result>    
  16.             <result name="stream" type="stream"> <!-- 以stream的形式,type为stream -->    
  17.                 <param name="inputName">inputStream</param> <!-- imputStream中有要传的数据 -->    
  18.              </result>    
  19.         </action>    
  20.             
  21.         <action name="account_*" class="accountAction" method="{1}">    
  22.             <result name="index">/index.jsp</result>    
  23.         </action>    
  24.             
  25.         <!-- 用来完成系统 请求转发的action,所有的请求都交给execute-->    
  26.         <action name="send_*_*" class="sendAction">    
  27.             <result name="send">/WEB-INF/{1}/{2}.jsp</result>    
  28.         </action>    
  29.     </package>    
  30.     
  31. </struts>   

  这样我们就做好了删除的操作了,看一下效果:

SSH电商项目实战之八:查询和删除最新送白菜网类别功能的实现

SSH电商项目实战之八:查询和删除最新送白菜网类别功能的实现

  测试成功,我们也可以一次性选择多项去删除,至此,删除功能做完了。

]]>
软件开发http://www.jizhuomi.com/software/768.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=768http://www.jizhuomi.com/cmd.asp?act=tb&id=768&key=f8b5b906
SSH电商项目实战之七:Struts2和Json的整合a@b.com (鸡啄米)http://www.jizhuomi.com/software/767.htmlWed, 27 Sep 2017 09:12:47 +0800http://www.jizhuomi.com/software/767.html  上一节我们完成了DataGrid显示jason数据,但是没有和后台联系在一起,只是单纯地显示了我们自己弄的json数据,这一节我们将json和Struts2整合,打通EasyUI和Struts2之间的交互。

  1. json环境的搭建

  json环境搭建很简单,导入json的jar包即可,如下:

SSH电商项目实战之七:Struts2和Json的整合

  (注:json-lib-2.4的jar包下载地址:http://download.csdn.net/detail/eson_15/9514985

  2. 完善Action

  在DataGrid控件中有个属性是url,可以指定请求数据的url地址,在上一节我们将这个地址直接设置成了一个具体的json文件,这里我们将这个url设置成一个action,如url:'category_queryJoinAccount.action',表示会去请求categoryAction的queryJoinAccount方法(文章最后会给出query.jsp的代码)。所以我们需要去完成categoryAction中的queryJoinAccount方法。

  在Struts2和json整合前,我们先看一下之前显示一次json数据都发了哪些请求:

SSH电商项目实战之七:Struts2和Json的整合

  因为type是Category类的一个属性,我们在BaseAction中已经实现了ModelDriven<Category>接口,所以这个type会被封装到model中,我们不需要管它,可以通过model来获取,但是EasyUI自动发过来的page和rows参数我们需要自己获取了,所以我们可以在BaseModel中增加两个成员变量page和rows并实现get和set方法,最后还要考虑一点,这些参数都获得了后,我们根据这些参数去数据库中查询数据,那么我们查出来的数据放到哪呢?而且还要打包成json格式发到前台才能被DataGrid显示。我们先不考虑将查询到的数据如何打包成json格式,我们先考虑把这些数据放到一个地方,很自然的想到了使用Map,因为json格式的数据就是key-value形式的。想到这里,我们继续完善BaseAction:

Java代码
  1. @Controller("baseAction")    
  2. @Scope("prototype")    
  3. public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {    
  4.     
  5.     //page和rows和分页有关,pageMap存放查询的数据,然后打包成json格式用的    
  6.     //page和rows实现get和set方法,pageMap只需要实现get方法即可,因为pageMap不是接收前台参数的,是让struts获取的    
  7.     protected Integer page;    
  8.     protected Integer rows;    
  9.     protected Map<String, Object> pageMap = null;//让不同的Action自己去实现    
  10.         //省略get和set方法……    
  11.         
  12.     /******************* 下面还是原来BaseAction部分 *************************/    
  13.     //service对象    
  14.     @Resource    
  15.     protected CategoryService categoryService;    
  16.     @Resource    
  17.     protected AccountService accountService;    
  18.     
  19.     //域对象    
  20.     protected Map<String, Object> request;    
  21.     protected Map<String, Object> session;    
  22.     protected Map<String, Object> application;    
  23.             
  24.     @Override    
  25.     public void setApplication(Map<String, Object> application) {    
  26.         this.application = application;    
  27.     }    
  28.     @Override    
  29.     public void setSession(Map<String, Object> session) {    
  30.         this.session = session;    
  31.     }    
  32.     @Override    
  33.     public void setRequest(Map<String, Object> request) {    
  34.         this.request = request;    
  35.     }    
  36.         
  37.     //ModelDriven    
  38.     protected T model;    
  39.     @Override    
  40.     public T getModel() {    
  41.         ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();    
  42.         Class clazz = (Class)type.getActualTypeArguments()[0];    
  43.         try {    
  44.             model = (T)clazz.newInstance();    
  45.         } catch (Exception e) {    
  46.             throw new RuntimeException(e);    
  47.         }       
  48.         return model;    
  49.     }    
  50. }    

  好,完善了BaseCategory后,我们可以写categoryAction中的queryJoinAccount方法了,我们将categoryAction中原来的方法全删掉,因为那些都是之前搭建环境时候测试用的,都不用了,现在真正开始项目代码了:

Java代码
  1. @Controller("categoryAction")    
  2. @Scope("prototype")    
  3. public class CategoryAction extends BaseAction<Category> {    
  4.         
  5.     public String queryJoinAccount() {    
  6.     
  7.         //用来存储分页的数据    
  8.         pageMap = new HashMap<String, Object>();    
  9.             
  10.         //根据关键字和分页的参数查询相应的数据。这个方法我们在Service中写过了,当时完成级联查询    
  11.         List<Category> categoryList = categoryService.queryJoinAccount(model.getType(), page, rows);    
  12.         pageMap.put("rows", categoryList); //存储为JSON格式,从上一节的json文件可以看出,一个key是total,一个key是rows,这里先把rows存放好    
  13.         //根据关键字查询总记录数    
  14.         Long total = categoryService.getCount(model.getType()); //这个方法没写,我们等会儿去Service层完善一下    
  15. //      System.out.println(total);    
  16.         pageMap.put("total", total); //存储为JSON格式,再把total存放好    
  17.     
  18.         return "jsonMap";    
  19.     }    
  20. }    

  这样Action我们就写好了,现在Action拿到前台传来的参数,然后根据参数查询了指定type的总记录数,以及指定type的所有最新送白菜网,并且按照json中指定的key(即total和rows)进行存放,放在HashMap中了,之后只要将这个HashMap中的数据打包成json格式发送到前台就可以被DataGrid显示了。我们先把这个HashMap放这,先去完善了Service层的代码后,再来打包这个HashMap中的数据。

  3. 完善categoryService

  从上面的categoryAction中可知,需要在categoryService中增加一个getCount方法,并且要在具体实现类中实现好,实现如下:

Java代码
  1. //CategoryService接口    
  2. public interface CategoryService extends BaseService<Category> {    
  3.     //查询类别信息,级联管理员    
  4.     public List<Category> queryJoinAccount(String type, int page, int size); //使用类别的名称查询    
  5.     //根据关键字查询总记录数    
  6.     public Long getCount(String type);    
  7. }    
  8.     
  9. //CategoryServiceImpl实现类    
  10. @SuppressWarnings("unchecked")    
  11. @Service("categoryService")    
  12. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  13.     
  14.     @Override    
  15.     public List<Category> queryJoinAccount(String type, int page, int size) {    
  16.         String hql = "from Category c left join fetch c.account where c.type like :type";    
  17.         return getSession().createQuery(hql)    
  18.                 .setString("type""%" + type + "%")    
  19.                 .setFirstResult((page-1) * size) //从第几个开始显示    
  20.                 .setMaxResults(size) //显示几个    
  21.                 .list();    
  22.     }    
  23.     
  24.     @Override    
  25.     public Long getCount(String type) {    
  26.         String hql = "select count(c) from Category c where c.type like :type";    
  27.         return (Long) getSession().createQuery(hql)    
  28.             .setString("type""%" + type + "%")    
  29.             .uniqueResult(); //返回一条记录:总记录数    
  30.     }    
  31. }    

  到现在为止,这个数据库中数据的获取这条路就打通了,前面两步完成了从前台-->数据库-->取数据,接下来就开始打包HashMap中存放的数据,然后发给前台了。

  4. 配置struts.xml

  在struts.xml中通过配置就可以完成对指定数据的打包,我们先看一下struts.xml中的配置:

XML/HTML代码
  1. <struts>    
  2.         
  3.     <constant name="struts.devMode" value="true" />    
  4.         
  5.     <package name="shop" extends="json-default"><!-- jason-default继承了struts-default -->    
  6.         
  7.         <global-results>    
  8.             <result name="aindex">/WEB-INF/main/aindex.jsp</result>    
  9.         </global-results>    
  10.     
  11.         <!-- class对应的是Spring中配置该Action的id值,因为要交给Spring管理 -->    
  12.         <action name="category_*" class="categoryAction" method="{1}">    
  13.             <!-- 必须要先添加json包,然后上面继承json-default -->    
  14.             <result name="jsonMap" type="json">    
  15.                 <!-- 要转换成json对象的数据 -->    
  16.                 <param name="root">pageMap</param>    
  17.                 <!-- 配置黑名单,过滤不需要的选项 ,支持正则表达式    
  18.                 json格式:{total:3,rows:[{account:{id:2,login:"user",name:"客服A",pass:"user"},hot:true,id:3,…}]}    
  19.                 -->    
  20.                 <param name="excludeProperties">    
  21.                     <!-- rows[0].account.pass-->    
  22.                                         <!-- 这里显示不了正则表达式, CSDN的一个bug,我接个图放下面 -->    
  23.                 </param>    
  24.             </result>    
  25.         </action>    
  26.             
  27.         <action name="account_*" class="accountAction" method="{1}">    
  28.             <result name="index">/index.jsp</result>    
  29.         </action>    
  30.             
  31.         <!-- 用来完成系统 请求转发的action,所有的请求都交给execute-->    
  32.         <action name="send_*_*" class="sendAction">    
  33.             <result name="send">/WEB-INF/{1}/{2}.jsp</result>    
  34.         </action>    
  35.     </package>    
  36.     
  37. </struts>    

SSH电商项目实战之七:Struts2和Json的整合

  从上面的配置可以看出,首先package要继承json-default,因为json-default继承了struts-default,因为在json的jar包里有个struts2-json-plugin-2.3.24.1.jar,打开即可看到里面有个struts-plugin.xml,打开即可看到json-default是继承了struts-default:

SSH电商项目实战之七:Struts2和Json的整合

  接下来我配置<result>,name是刚刚action返回的字符串,type一定要配成json。然后就是result中的参数了,首先必须要配的就是name为root的参数,这个参数要配成刚刚需要转换的HashMap对象,即我们定义的pageMap,有了这个参数的配置,struts才会将pageMap中的数据打包成json格式。然后就是配置黑名单,黑名单的意思就是告诉struts在打包的时候,哪些字段不需要打包,比如管理员密码之类的信息,由上面注释中的jason格式可以看出rows[0].account.pass表示密码字段,但是数据肯定不止一条,所以我们得用正则表达式来表示,这样所有密码都不会被打包到json中。

  5. 修改query.jsp内容

  到此,我们已经将数据打包成了json格式了,接下来我们完善一下前台query.jsp的内容就可以让DataGrid正确显示了:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <script type="text/javascript">    
  7.         $(function(){    
  8.             $('#dg').datagrid({       
  9.                 //url地址改为请求categoryAction    
  10.                 url:'category_queryJoinAccount.action',    
  11.                 loadMsg:'Loading......',    
  12.                 queryParams:{type:''},//type参数,这里不需要传具体的type,因为我们要显示所有的    
  13.                 //width:300,    
  14.                 fitColumns:true,    
  15.                 striped:true,    
  16.                 nowrap:true,    
  17.                 singleSelect:true,    
  18.                 pagination:true,    
  19.                 rowStyler: function(index,row){    
  20.                     console.info("index" + index + "," + row)    
  21.                     if(index % 2 == 0) {    
  22.                         return 'background-color:#fff;';    
  23.                     } else {    
  24.                         return 'background-color:#ff0;';    
  25.                     }    
  26.                         
  27.                 },          
  28.                 frozenColumns:[[    
  29.                     {field:'checkbox',checkbox:true},    
  30.                     {field:'id',title:'编号',width:200}    //这里的field字段要和json数据中的一样                 
  31.                 ]],    
  32.                 columns:[[                         
  33.                     {field:'type',title:'类别名称',width:100, //字段type    
  34.                         formatter: function(value,row,index){    
  35.                             return "<span title=" + value + ">" + value + "</span>";    
  36.                         }    
  37.                     },        
  38.                     {field:'hot',title:'热卖',width:100,  //字段hot    
  39.                         formatter: function(value,row,index){    
  40.                             if(value) { //如果是hot,该值为true,value是boolean型变量    
  41.                                 return "<input type='checkbox' checked='checked' disabled='true'"; //勾选    
  42.                             } else {    
  43.                                 return "<input type='checkbox' disable='true'"; //不勾选    
  44.                             }    
  45.                         }    
  46.                     },    
  47.                     {field:'account.login',title:'所属管理员',width:200, //account.login管理员登录名    
  48.                         formatter: function(value,row,index){    
  49.                             if(row.account != null && row.account.login != null) {    
  50.                                 return row.account.login; //如果登录名不为空,显示登录名    
  51.                             } else {    
  52.                                 return "此类别没有管理员";    
  53.                             }    
  54.                     }       
  55.                     }    
  56.                 ]]        
  57.             });     
  58.         });    
  59.     </script>    
  60.   </head>    
  61.       
  62.   <body>    
  63.     <table id="dg"></table>    
  64.   </body>    
  65. </html>    

  6.  测试显示结果

  最后我们测试一下DataGrid的显示结果,如下:

SSH电商项目实战之七:Struts2和Json的整合

  到这里,我们成功整合了Struts2和json,现在可以和前台传输json格式的数据了。

 

 

]]>
软件开发http://www.teaching4real.com/software/767.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=767http://www.jizhuomi.com/cmd.asp?act=tb&id=767&key=d3fa2df2
长文:内容产业的赢家与输家a@b.com (鸡啄米)http://www.jizhuomi.com/internet/766.htmlWed, 20 Sep 2017 10:36:58 +0800http://www.jizhuomi.com/internet/766.html  

  自微信公号12年开始以来,一波内容创业兴起。

  在内容创业大潮的推动下,涌现出了一些被称为“头部”的大号。有些大号是从博客迁移而来,有些大号则本身就一直是机构媒体或成名大V,还有些大号,在若干年前根本籍籍无名。

  从零到年入百万千万,估值动辄上亿,当然是赢家,还是不小的赢家。

  但对于产业来说,基于我一向的“渠道为王 内容为本”的观点,一个内容生产源,比起平台渠道,影响力还是弱很多。

  所以,本篇着重谈的,依然是渠道/平台类的赢家。(以图文为主,视音频类容我再思考思考,再码一篇)

  

  第一个大赢家:今日头条。

  今日头条创建于12年3月,五年的时间,当下估值已进入百亿美元俱乐部,2017年收入过一百五十亿人民币,应无悬念。

  今日头条的早期崛起,和内容创业关系并不是太大。

  2014年6月,今日头条宣布完成1亿美金融资,估值5亿。这个新闻引起了整个媒体圈的关注,也引发了后来的所谓版权争议。但在当时,内容创业虽已悄然起步,不过谈不上是什么风口。我们基金曾经以100万人民币估值的价格,还投资了一个号。在今天,是很难找到这种估值项目的。

  但今日头条后来更为彪悍的发展,与内容创业就颇有关系了。因为大多数内容创业者,即便重心在微信公号上,依然会在头条同步更新。

  这给头条带来了巨大的内容数量,对应的,也就带来了巨大的位数量。

  

  插播。

  观察一个以为主要收入模式的内容生意,“位数量”是一个非常重要的研判指标。

  一般意义上所谓的媒体——也就是一个内容生产端——通常会被判定为“体量有限”。体量有限的原因就在于,它的位是有限的。

  依附于内容之上,但内容数量存在天花板后,收入就一定会存在天花板。而要提升内容数量,对于内容生产型的媒体来说,这就意味着提高成本。

  这就是为什么传统媒体大多都是“集团式生存”的原因,一个组织办了好多媒体,商业上,就是想提升位数量。

  不过内容平台就和内容生产端不同:它的内容存在两个特点:1、相对于一个独立的生产端,数量极其巨大;2、成本非常低。

  互联网的几个做出巨大生意的内容平台,都是这样的。从桌面的门户到搜索;从移动的内容聚合APP到社会化媒体。

  它们的位数量甚至可以用“无穷”来表达,只要天下依然有生产内容的人。一茬一茬的韭菜,一波一波的羊毛,取之不尽用之不竭。

  这段插播其实是很简单的媒体商业博一把白菜论坛手机知识,但有鉴于至今还有人认为“内容为王”,必须写出来以正视听。

  

  头条并不是继承门户的衣钵,虽然它看上去和门户有点像。而且它当年起家的时候,主要的竞品也都是门户做的,比如搜狐的新闻客户端。至今,主掌腾讯门户的OMG(网络媒体事业群)还是把今日头条视为最重要的竞争对手之一。

  但本质上,张一鸣接过的,是李彦宏的旗帜。

  一个很表象的证据就是,今日头条并不想设置总编辑这种岗位(后来出于某些原因设置了),但门户恐怕组建团队的第一件事就是锚定一个总编辑。

  你知道百度(不是百度新闻这种业务线)的总编辑是谁么?(笑)

  核心关键词是:匹配。

  百度的方式是用户发起一个关键字,然后进行相关内容匹配。

  头条的方式是挖掘用户的属性指标,推送内容进行匹配。

  一个是拉取(pull)一个是推送(push),但只是由于时代原因造成的技术使用不同。根子上,都是与用户兴趣匹配加载内容。

  

  头条的崛起,当然有主观努力、战术使用得当之类的原因——任何一个产品崛起都离不开自身努力。但我还是想讲一讲大势。

  头条占了很大的移动互联网的便宜。

  百度搜索和移动互联网小屏是不匹配的。在桌面,百度是最大的流量入口,真的没有之一。但在移动小屏,这个力量被消解了。

  百度作为流量入口,一家就掌握了中国整个互联网盘子的2-3成的投放费用。对于甲方而言,我称之为“枢纽式投放”。移动小屏消解了它的力量,等同于部分释放出这一大笔投放费用。而百度一年营收数百亿人民币,即便是点零头,也够一个startup公司吃了。

  甲方的一部分预算流向了公号,直接和内容生产端对接,另外有一部分,就在寻找新的枢纽式投放。在腾讯整个体系还没有搭载起来之前,这个枢纽,内容聚合类APP是一个不错的选择。

  头条准确地掌握住了这个大势,而另外一个内容APP,搜狐新闻,虽然是最早达到装机过亿的,显然战略判断失误,没有继续深耕。

  到了头条的第五个年头,也就是今年,正如当年百度在第五个年头创建百度知道(2005年6月)一样,头条推出了自己的问答产品:悟空问答。

  这个产品遭到了业内至少是口头上的阻击,包括知乎和微博。而这一变化,也可以让我们观察到,今日头条的对手,已经不再是当年叫喊着版权的内容生产端(媒体),也不完全是诸家门户的聚合新闻APP。

  它的敌人更多了。

  

  第二个赢家:新浪微博。

  微博于09年开始内测运营,到现在已经八年。与头条不同,这是一个横跨桌面到移动阶段的产品:微博一开始还是很重视桌面表现的,现在应该说几乎所有力量都在移动上。

  微博当下市值已经超越了它的老师:twitter。

  微博从初创到繁荣到衰落又到繁荣,充分证明了一点,内容平台的核心要务是信息整合。微博盛于海量的第三方信息供给,衰于信息过于嘈杂,又盛于部落化后信息相对有效的分拣与触达。这里面也包括对信息的控制。

  微博分为两个阶段,中间大致以2013年为分割线。

  第一个阶段,超级大V策略。在这个阶段中,几乎所有人都看好微博,甚至喊出“围观改变中国”这类不乏天真的口号。

  第二个阶段,部落化策略,也就是扶持中小V。这个阶段微博努力下沉,并开始收编或整肃第三方势力。但这个阶段刚开始非常艰难,以至于在北上广很多会议论坛,都会听到“现在没人用微博”了之类的说法。

  我和大多数注册送白菜网者不同,第一个阶段我看空第二阶段我看多。而空翻多的一个重要原因,在于一个指标。

  与前文所提及的位数量一样,这个指标也是观察一个以为主要收入模型的内容业态的关键数据:主数量。

  12年四季度的时候,曹国伟在披露微博数据时提到,后者的主数量是80个——嗯,真的,没少写个零。

  时至今日,已经坐拥数十万主,是非常标准的“小生意的大故事”。

  

  微博的传播模型是一个很混杂的方式。它既有用户到用户的所谓社会化传播,也有微博运营方自己做的所谓主题议程设置。你很难说微博场域里是中心化的,也不能简单地一刀切下去说这是去中心化的。

  脱胎于新浪这样的门户,其实微博无法摆脱骨子里媒体公司的基因。所以对微博正确的描述是:社会化媒体(social media),而不是社交网络(social network)。可以说,本文说论述的四大赢家中,最接近媒体的,是微博。

  头条上的内容来自于外部内容抓取/同步,以及,直接在头条平台上推送(或自行同步其它平台帐号)。后者被称之为头条号。头条号目前总量已经达到80万的规模,其中有八成,属于江湖意义上的“自媒体”——一般理解为正统传统媒体开设的媒体帐号。

  微博就与此非常不同。微博几乎没有来自第三方同步或抓取的内容。几乎所有的微博用户,都在微博上生产、传播内容。故而微博很少碰到所谓版权争议的问题。

  也正因为此,微博视自己为一个内容生产部门,对头条的同步越来越不能忍。才会有最近两天发生的一次昏招。这个匪夷所思的对用户著作权的权利主张,其背后动因就在于,是附着于内容上的。微博大概以为,打掉了头条(包括悟空问答)对微博的同步,可能会减少后者可卖的位吧。

  直至今日,微博还是有很强的从新浪传承过来的内容思维的影子,而不是如头条、百度那般,骨子里认为自己是一家技术公司。其实微信公号平台方也从来不认为自己是一家内容公司,虽然表象上如微博般,也是海量的第三方在平台上生产内容。

  

  第三个大赢家:腾讯。

  的确,类似朋友圈、广点通之类,和WXG(微信事业群)关系不大,也的确OMG还在苦苦寻找自己的存在感,但我说的是腾讯,一个整体的腾讯。

  腾讯主要的收入依然在游戏上,所以它的市值飙升和内容赢家是否有关联还不好讲。依然看一下的表现。

长文:内容产业的赢家与输家

  这张表格能够清晰地反映出,占整个腾讯收入盘子的比重在逐年上升。

  同时,我们也能从两栏同比增幅中注意到,腾讯收入的增长相当快,跑过整个腾讯收入大盘。

  那么,腾讯收入发力在哪里呢?

  

长文:内容产业的赢家与输家

  这张表的是腾讯两个业务(游戏和)从2014年Q1到2017年Q2的逐季表现。

  首先我们能看到的是,游戏在腾讯整体收入的占比最近五个季度已经长期低于5成。2017年现象级游戏王者荣耀的耀眼表现,使得Q1游戏占比阻换了一直下滑的占比,但到了二季度,度过行业营收最惨淡的Q1之后,占比重新回落。

  15年开始的历个季度,单季度同比增幅非常可观。细查下来,腾讯两翼,其效果(2017年略调了口径,改名为社交及其它)同比增幅超越其品牌(2017年略调了口径,改名为媒体)。

  而效果,主要的构成部分就是朋友圈、微信公号上的广点通等。

  2013年开始酝酿14年正式兴起的内容创业,公号数量从十万级到百万级到今天的千万级,为广点通提供了大量的可售卖位置。而微信用户对朋友圈的粘着度——按照腾讯科技企鹅智酷的说法,朋友圈力压点对点通讯,是微信头号使用频率的功能——也使得朋友圈得以大卖。

  所谓的品牌,腾讯官方说法是主要反映来自移动端平台(如腾讯新闻)、腾讯视频等收入。也就是说,这个部分,的确可以基本对标今日头条。整个2016年,收入也过百亿。2017年调整口径后,被定义为媒体收入(主要包括新闻、视频及音乐的位产生的收入),上半年也已经斩获65亿余人民币,而行业一般下半年高于上半年,全年收入过一百五十亿也应无疑问。

  这样的成绩单,说腾讯要提防今日头条倒也没差,但要说被头条压制,显然属于杞人忧天了。

  腾讯,理所当然的,是内容行业里的大赢家。

  

  第四个大赢家:阿里。

  与类亚马逊的京东所不同的是,阿里本质上的业务并不是零售,而是(商家的费用)。号称免费的淘宝靠帮助商家获取利润,品牌扎堆的天猫,则是抽取交易佣金。

  京东的交易量(GMV)越大,并不一定会形成利润,因为它是差价模式,有进货成本。但阿里系GMV越大,自身的收入也就越高,GMV到达一定规模后,足以形成利润。

  但阿里这样“虚拟商业地产”的模式,伴随着移动兴起的小屏化后,面临严峻的考验:推荐位位位相对PC端还要不够用。如果应对不当,将会导致极速推高平台上商家的成本,使得所有商家进入囚徒困境,最终整个生态崩盘。

  阿里的应对方式是:引入化的内容,一方面增强用户粘度,一方面创造无穷多的位置。

  淘宝达人登场,发展到极盛时,有百万之众。

  十一

  早期的所谓内容生产极其简单,被称为双列清单:

长文:内容产业的赢家与输家

  一句话+若干个最新送白菜网图片,就可以抽佣。前提是,达人清晰这里的游戏规则:哪些最新送白菜网转化率高、佣金高。据称有年入可达数百万。

  但这些内容非常轻,而且制作者通常会偏向于所谓爆款。这使得淘系流量依然会向头部最新送白菜网聚焦,无法对平台上大部分商家形成普惠式的流量供给。

  阿里开始改变策略。

  十二

  有必要略微介绍一下淘系内容生态。

  今天你打开手机淘宝,与其说是打开了一个网络购物应用,不如说是打开了一本消费类电子刊物。这个刊物的内容相当庞杂海量,天量级内容的“精品购物指南”。

  首屏所出现的“淘宝头条”,理所当然的是一种内容,或可定位成消费领域的今日头条。

  但往下的类似有好货、必买清单、男神范(如果是女生打开淘宝,看到的是爱逛街)等等,都是它的内容板块。只不过淘宝头条的内容偏“资讯”类,而这些板块的内容偏“导购”类。佣金结算方式前者以CPC为主,后者以CPS为主。

  这些内容按照阿里的定义,被称为“公域内容”。

  而底部导航的第二个按钮“微淘”,所指向的,被称为“私域内容”。公域和私域内容所产生的佣金,结算方式并不相同。

  之所以前者被称为公域内容,是因为内容生产者所进行的内容生产,是有固定格式的,且用户也是根据算法匹配的。而后者的私域内容,生产格式并不强调,用户也是因为关注了某个内容源(可能就是个商铺,也可能是一个内容生产者)才会被这些内容触达。

  与消费密切相关的内容,大部分情况下,并没有太强的时效性。一篇对某皮衣进行介绍的文字,有可能在“有好货”或者“必买清单”之类的板块中有长达半年的曝光期,换而言之,内容生产者可以指望这篇文章长达半年的佣金获取。这是淘系内容,与其它内容平台非常不同的地方。

  十三

  阿里的策略调整在于两个方面:

  1、鼓励有一定篇幅的内容生产(当然也包括视频、直播),一句话加一堆最新送白菜网照片的双列清单在今天已经很少见了。

  2、在公域内容里的佣金计算上做了大刀阔斧的调整。一份CPS,扣除阿里系提走的3成,剩下的7成,内容生产者只能立刻获得其中的10%(也就是整个CPS的7%),还有9成(相当于63%)被置入资金池,由阿里进行动态分配。大致上,就是对所谓好内容但可能转化不高进行扶持。

  阿里于今年启动daren.taobao.com,中文名字叫阿里创作平台。号称要在三年里投入100亿来扶持内容。

长文:内容产业的赢家与输家

  请注意上方的频道投稿。

  基于身份标签——官方根据具体情况,赋予不同的标签,比如图文类、短视频类、主播类(不开店)、红人类(开店的)、淘女郎(一般背后有经纪公司)等等。据说,这种身份标签高达上百个——内容生产者可以申请相应的频道开通,并针对性地进行写作。比如投在“有好货”频道,内容就有可能出现手淘首页的有好货板块。但频道投稿有非常严格的格式要求,不是像微信公号那样,想怎么写就怎么写,想怎么排版就怎么排版。

  另外商家也可以发布V任务,由内容生产者去申领,其结算方式以CPM为主。

  当下,月入过十万的生产者,已经超过1500人——其实这个数字并不算太低。微信公号月入过十万的,又有多少呢?

  而对于阿里来说,内容有效缓解了整个淘系资源紧缺,商家囚徒困境的局面。我个人认为,最新一季财报的亮眼表现,其背后,与内容是分不开的。

  十四

  有赢家就有输家。

  百度是当仁不让的输家之一,在贴吧事件之后魏则西事件之前,百度的财务表现就出了问题。我专门写过《这两年百度发生了什么》分析它13、14、15年的状况。

  百度更冤的一件事是:多少公号狗拿着百度写(pin)稿子,收入赚得盆满钵满,却和百度一点关系都没有。

  百度还有机会么?

  我不知道。

  十五

  有个小公司,我看有机会。

  这家小公司的创始人叫徐达内,公司名字叫新榜,一向是做2B的生意,估值最近在一轮华人、华盖投完后,也快近十亿人民币了——比起上面的赢家输家,当然还是小公司。

长文:内容产业的赢家与输家

  这张图反映了做2C和2B的不同。做2C的,做得好的,可以万古流芳,做2B就比较默默无闻了。

  徐达内出身媒体人,对万古流芳这种事很看重。

  于是,他决定启动一个2C的项目。原来想叫“见识”,不巧正好和另外一家也是华人文化基金投资的华尔街见闻做的一个产品撞名,所以现在改名叫“微见”。

  这个产品的核心就是汇聚一帮喜欢扯淡的kol们,对各种新闻、文章进行评头论足。用户主要是去看他们评头论足的。

  如果你知道徐达内早年写过媒体札记这件事的话,你会看到这个事在微见里的影子的。

 转自:微信公众号 扯淡集

]]>
IT互联网http://www.teaching4real.com/internet/766.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=766http://www.jizhuomi.com/cmd.asp?act=tb&id=766&key=83824101
SSH电商项目实战之六:基于DataGrid的数据显示a@b.com (鸡啄米)http://www.jizhuomi.com/software/765.htmlTue, 19 Sep 2017 08:54:19 +0800http://www.jizhuomi.com/software/765.html  EasyUI中DataGrid以表格形式展示数据,并提供了丰富的选择、排序、分组和编辑数据的功能支持。DataGrid的设计用于缩短开发时间,并且使开发人员不需要具备特定的知识。它是轻量级的且功能丰富。单元格合并、多列标题、冻结列和页脚只是其中的一小部分功能。

  1. 回顾一下第4节内容

  在第4节中,我们使用EasyUI搭建好了左侧菜单栏,并且通过点击菜单选项在右边弹出对应的选项卡。这节我们来使用DataGrid把右边的选项卡部分做好。先看一下第4节中最后的aindex.jsp文件(也可参见第4节中的内容):

  2. 创建DataGrid控件的几种方式

  DataGrid显示数据是json格式的,所以我们首先要把从后台获取到的数据打包成Jason格式,然后传到前台来让DataGrid来显示,这一节我们先不从后台获取数据,先自己准备一个.json文件,里面有ison格式的数据,然后我们来让DataGird显示,先把显示功能做好,再请求后台数据。

  我们先从EasyUI的参考文档中看一下DataGrid显示的格式是什么样的,如下图所示:

  我们沿着参考文档往下看,我们发现DataGrid空间是通过<table>来创建的,有三种创建方式:

  第一种:从现有的表格元素创建DataGrid,在HTML中定义列、行和数据。

  第二种:通过<table>标签创建DataGrid控件。在表格内使用<th>标签定义列。

  第三种:使用Javascript去创建DataGrid控件。

  我们采取第三种,用js去创建DataGrid控件,首先我们得先准备一个存储了json格式数据的文件,在WebRoot/jquery-easyui-1.3.5/demo/datagrid/下面有几个json文件,我们选择一个datagrid_data1.json,拷贝到WebRoot目录下,修改一下参数,等会我们要来显示这个json文件里的数据。如下:

JavaScript代码
  1. {"total":10,"rows":[    
  2.     {"code":"FI-SW-01","productname":"Koi","price":10.00},    
  3.     {"code":"K9-DL-01","productname":"Dalmation","price":12.00},    
  4.     {"code":"RP-SN-01","productname":"Rattlesnake","price":12.00},    
  5.     {"code":"RP-LI-02","productname":"Iguana","price":12.00},    
  6.     {"code":"FL-DSH-01","productname":"Manx","price":12.00},    
  7.     {"code":"FL-DSH-01","productname":"Manx","price":12.00},    
  8.     {"code":"FL-DLH-02","productname":"Persian","price":12.00},    
  9.     {"code":"FL-DLH-02","productname":"Persian","price":12.00},    
  10.     {"code":"AV-CB-01","productname":"Amazon Parrot","price":92.00},    
  11.     {"code":"AV-CB-03","productname":"Amazon Parrot","price":92.00}    
  12. ]}    

  我们可以看到,json数据格式是:"key1": value1, "key2":value2。每个value里面又可以是数组,数组中保存新的Jason数据。

  有了json文件,我们接下来就可以设计DataGrid控件了,整个DataGrid都是在query.jsp中设计的,因为要显示的内容就是query.jsp中的内容。我们来看看query.jsp页面:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <script type="text/javascript">    
  7.         $(function(){    
  8.             $('#dg').datagrid({       
  9.                 //请求数据的url地址,后面会改成请求我们自己的url    
  10.                 url:'datagrid_data.json',    
  11.                 loadMsg:'Loading......',    
  12.                 queryParams:{type:''},//参数    
  13.                 //width:300,    
  14.                 fitColumns:true,//水平自动展开,如果设置此属性,则不会有水平滚动条,演示冻结列时,该参数不要设置    
  15.                 //显示斑马线    
  16.                 striped:true,    
  17.                 //当数据多的时候不换行    
  18.                 nowrap:true,    
  19.                 singleSelect:true, //如果为真,只允许单行显示,全显功能失效    
  20.                 //设置分页    
  21.                 pagination:true,    
  22.                 rowStyler: function(index,row){    
  23.                     console.info("index" + index + "," + row)    
  24.                     if(index % 2 == 0) {    
  25.                         return 'background-color:#fff;';    
  26.                     } else {    
  27.                         return 'background-color:#ff0;';    
  28.                     }    
  29.                         
  30.                 },    
  31.                 //同列属性,但是这些列将会冻结在左侧,大小不会改变,当宽度大于250时,会显示滚动条,但是冻结的列不在滚动条内    
  32.                 frozenColumns:[[    
  33.                     {field:'checkbox',checkbox:true},    
  34.                     {field:'code',title:'编号',width:200}                     
  35.                 ]],    
  36.                 //配置datagrid的列字段     
  37.                 //field:列字段的名称,与json的key捆绑    
  38.                 //title:列标题,是显示给人看的    
  39.                 columns:[[                         
  40.                     {field:'productname',title:'类别名称',width:100,    
  41.                         //用来格式化当前列的值,返回的是最终的数据    
  42.                         formatter: function(value,row,index){    
  43.                         return "<span title=" + value + ">" + value + "</span>";    
  44.                     }    
  45.                 },        
  46.                     {field:'price',title:'价格',width:100,    
  47.                     styler: function(value,row,index){    
  48.                         //设置当前单元格的样式,返回的字符串直接交给 style属性    
  49.                         //console.info("val:" + value + ",row:" + row + ",index:" + index)    
  50.                         if (value < 20){    
  51.                             return 'color:red;';    
  52.                         }    
  53.                     }       
  54.                     }    
  55.                 ]]        
  56.             });     
  57.         });    
  58.     </script>    
  59.   </head>    
  60.       
  61.   <body>    
  62.     <table id="dg"></table>    
  63.   </body>    
  64. </html>    

  3. DataGrid控件的属性

  我们可以看到,使用js去创建DataGrid控件的话,只要一个<table>标签即可,主要都是在js中完成。DataGrid的控件很强大,这里我们主要做一下基本的显示,更多其他的功能可以参照EasyUI的开发文档。我们现在针对上面的query.jsp文件做一下分析:

  首先DataGrid控件有两种属性:一个是DataGrid属性,还有一个是列属性。顾名思义,DataGrid属性是给整个DataGrid控件添加的属性,而列属性是针对某一列的。每中属性有很多,这里只做了一些基本的常用的属性。

  DataGrid属性里最重要的是columns属性,它是一个数组,可以创建多列,见下面的截图:

  我们来看下columns属性中有哪些细节:

  列属性中,field表示字段名称,对应与json数据的key,然后title是要显示给用户看的标题,见query.jsp文件中,还有其他一些基本属性可以参照EasyUI文档。列属性中比较重要的也比较常用的两个属性是formatter和styler,分别是用来格式化当前列的值和设置单元格样式的,我们主要来看一下这两个属性:

  我们具体来分析一下上面query.jsp中的columns属性中,如何使用这两个列属性的:

JavaScript代码
  1. {field:'productname',title:'类别名称',width:100,    
  2.     //用来格式化当前列的值,返回的是最终的数据    
  3.     formatter: function(value,row,index){    
  4.         return "<span title=" + value + ">" + value + "</span>";//设置为鼠标放上去显示value值    
  5.     }    
  6. },        
  7. {field:'price',title:'价格',width:100,    
  8.     styler: function(value,row,index){    
  9.         //设置当前单元格的样式,返回的字符串直接交给 style属性    
  10.             //console.info("val:" + value + ",row:" + row + ",index:" + index)    
  11.         if (value < 20){ //如果value值小于20    
  12.             return 'color:red;'//将value值显示为红色    
  13.         }    
  14.     }       
  15. }    

  然后我们再看看DataGrid控件的一些属性:

  url表示要显示的数据来源,这里设置成datagrid_data.json表示数据来源是这个json文件,放在WebRoot目录下了;

  loadMsg表示加载数据过程中显示的信息;

  queryParams表示传给后台的参数,在这里用不到,因为我们目前还没有和后台关联上,只是显示一个json文件,后面会用到;

  fitColums设置为true后表示水平自动展开,自适应网格的宽度,如此设置,水平方向就不会有滚动条了,也不用设置宽度了;

  width是宽度,如果数据过长显示不下,水平方向就会出现滚动条;

  striped设置为true后表示显示斑马线,这是一个显示样式,试一下便知;

  nowrap设置为true后表示当数据多的时候不换行,否则某一行数据多的时候会换行,会比较难看;

  pagination设置为true后表示开启分页功能;

  singleSelect设置为true时,只允许勾选单行,全选功能失效,主要用于最前面一列的复选框;

  frozenColums是为了设置冻结列,在frozenColums中设置的列,不会改变大小。里面如果设置了{field:'checkbox',checkbox:true},表示这是个复选框列,给用户勾选用的,如果设置了上面的singleSelect,那么只能选择一项,不能全选;

  rowStyler是设置所有行的样式的,两个参数为行索引和行,上面设置的是偶数行是白色,奇数行是黄色。

  等等……还有其他DataGrid控件的属性,可以参考EasyUI的技术文档,在这里就不一一解说了。

  4. DataGrid数据显示的效果

  好了,完成了query.jsp后,我们重启tomcat,然后进入到后台,点击左侧菜单栏的类别管理,就会在右边出现一个类别管理的选项卡,然后就会显示我们指定的json数据,这个Jason数据是我们自己放在WebRoot目录下的,后面我们将会把json和struts整合,动态的获取从后台传过来的json数据。

]]>
软件开发http://www.teaching4real.com/software/765.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=765http://www.jizhuomi.com/cmd.asp?act=tb&id=765&key=33675b20
SSH电商项目实战之五:完成数据库的级联查询和分页a@b.com (鸡啄米)http://www.jizhuomi.com/software/764.htmlWed, 13 Sep 2017 08:53:38 +0800http://www.jizhuomi.com/software/764.html  上一节我们完成了EasyUI菜单的实现。这一节我们主要来写一下CategoryServiceImpl实现类,完成数据库的级联查询。一般项目从后往前做,先做service(我们没有抽取Dao,最后再抽取),做完了再做上面层。

  在写之前,先看一下数据库中的表的情况:

SQL代码
  1. drop database if exists shop;    
  2. /*创建数据库,并设置编码*/    
  3. create database shop default character set utf8;    
  4.     
  5. use shop;    
  6. /*删除管理员表*/    
  7. drop table if exists account;    
  8. /*删除最新送白菜网类别表*/    
  9. drop table if exists category;    
  10.     
  11. /*============================*/    
  12. /*      Table:管理员表结构                       */    
  13. /*============================*/    
  14. create table account    
  15. (    
  16.     /* 管理员编号,自动增长 */    
  17.     id int primary key not null auto_increment,    
  18.     /* 管理员登录名 */    
  19.     login varchar(20),    
  20.     /* 管理员姓名 */    
  21.     name varchar(20),    
  22.     /* 管理员密码 */    
  23.     pass varchar(20)    
  24. );    
  25.     
  26. /*============================*/    
  27. /*     Table:最新送白菜网类别表结构                      */    
  28. /*============================*/    
  29. create table category    
  30. (    
  31.    /* 类别编号,自动增长 */    
  32.    id  int primary key not null auto_increment,    
  33.    /* 类别名称 */    
  34.    type varchar(20),    
  35.    /* 类别是否为热点类别,热点类别才有可能显示在首页*/    
  36.    hot  bool default false,    
  37.    /* 外键,此类别由哪位管理员管理 */    
  38.    account_id int,    
  39.    constraint aid_FK foreign key(account_id) references account(id)    
  40. );    

  主要有两张表,最新送白菜网类别表和管理员表,并且最新送白菜网类别表中提供了一个外键关联管理员表。也就是最新送白菜网和管理员是多对一的关系。现在我们开始编写查询最新送白菜网的类别信息,需要级联管理员。

  1. 实现级联查询方法

  首先在CategoryService接口中定义该方法:

Java代码
  1. public interface CategoryService extends BaseService<Category> {    
  2.     //查询类别信息,级联管理员    
  3.     public List<Category> queryJoinAccount(String type); //使用类别的名称查询    
  4. }   

  然后我们在CategoryService的实现类CategoryServiceImpl中实现这个方法:

Java代码
  1. @Service("categoryService")    
  2. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  3.     
  4.     @Override    
  5.     public List<Category> queryJoinAccount(String type) {    
  6.         String hql = "from Category c where c.type like :type";    
  7.         return getSession().createQuery(hql)    
  8.                 .setString("type""%" + type + "%").list();    
  9.     }    
  10. }    

  在两个Model中我们配一下关联注解:

Java代码
  1. //Category类中    
  2. @ManyToOne(fetch = FetchType.EAGER)    
  3. @JoinColumn(name = "account_id")    
  4. public Account getAccount() {    
  5.     return this.account;    
  6. }    
  7. //Account类中    
  8. @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "account")    
  9. public Set<Category> getCategories() {    
  10.     return this.categories;    
  11. }    

  然后我们在测试类中测试一下:

Java代码
  1. @RunWith(SpringJUnit4ClassRunner.class)    
  2. @ContextConfiguration(locations="classpath:beans.xml")    
  3. public class CategoryServiceImplTest {    
  4.     
  5.     @Resource    
  6.     private CategoryService categoryService;    
  7.         
  8.     @Test    
  9.      public void testQueryJoinAccount() {    
  10.         for(Category c : categoryService.queryJoinAccount("")) {    
  11.              System.out.println(c);    
  12.              System.out.println(c.getAccount());    
  13.         }    
  14.     }    
  15. }    

  2. 级联查询存在的问题

  我们看一下控制台的输出可以看出,它发了不止一条SQL语句,但是我们明明只查询了一次,为什么会发这么多语句呢?这就是常见的1+N问题。所谓的1+N问题,就是首先发出一条语句查询当前对象,然后发出N条语句查询关联对象,因此效率变得很低。这里就两个对象,如果有更多的对象,那效率就会大打折扣了,我们该如何解决这个问题呢?

  可能大家会想到将fetch设置生FetchType.LAZY就不会发多条语句了,但是这肯定不行,因为设置成LAZY后,我们就拿不到Account对象了,比较好的解决方法是我们自己写hql语句,使用join fetch。具体看修改后的CategoryServiceImpl实现类:

Java代码
  1. @Service("categoryService")    
  2. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  3.     
  4.     @Override    
  5.     public List<Category> queryJoinAccount(String type) {    
  6.         String hql = "from Category c left join fetch c.account where c.type like :type";    
  7.         return getSession().createQuery(hql)    
  8.                 .setString("type""%" + type + "%").list();    
  9.     }    
  10. }    

  left join表示关联Account一起查询,fetch表示将Account对象加到Category中去,这样就只会发一条SQL语句了,并且返回的Category中也包含了Account对象了。

  3. 完成分页功能

  Hibernate中的分页很简单,只需要调用两个方法setFirstResult和setMaxResults即可:我们修改一下CategoryService接口和它的实现类CategoryServiceImpl:

Java代码
  1. //CategoryService    
  2. public interface CategoryService extends BaseService<Category> {    
  3.     //查询类别信息,级联管理员    
  4.     public List<Category> queryJoinAccount(String type, int page, int size); //并实现分页    
  5. }    
  6.     
  7. //CategoryServiceImpl    
  8. @Service("categoryService")    
  9. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  10.     
  11.     @Override    
  12.     public List<Category> queryJoinAccount(String type, int page, int size) {    
  13.         String hql = "from Category c left join fetch c.account where c.type like :type";    
  14.         return getSession().createQuery(hql)    
  15.                 .setString("type""%" + type + "%")    
  16.                 .setFirstResult((page-1) * size) //从第几个开始显示    
  17.                 .setMaxResults(size) //显示几个    
  18.                 .list();    
  19.     }    
  20. }    

  我们在测试类中测试一下:

Java代码
  1. @RunWith(SpringJUnit4ClassRunner.class)    
  2. @ContextConfiguration(locations="classpath:beans.xml")    
  3. public class CategoryServiceImplTest {    
  4.     
  5.     @Resource    
  6.     private CategoryService categoryService;    
  7.     
  8.     @Test    
  9.     public void testQueryJoinAccount() {    
  10.         for(Category c : categoryService.queryJoinAccount("",1,2)) { //显示第一页,每页2条数据    
  11.             System.out.println(c + "," + c.getAccount());    
  12.         }    
  13.     }    
  14. }    

  为此,我们写完了Service的方法了,完成了对最新送白菜网类别的级联查询和分页功能。

]]>
软件开发http://www.teaching4real.com/software/764.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=764http://www.jizhuomi.com/cmd.asp?act=tb&id=764&key=07866ec7
SSH电商项目实战之四:EasyUI菜单的实现a@b.com (鸡啄米)http://www.jizhuomi.com/software/763.htmlMon, 11 Sep 2017 08:40:59 +0800http://www.jizhuomi.com/software/763.html  上一节我们使用EasyUI搭建了后台页面的框架,这一节我们主要使用EasyUI技术简单实现后台菜单,先将简单功能做出来,后期再继续丰富。(EasyUI下载地址

  1. 实现左侧菜单

  首先看一下效果图:

SSH电商项目实战之四:EasyUI菜单的实现

  我们可以点击“基本操作”和“其他操作”来切换菜单选项,在具体的选项内,点击不同的连接,会在右侧显示出来。我们先把左边的菜单做出来。

  左侧菜单内容主要有两个:“类别管理”和“最新送白菜网管理”。我们知道,上一节中,在aindex.jsp中应将后台页面的框架搭建好了,那么现在我们只要做好这两个超链接,然后放到aindex.jsp中相应的div中即可。所以我们先在WebRoot文件夹下新建一个temp.jsp文件作为临时开发文件,因为在这里写jsp可以直接测出来,等效果可以后,再将内容复制到aindex.jsp中的相应位置。

  temp.jsp页面如下:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <style type="text/css">    
  7.         #menu {    
  8.             width:200px;    
  9.             /*border:1px solid red;*/    
  10.         }    
  11.         #menu ul {    
  12.             list-style: none;    
  13.             padding: 0px;    
  14.             margin: 0px;    
  15.         }    
  16.         #menu ul li {    
  17.             border-bottom: 1px solid #fff;    
  18.                 
  19.         }    
  20.         #menu ul li a {    
  21.             /*先将a标签转换为块级元素,才能设置宽和内间距*/    
  22.             display: block;    
  23.             background-color: #00a6ac;    
  24.             color: #fff;    
  25.             padding: 5px;    
  26.             text-decoration: none;    
  27.         }    
  28.         #menu ul li a:hover {    
  29.             background-color: #008792;    
  30.         }    
  31.             
  32.     </style>    
  33.   </head>    
  34.       
  35.   <body>    
  36.     <div id="menu">       
  37.         <ul>    
  38.             <li><a href="#">类别管理</a>    
  39.             <li><a href="#">最新送白菜网管理</a>    
  40.         </ul>    
  41.     </div>    
  42.   </body>    
  43. </html>    

  temp.jsp中只做了两个链接,用li封装起来并放到div中,上面css是给这两个链接设置样式的,然后我们开启tomcat,测试一下效果如下:

SSH电商项目实战之四:EasyUI菜单的实现

  做好了这两个超链接后,我们将封装两个超链接的ul拷贝到aindex.jsp中的左侧菜单内容显示位置,并简要的修改,如下:

SSH电商项目实战之四:EasyUI菜单的实现

  css部分直接考到aindex.jsp的head标签里即可。看上面那个a标签,里面是title属性,并不是href,因为我们不是跳转到新的页面,因为EasyUI就这一个页面,我们要让点击后的显示放到右边的tab选项卡那里,所以我们先把跳转的action写在title属性里,后面再改。接下来,我们要通过点击类别管理,在右边的选项卡中弹出具体类容的功能。

  2. 实现右侧tab选项卡

  实现点击左边菜单栏弹出右边选项卡的功能,需要加入js代码了。使用EasyUI的思路是:首先点击超链接,拿到这个超链接的名字,因为弹出来的选项卡标题应该和这个超链接的名字一样的,比如“类别管理”;然后判断改名字的选项卡是否已经存在,如果存在则显示,如果不存在则创建,并显示要显示的内容。我们来看下js部分的代码:

XML/HTML代码
  1. <script type="text/javascript">    
  2.     $(function(){    
  3.         $("a[title]").click(function(){    
  4.             var text = $(this).text();    
  5.             var href = $(this).attr("title");    
  6.             //判断当前右边是否已有相应的tab    
  7.             if($("#tt").tabs("exists", text)) {    
  8.                 $("#tt").tabs("select", text);    
  9.             } else {    
  10.                 //如果没有则创建一个新的tab,否则切换到当前tag    
  11.                 $("#tt").tabs("add",{    
  12.                     title:text,    
  13.                     closable:true,    
  14.                     content:'<iframe title=' + text + 'src=' + href + ' frameborder="0" width="100%" height="100%" />'    
  15.                     //href:默认通过url地址加载远程的页面,但是仅仅是body部分    
  16.                     //href:'send_category_query.action'    
  17.                 });    
  18.             }    
  19.                     
  20.         });    
  21.     });    
  22. </script>    

  我们来分析下这段js代码,首先拿到a标签,注意这个a标签是带title属性的a标签,也就是我们上面的“类别管理”超链接,然后点击,click里面又有一个function,这个function都干啥了呢?首先获取当前链接的名字,即text,然后通过title属性拿到url(因为我们刚刚把url写到title属性了),接下来判断是否已经有这个名字的选项(tab),如果有则显示该名字的选项,如果没有则创建。

  我们来具体看看if里面的语句,首先通过"#tt"拿到右边部分的jquery对象,然后调用tabs构造方法即拿到tab对象,如果有则返回true,否则返回false。那么tabs()里面的两个参数是什么意思呢?首先第一个参数是方法名,第二个参数是第一个参数(方法)对应的参数,tabs("exists", text)表示调用EasyUI的exists方法,参数为text,即判断名字为text的tab是否存在,同样,下面的tabs("select", text)表示选择名字为text的tab显示,tabs("add", {})表示新创建一个tab,{}里添加新添加tab的一些属性:title表示名字,closable:true表示有关闭按钮,即右上角的叉叉,content表示要显示的内容从哪获得,后面用<iframe>标签将一个页面的内容给包进来,这个页面不能直接访问,是通过action跳转的,从action的名字中可以看出,是引入WEB-INF/category/query.jsp页面。如果我们在该页面中的body标签中随便写个内容,然后通过点击左边菜单栏,就会在右边选项卡中显示内容。如下:

SSH电商项目实战之四:EasyUI菜单的实现

  最后把aindex.jsp中的代码放到这里来:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4. <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.     <style type="text/css">    
  7.         #menu {    
  8.             width:60px;    
  9.             /*border:1px solid red;*/    
  10.         }    
  11.         #menu ul {    
  12.             list-style: none;    
  13.             padding: 0px;    
  14.             margin: 0px;    
  15.         }    
  16.         #menu ul li {    
  17.             border-bottom: 1px solid #fff;    
  18.                 
  19.         }    
  20.         #menu ul li a {    
  21.             /*先将a标签转换为块级元素,才能设置宽和内间距*/    
  22.             display: block;    
  23.             background-color: #00a6ac;    
  24.             color: #fff;    
  25.             padding: 5px;    
  26.             text-decoration: none;    
  27.         }    
  28.         #menu ul li a:hover {    
  29.             background-color: #008792;    
  30.         }    
  31.             
  32.     </style>    
  33.         
  34.     <script type="text/javascript">    
  35.         $(function(){    
  36.             $("a[title]").click(function(){    
  37.                 var text = $(this).text();    
  38.                 var href = $(this).attr("title");    
  39.                 //判断当前右边是否已有相应的tab    
  40.                 if($("#tt").tabs("exists", text)) {    
  41.                     $("#tt").tabs("select", text);    
  42.                 } else {    
  43.                     //如果没有则创建一个新的tab,否则切换到当前tag    
  44.                     $("#tt").tabs("add",{    
  45.                         title:text,    
  46.                         closable:true,    
  47.                         content:'<iframe src="send_category_query.action" frameborder="0" width="100%" height="100%" />'    
  48.                         //href:默认通过url地址加载远程的页面,但是仅仅是body部分    
  49.                         //href:'send_category_query.action'    
  50.                     });    
  51.                 }    
  52.                     
  53.             });    
  54.         });    
  55.     </script>    
  56. </head>    
  57.     
  58.     <body class="easyui-layout">    
  59.         <div data-options="region:'north',title:'欢迎来到易购后台管理',split:true" style="height:100px;"></div>       
  60.         <div data-options="region:'west',title:'系统操作',split:true" style="width:200px;">    
  61.             <!-- 此处显示的是系统菜单 -->    
  62.             <div id="menu" class="easyui-accordion" data-options="fit:true">       
  63.                 <div title="基本操作" data-options="iconCls:'icon-save'">       
  64.                     <ul>    
  65.                         <li><a href="#" title="send_category_query.action">类别管理</a>    
  66.                         <li><a href="#">最新送白菜网管理</a>    
  67.                     </ul>    
  68.                 </div>       
  69.                 <div title="其他操作" data-options="iconCls:'icon-reload'">    
  70.                     <ul>    
  71.                         <li><a href="#">类别管理</a>    
  72.                         <li><a href="#">最新送白菜网管理</a>    
  73.                     </ul>    
  74.                 </div>       
  75.             </div>       
  76.         </div>       
  77.         <div data-options="region:'center',title:'后台操作页面'" style="padding:1px;background:#eee;">    
  78.             <div id="tt" class="easyui-tabs" data-options="fit:true">       
  79.                 <div title="系统缺省页面" style="padding:10px;">    
  80.                     此处以后显示相应的系统信息(当前操作系统的类型,当前项目的域名,硬件的相关配置或者显示报表    
  81.                 </div>       
  82.                     
  83.             </div>                        
  84.         </div>       
  85.     </body>      
  86.     
  87. </html>    

  很明显,代码没有抽取,css和js都混在一个jsp页面了,没关系,后面会一起抽取的。

  到此为止,我们完成了EasyUI菜单的实现,这里只是将实现方法完成了,具体显示的内容后面根据具体需求再完善。

 

]]>
软件开发http://www.teaching4real.com/software/763.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=763http://www.jizhuomi.com/cmd.asp?act=tb&id=763&key=ba94d3ff
SSH电商项目实战之三:使用EasyUI搭建后台页面框架a@b.com (鸡啄米)http://www.jizhuomi.com/software/762.htmlWed, 06 Sep 2017 09:10:09 +0800http://www.jizhuomi.com/software/762.html  前面两节,我们整合了SSH并且抽取了service和action部分的接口,可以说基本开发环境已经搭建好了,这一节我们搭建一下后台的页面。我们讨论一下两种搭建方式:基于frameset和基于easyUI。最后我们会使用easyUI来开发。

  1. 抽取公共JSP页面

  我们先来看一下当前的jsp页面:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
  3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  4. <html>    
  5.   <head>    
  6.     
  7.   </head>    
  8.       
  9.   <body>    
  10.     <!-- 省略…… -->    
  11.     </c:forEach>    
  12.   </body>    
  13. </html>  

  先撇开body部分的内容不看,因为这都是之前测试用的,抽取JSP页面是指将一些共有部分抽取出来到一个新的JSP页面,然后在当前JSP页面中包含进来。因为后期项目中肯定会引入js、css等文件,如果在每个jsp页面都写的话,会很冗余,所以我们得抽取一个共有的jsp来引入这些文件以及其他东西。我们在WebRoot目录下新建一个public文件夹,在里面新建一个head.jspf(jspf表示JSP片段,供其他JSP页面包含的)。

XML/HTML代码
  1. <%@ page language="java" pageEncoding="utf-8"%>    
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
  3. <c:set value="${pageContext.request.contextPath }" var="shop" />    
  4. <title>易购商城</title>    
  5. <!--     
  6. <script type="text/javascript" src=""></script>    
  7. <style type="text/css"></style>    
  8.  -->  

  注释部分主要是包含js和css,因为目前还没用到,只是搭建一个框架,等用到了再去掉。<c:set>标签将${pageContext.request.contextPath }用${shop}来代替,方便书写。这样以后新的JSP只要包含这个head.jspf即可。我们看一下修改后的index.jsp:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4.   <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6.   </head>    
  7.       
  8.   <body>    
  9.       <!-- 省略…… -->    
  10.   </body>    
  11. </html>   

  是不是有种面向对象的思想~

  2. 基于frameset搭建后台页面

  2.1 发现问题

  模板抽取好了,现在我们开始搭建后台页面框架了,首先我们使用frameset来做。在WEB-INF目录下新建一个文件夹main用来保存后台的主要页面,在main中新建四个jsp文件:aindex.jsp、top.jsp、left.jsp和right.jsp。我们的frameset写在aindex.jsp中,其他三个只是简单写一句话用来测试,我们来看看aindex.jsp:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4. <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6. </head>    
  7. <!-- 框架体,里面包含了3+1个页面 -->    
  8. <frameset border="5" rows="150,*">    
  9.     <frame src="top.jsp" />    
  10.     <frameset border="5" cols="150,*">    
  11.         <frame src="left.jsp" />    
  12.         <frame src="right.jsp" />    
  13.     </frameset>    
  14. </frameset>      
  15.     
  16. </html>  

  结构很明显,将页面分为3块,上左右。每个模块包含相应的jsp页面,然后我们在index.jsp的body中写入<a href="/WEN-INF/main/aindex.jsp">测试到后台</a>,启动tomcat,发现点击链接是无法访问到后台的。原因在于WEB-INF目录下的jsp不能直接跳转,需要通过Servlet或者Action来跳转。那没办法,只能新写一个跳转的Action了。

  2.2 编写页面跳转的Action

  我们首先写一个Action来完成页面的跳转,该Action只是单纯的实现页面跳转,不处理任何业务逻辑。

Java代码
  1. /**  
  2.  * @Description: TODO(此Action用来完成WEB-INF中JSP与JSP请求转发功能,此Action不处理任何的逻辑)  
  3.  * @author eson_15  
  4.  *  
  5.  */    
  6. public class SendAction extends ActionSupport {    
  7.         
  8.     public String execute() {    
  9.         return "send";    
  10.     }    
  11. }    

  我们可以看出,SendAction没有继承我们之前抽取的BaseAction,只是单纯的继承了ActionSupport。然后我们在struts.xml文件中配置一下:

XML/HTML代码
  1. <?xml version="1.0" encoding="utf-8" ?>    
  2. <!DOCTYPE struts PUBLIC    
  3.     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"    
  4.     "http://struts.apache.org/dtds/struts-2.3.dtd">    
  5.     
  6. <struts>    
  7.     <package name="shop" extends="struts-default">    
  8.     
  9.                 <!-- 全局result,对这个package中的所有action有效 --></span>     
  10.         <global-results>    
  11.             <result name="aindex">/WEB-INF/main/aindex.jsp</result>    
  12.         </global-results>    
  13.     
  14.         <!-- 省略其他action的配置……</span> -->    
  15.             
  16.         <!-- 用来完成系统 请求转发的action,所有的请求都交给execute-->    
  17.         <action name="send_*_*" class="sendAction">    
  18.             <result name="send">/WEB-INF/{1}/{2}.jsp</result>    
  19.         </action>    
  20.     </package>    
  21.     
  22. </struts>    

  别忘了在beans.xml中配置:<bean id="sendAction" class="cn.it.shop.action.SendAction" />。

  这个action中之所以配两个*号是为了便于访问WEB-INF/*/*.jsp,这需要在jsp中约定好地址的写法了。下面我们看一下aindex.jsp中的写法:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4. <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6. </head>    
  7. <!-- 框架体,里面包含了3+1个页面 -->    
  8. <frameset border="5" rows="150,*">    
  9.     <frame src="send_main_top.action" />    
  10.     <frameset border="5" cols="150,*">    
  11.         <frame src="send_main_left.action" />    
  12.         <frame src="send_main_right.action" />    
  13.     </frameset>    
  14. </frameset>      
  15.     
  16. </html>  

  从修改后的aindex.jsp中可以看出,我们不直接访问WEB-INF/下的jsp(我们也访问不了),我们通过Action去跳转,这样我们在index.jsp的body中写入<a href="send_main_aindex.action">测试到后台</a>,然后启动tomcat,就可以点开链接正常访问后台主页面了。

  从上面使用frameset搭建后台页面的过程来看,还是挺麻烦的,它是一个个页面包含进来的,开发中也不会用frameset,而easyUI只有一个页面,所有的请求都是AJAX请求,接下来我们看一下如何使用easyUI来搭建后台页面。

  3. 基于EasyUI搭建后台页面

  jQuery EasyUI是一组基于jQuery的UI插件集合体,而jQuery EasyUI的目标就是帮助web开发者更轻松的打造出功能丰富并且美观的UI界面。开发者不需要编写复杂的javascript,也不需要对css样式有深入的了解,开发者需要了解的只有一些简单的html标签。(EasyUI下载地址)

  3.1 导入EasyUI相关组件

  我们先在工程中的WebRoot目录下导入EasyUI所需要的组件,网上都有下载,我用的是jquery-easyui-1.3.5,去掉一些不需要的东西,最后的结果如下:

SSH电商项目实战之三:使用EasyUI搭建后台页面框架

  3.2 搭建EasyUI的环境

  我们打开刚刚抽取出来的head.jspf文件,在这里导入EasyUI所依赖的css和js,其他页面引入该jspf文件即可间接引入了EasyUI所依赖的css和js了:

XML/HTML代码
  1. <%@ page language="java" pageEncoding="utf-8"%>    
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
  3. <c:set value="${pageContext.request.contextPath }" var="shop" />    
  4. <title>易购商城</title>    
  5.     
  6. <!-- 下面是easyui的环境 -->    
  7. <link rel="stylesheet" href="${shop }/jquery-easyui-1.3.5/themes/icon.css" type="text/css"></link>    
  8. <link rel="stylesheet" href="${shop }/jquery-easyui-1.3.5/themes/default/easyui.css" type="text/css"></link>    
  9. <script type="text/javascript" src="${shop }/jquery-easyui-1.3.5/jquery.min.js"></script>    
  10. <script type="text/javascript" src="${shop }/jquery-easyui-1.3.5/jquery.easyui.min.js"></script>    
  11. <script type="text/javascript" src="${shop }/jquery-easyui-1.3.5/locale/easyui-lang-zh_CN.js"></script>    

  3.3 搭建后台的框架

  将WEB-INF/main/目录下的top.jsp、left.jsp和right.jsp都删掉,因为现在用不上了,然后修改aindex.jsp页面,现在可以使用EasyUI来做了:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  3. <html>    
  4. <head>    
  5.     <%@ include file="/public/head.jspf" %>    
  6. </head>    
  7.     
  8.     <body class="easyui-layout">    
  9.         <div data-options="region:'north',title:'North Title',split:true" style="height:100px;"></div>       
  10.         <div data-options="region:'west',title:'West',split:true" style="width:200px;">    
  11.             <!-- 此处显示的是系统菜单 -->    
  12.             <div id="aa" class="easyui-accordion" style="width:300px;height:200px;">       
  13.                 <div title="Title1" data-options="iconCls:'icon-save'" style="overflow:auto;padding:10px;">       
  14.                     <h3 style="color:#0099FF;">Accordion for jQuery</h3>       
  15.                     <p>Accordion is a part of easyui framework for jQuery.         
  16.                     It lets you define your accordion component on web page more easily.</p>       
  17.                 </div>       
  18.                 <div title="Title2" data-options="iconCls:'icon-reload',selected:true" style="padding:10px;">content2</div>       
  19.                 <div title="Title3">content3</div>       
  20.             </div>       
  21.         </div>       
  22.         <div data-options="region:'center',title:'center title'" style="padding:5px;background:#eee;"></div>       
  23.     </body>      
  24.     
  25. </html>    

  这里的这么多<div>都是参照上面那个EasyUI的说明文档,我在下面贴出来。先进行整个layout布局,去掉我们不需要的,我们只要north、west和center三部分:

SSH电商项目实战之三:使用EasyUI搭建后台页面框架

  再在west部分的div中加上accordon分类的布局,将代码添加到head.jspf中:

SSH电商项目实战之三:使用EasyUI搭建后台页面框架

  这样我们就简单搭建好了后台的页面框架了,后期只要往里面填入东西就行了。我们在index.jsp中测试一下:<a href="send_main_aindex.action">直接到后台EasyUI版</a>,这样jsp就会找我们刚刚写好的SendAction然后跳转到EWB-INF/main/aindex.jsp,就能正确显示后台框架了,如下:

SSH电商项目实战之三:使用EasyUI搭建后台页面框架

  至此,我们使用EasyUI成功搭建好了后台页面的框架。

 转自:倪升武的CSDN博客

]]>
软件开发http://www.teaching4real.com/software/762.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=762http://www.jizhuomi.com/cmd.asp?act=tb&id=762&key=2c91c636
SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xmla@b.com (鸡啄米)http://www.jizhuomi.com/software/761.htmlMon, 04 Sep 2017 09:37:14 +0800http://www.jizhuomi.com/software/761.html  上一节我们搭建好了Struts2、Hibernate和Spring的开发环境,并成功将它们整合在一起。这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。

  1. Service层的抽取

  上一节中,我们在service层简单写了save和update方法,这里我们开始完善该部分的代码,然后对service层的代码进行抽取。

  1.1 完善CategoryService层

  对数据库的操作无非是增删改查,首先我们来完善CategoryService层的接口和实现:

Java代码
  1. //CategoryService接口    
  2. public interface CategoryService extends BaseService<Category> {    
  3.         
  4.     public void save(Category category); //插入    
  5.     
  6.     public void update(Category category);//更新    
  7.         
  8.     public void delete(int id); //删除    
  9.         
  10.     public Category get(int id); //获取一个Category    
  11.         
  12.     public List<Category> query(); //获取全部Category    
  13.     
  14. }    

  对CategoryService接口的具体实现:

Java代码
  1. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  2.     
  3.     private SessionFactory sessionFactory;    
  4.         
  5.     //Spring会注进来    
  6.     public void setSessionFactory(SessionFactory sessionFactory) {    
  7.         this.sessionFactory = sessionFactory;    
  8.     }    
  9.         
  10.     protected Session getSession() {    
  11.         //从当前线程获取session,如果没有则创建一个新的session    
  12.         return sessionFactory.getCurrentSession();    
  13.     }    
  14.          
  15.     @Override     
  16.     public void save(Category category) {    
  17.         getSession().save(category);    
  18.     }    
  19.         
  20.     @Override     
  21.     public void update(Category category) {    
  22.         getSession().update(category);      
  23.     }    
  24.     
  25.     @Override    
  26.     public void delete(int id) {    
  27.         /*第一种方法有个弊端,就是没删除一次得先查询一次  
  28.         Object obj = getSession().get(Category.class, id);  
  29.         if(obj != null) {  
  30.             getSession().delete(obj);  
  31.         }*/    
  32.         String hql = "delete Category while id=:id";    
  33.         getSession().createQuery(hql) //    
  34.                 .setInteger("id", id) //    
  35.                 .executeUpdate();    
  36.     }    
  37.     
  38.     @Override    
  39.     public Category get(int id) {    
  40.         return (Category) getSession().get(Category.class, id);    
  41.     }    
  42.     
  43.     @Override    
  44.     public List<Category> query() {    
  45.         String hql = "from Category";    
  46.         return getSession().createQuery(hql).list();    
  47.     }    
  48. }    

  1.2 Service层抽取实现

  完成了CategoryService后,我们来抽取Service层的基础实现。思路是这样的:我们抽取一个基础接口BaseService以及基础接口的实现BaseServiceImpl,后面开发的时候,如果需要新的Service,只需要做两步即可:首先定义一个新的接口xxxService继承BaseService接口,这个接口可以增加新的抽象方法;然后定义一个新的实现类xxxServiceImpl继承BaseServiceImpl并实现xxxService接口即可。这样更加便于项目的维护。

  我们先根据上面的CategoryService接口来创建BaseService接口:

Java代码
  1. //基础接口BaseService,使用泛型    
  2. public interface BaseService<T> {    
  3.     public void save(T t);    
  4.     
  5.     public void update(T t);    
  6.         
  7.     public void delete(int id);    
  8.         
  9.     public T get(int id);    
  10.         
  11.     public List<T> query();    
  12. }    

  然后再根据CategoryServiceImpl实现类创建BaseService接口的实现类BaseServiceImpl:

Java代码
  1. /**  
  2.  * @Description TODO(公共模块的抽取)  
  3.  * @author eson_15  
  4.  *  
  5.  */    
  6. @SuppressWarnings("unchecked")    
  7. public class BaseServiceImpl<T> implements BaseService<T> {    
  8.     
  9.     private Class clazz; //clazz中存储了当前操作的类型,即泛型T    
  10.     private SessionFactory sessionFactory;    
  11.         
  12.     public BaseServiceImpl() {    
  13.                 //下面三个打印信息可以去掉,这里是给自己看的    
  14.                 System.out.println("this代表的是当前调用构造方法的对象" + this);    
  15.         System.out.println("获取当前this对象的父类信息" + this.getClass().getSuperclass());    
  16.         System.out.println("获取当前this对象的父类信息(包括泛型信息)" + this.getClass().getGenericSuperclass());    
  17.         //拿到泛型的参数类型    
  18.         ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();    
  19.         clazz = (Class)type.getActualTypeArguments()[0];    
  20.     }    
  21.         
  22.     public void setSessionFactory(SessionFactory sessionFactory) {    
  23.         this.sessionFactory = sessionFactory;    
  24.     }    
  25.         
  26.     protected Session getSession() {    
  27.         //从当前线程获取session,如果没有则创建一个新的session    
  28.         return sessionFactory.getCurrentSession();    
  29.     }    
  30.         
  31.     @Override    
  32.     public void save(T t) {    
  33.         getSession().save(t);    
  34.     }    
  35.     
  36.     @Override    
  37.     public void update(T t) {    
  38.         getSession().update(t);     
  39.     }    
  40.     
  41.     @Override    
  42.     public void delete(int id) {    
  43.         System.out.println(clazz.getSimpleName());    
  44.         String hql = "delete " + clazz.getSimpleName() + " as c where c.id=:id";    
  45.         getSession().createQuery(hql) //    
  46.                   .setInteger("id", id) //    
  47.                   .executeUpdate();    
  48.     }    
  49.     
  50.     @Override    
  51.     public T get(int id) {    
  52.         return (T) getSession().get(clazz, id);    
  53.     }    
  54.     
  55.     @Override    
  56.     public List<T> query() {    
  57.         String hql = "from " + clazz.getSimpleName();    
  58.         return getSession().createQuery(hql).list();    
  59.     }    
  60.     
  61. }    

  抽取完了后,我们就可以改写CategoryService接口和CategoryServiceImpl实现类了。如下:

Java代码
  1. //CategoryService接口继承BaseService接口    
  2. public interface CategoryService extends BaseService<Category> {    
  3.     /*  
  4.         * 只要添加CategoryService本身需要的新的方法即可,公共方法已经在BaseService中了  
  5.         */    
  6. }    
  7.     
  8. /**  
  9.  * @Description TODO(模块自身的业务逻辑)  
  10.  * @author eson_15  
  11.  *  
  12.  */    
  13. public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {    
  14.     
  15.     /*  
  16.      * 只需实现CategoryService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了  
  17.      */    
  18. }    

  从代码中可以看出,新增的Service只需要继承BaseService接口,然后在接口中新增本Service所需要的业务逻辑即可。新增的ServiceImpl只需要继承BaseServiceImpl并实现新增的业务逻辑即可。

  但是别忘了很重要的一点:就是修改Spring的配置文件beans.xml中的bean。

XML/HTML代码
  1. <!-- 泛型类是不能实例化的,所以要加lazy-init属性 -->    
  2. <bean id="baseService" class="cn.it.shop.service.impl.BaseServiceImpl" lazy-init="true">    
  3.      <property name="sessionFactory" ref="sessionFactory" />    
  4. </bean>    
  5.          
  6. <bean id="categoryService" class="cn.it.shop.service.impl.CategoryServiceImpl" parent="baseService"/>    

  将原来categoryService中的property干掉,然后增加parent属性,指明继承baseService;然后配置一下baseService,将sessionFactory配到baseService中去,另外要注意一点:设置lazy-init属性为true,因为baseService是泛型类,泛型类是不能实例化的。至此,Service层的抽取就搞定了。

  2. Service层添加一个Account

  刚刚抽取好了Service层,那么现在我们想写一个Account(管理员)的service就很简单了:

  首先写一个AccountService接口继承BaseService:

Java代码
  1. public interface AccountService extends BaseService<Account> { //注意BaseService里的泛型现在是Account    
  2.     /*  
  3.      * 只要添加AccountService本身需要的新的方法即可,公共方法已经在BaseService中了  
  4.      */    
  5. }    

  然后写一个AccountServiceImpl实现类继承BaseServiceImpl实现类,并实现AccountService接口即可:

Java代码
  1. public class AccountServiceImpl extends BaseServiceImpl<Account> implements AccountService {    
  2.     
  3.     /*  
  4.      * 只需实现AccountService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了  
  5.      */    
  6.         
  7.     //管理登陆功能,后期再完善    
  8. }    

  最后在beans.xml文件里加上如下配置:

XML/HTML代码
  1. <bean id="accountService" class="cn.it.shop.service.impl.AccountServiceImpl" parent="baseService" />

  这样就写好了一个新的service了,以后需要添加service就遵循这个流程,非常方便。

  3. Action的抽取

  3.1 Action中往域(request,session,application等)中存数据

  我们知道,在Action中可以直接通过ActionContext.getContext()去获取一个ActionContext对象,然后通过该对象再去获得相应的域对象;也可以通过实现xxxAware接口来注入相应的域对象。我们先来看一下这两种方法:

Java代码
  1. public class CategoryAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{    
  2.         
  3.     private Category category;    
  4.     
  5.         private CategoryService categoryService;    
  6.         
  7.         public void setCategoryService(CategoryService categoryService) {    
  8.             this.categoryService = categoryService;    
  9.         }    
  10.     
  11.     public String update() {    
  12.         System.out.println("----update----");    
  13.         categoryService.update(category);    
  14.         return "index";    
  15.     }    
  16.         
  17.     public String save() {    
  18.         System.out.println("----save----");    
  19.         return "index";    
  20.     }    
  21.         
  22.     public String query() {    
  23.          //解决方案一,采用相应的map取代原来的内置对象,这样与jsp没有依赖,但是代码量比较大    
  24.  //     ActionContext.getContext().put("categoryList", categoryService.query()); //放到request域中    
  25.  //     ActionContext.getContext().getSession().put("categoryList", categoryService.query()); //放到session域中    
  26.  //     ActionContext.getContext().getApplication().put("categoryList", categoryService.query()); //放到application域中    
  27.             
  28.         //解决方案二,实现相应的接口(RequestAware,SessionAware,ApplicationAware),让相应的map注入    
  29.         request.put("categoryList", categoryService.query());     
  30.         session.put("categoryList", categoryService.query());     
  31.         application.put("categoryList", categoryService.query());     
  32.         return "index";    
  33.     }    
  34.     
  35.     public Category getCategory() {    
  36.         return category;    
  37.     }    
  38.     
  39.     public void setCategory(Category category) {    
  40.         this.category = category;    
  41.     }    
  42.         
  43.     private Map<String, Object> request;    
  44.     private Map<String, Object> session;    
  45.     private Map<String, Object> application;    
  46.     
  47.     @Override    
  48.     public void setApplication(Map<String, Object> application) {    
  49.         this.application = application;    
  50.     }    
  51.     
  52.     @Override    
  53.     public void setSession(Map<String, Object> session) {    
  54.         this.session = session;    
  55.     }    
  56.     
  57.     @Override    
  58.     public void setRequest(Map<String, Object> request) {    
  59.         this.request = request;    
  60.     }    
  61. }    

  还是上一节整合三大框架时的CategoryAction类,我们在里面加了一个query方法,在该方法中,我们通过向request域、session域和application域中存入查询的结果。第一种方法是直接使用ActionContext来实现,不需要实现任何接口,但是代码量较大;第二种方法通过实现RequestAware、SessionAware和ApplicationAware接口,实现该接口的三个抽象方法把request、session和application注入进来,然后赋给相应的成员变量中,这样就可以在query方法中向域中存放查询结果了。这代码量貌似比第一种方法更大……但是我们可以抽取,先往下看。

  我们在index.jsp中新加一个查询连接来测试能否将查询结果显示出来:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
  3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  4. <html>    
  5.   <head>    
  6.     <title>My JSP 'index.jsp' starting page</title>    
  7.   </head>    
  8.       
  9.   <body>    
  10.     <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>    
  11.     <a href="category_save.action">访问save</a>    
  12.     <a href="category_query.action">查询所有类别</a><br/>    
  13.     <c:forEach items="${requestScope.categoryList }" var="category">    
  14.         ${category.id } | ${category.type } | ${category.hot } <br/>    
  15.     </c:forEach>    
  16.         
  17.     <c:forEach items="${sessionScope.categoryList }" var="category">    
  18.         ${category.id } | ${category.type } | ${category.hot } <br/>    
  19.     </c:forEach>    
  20.         
  21.     <c:forEach items="${applicationScope.categoryList }" var="category">    
  22.         ${category.id } | ${category.type } | ${category.hot } <br/>    
  23.     </c:forEach>    
  24.   </body>    
  25. </html>    

  3.2 抽取BaseAction

  刚刚提到了,第二种方法的代码量更大,但是我们可以抽取一个BaseAction,专门处理这些域相关的操作。

Java代码
  1. public class BaseAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware {    
  2.     
  3.     protected Map<String, Object> request;    
  4.     protected Map<String, Object> session;    
  5.     protected Map<String, Object> application;    
  6.         
  7.     @Override    
  8.     public void setApplication(Map<String, Object> application) {    
  9.         this.application = application;    
  10.     }    
  11.     
  12.     @Override    
  13.     public void setSession(Map<String, Object> session) {    
  14.         this.session = session;    
  15.     }    
  16.     
  17.     @Override    
  18.     public void setRequest(Map<String, Object> request) {    
  19.         this.request = request;    
  20.     }    
  21. }    

  然后我们自己的Action如果需要用到这些域对象来存储数据时,直接继承BaseAction即可,就能直接使用request、session和application对象了。所以修改后的CategoryAction如下:

Java代码
  1. public class CategoryAction extends BaseAction {            
  2.     private Category category;       
  3.     private CategoryService categoryService;      
  4.           
  5.     public void setCategoryService(CategoryService categoryService) {      
  6.         this.categoryService = categoryService;      
  7.     }  
  8.   
  9.     public String update() {  
  10.         System.out.println("----update----");  
  11.         categoryService.update(category);   
  12.         return "index";   
  13.     }  
  14.   
  15.     public String save() {  
  16.         System.out.println("----save----");  
  17.         return "index";   
  18.     }   
  19.   
  20.     public String query() {  
  21.         request.put("categoryList",categoryService.query());   
  22.         session.put("categoryList",categoryService.query());   
  23.         application.put("categoryList",categoryService.query()); return "index";   
  24.     }   
  25.   
  26.     public Category getCategory() { return category; }   
  27.   
  28.     public void setCategory(Category category) {this.category = category; }  
  29. }   

  后面所有要使用request、session和application域的Action,只要直接继承BaseAction即可,非常方便。

  3.3 获取参数(ModelDriven)

  我们继续看上面的CategoryAction类,里面有个成员变量category,这是个POJO,定义这个变量并写好set和get方法是为了JSP页面可以通过url后面附带参数传进来,参数是category对象中的属性,比如id,type等,但是url中的参数必须写成category.id、category.type等。这样struts会自动将这写参数注入到category对象中,然后我们就可以直接使用这个category对象了,但是这样有点繁琐。我们可以使用ModelDriven来更方便的解决。

Java代码
  1. public class CategoryAction extends BaseAction implements ModelDriven<Category>{    
  2.         
  3.     private Category category;    
  4.         
  5.     //使用ModelDriven接口必须要实现getModel()方法,此方法会把返回的项压到栈顶    
  6.     @Override    
  7.     public Category getModel() {    
  8.         category = new Category();    
  9.         return category;    
  10.     }    
  11.    
  12.     private CategoryService categoryService;    
  13.         
  14.     public void setCategoryService(CategoryService categoryService) {    
  15.         this.categoryService = categoryService;    
  16.     }    
  17.     
  18.     public String update() {    
  19.         System.out.println("----update----");    
  20.         categoryService.update(category);    
  21.         return "index";    
  22.     }    
  23.         
  24.     public String save() {    
  25.         System.out.println("----save----");    
  26.         return "index";    
  27.     }    
  28.         
  29.     public String query() {    
  30.         request.put("categoryList", categoryService.query());     
  31.         session.put("categoryList", categoryService.query());     
  32.         application.put("categoryList", categoryService.query());     
  33.         return "index";    
  34.     }    
  35.     
  36. }    

  这样我们在前台JSP页面就不用带category.id这种繁琐的参数了,看JSP页面中的ModelDriven部分:

XML/HTML代码
  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>    
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
  3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    
  4. <html>    
  5.   <head>    
  6.     <title>My JSP 'index.jsp' starting page</title>    
  7.   </head>    
  8.       
  9.   <body>    
  10.     <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>    
  11.     <a href="category_save.action?id=1&type=haha&hot=true">测试ModelDriven</a>    
  12.     <a href="category_query.action">查询所有类别</a><br/>    
  13.     <c:forEach items="${requestScope.categoryList }" var="category">    
  14.         ${category.id } | ${category.type } | ${category.hot } <br/>    
  15.     </c:forEach>    
  16.         
  17.     <c:forEach items="${sessionScope.categoryList }" var="category">    
  18.         ${category.id } | ${category.type } | ${category.hot } <br/>    
  19.     </c:forEach>    
  20.         
  21.     <c:forEach items="${applicationScope.categoryList }" var="category">    
  22.         ${category.id } | ${category.type } | ${category.hot } <br/>    
  23.     </c:forEach>    
  24.   </body>    
  25. </html>    

  测试结果是可以获得catgory,并且将id,type和hot属性全部赋值好。我们可以看出,通过实现ModelDriven接口,我们可以很方便的在url中携带参数,Action中只需要实现getModel方法,new一个要使用的对象返回即可。到这里我们很容易想到,struts中肯定会有很多这种model需要获取,所以这一块我们也要抽取到BaseAction中去。

  3.4 抽取ModelDriven到BaseAction

  首先我们在BaseAction中添加ModelDriven部分的代码,如下:

Java代码
  1. //因为有很多不同的model都需要使用ModelDriven,所以这里使用泛型    
  2. public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {    
  3.     
  4.     protected Map<String, Object> request;    
  5.     protected Map<String, Object> session;    
  6.     protected Map<String, Object> application;    
  7.         
  8.     protected T model;    
  9.         
  10.     @Override    
  11.     public void setApplication(Map<String, Object> application) {    
  12.         this.application = application;    
  13.     }    
  14.     
  15.     @Override    
  16.     public void setSession(Map<String, Object> session) {    
  17.         this.session = session;    
  18.     }    
  19.     
  20.     @Override    
  21.     public void setRequest(Map<String, Object> request) {    
  22.         this.request = request;    
  23.     }    
  24.     
  25.     @Override    
  26.     public T getModel() { //这里通过解析传进来的T来new一个对应的instance    
  27.         ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();    
  28.         Class clazz = (Class)type.getActualTypeArguments()[0];    
  29.         try {    
  30.             model = (T)clazz.newInstance();    
  31.         } catch (Exception e) {    
  32.             throw new RuntimeException(e);    
  33.         }       
  34.         return model;    
  35.     }    
  36. }    

  抽取完了后,CategoryAction中的代码会越来越少:

Java代码
  1. //继承BaseAction,并且加上泛型    
  2. public class CategoryAction extends BaseAction<Category> {    
  3.     
  4.     private CategoryService categoryService;    
  5.         
  6.     public void setCategoryService(CategoryService categoryService) {    
  7.         this.categoryService = categoryService;    
  8.     }    
  9.         
  10.     public String update() {    
  11.         System.out.println("----update----");    
  12.         categoryService.update(model);//直接使用model    
  13.         return "index";    
  14.     }    
  15.         
  16.     public String save() {    
  17.         System.out.println("----save----");    
  18.         System.out.println(model); //直接使用model    
  19.         return "index";    
  20.     }    
  21.         
  22.     public String query() {     
  23.         request.put("categoryList", categoryService.query());     
  24.         session.put("categoryList", categoryService.query());     
  25.         application.put("categoryList", categoryService.query());     
  26.         return "index";    
  27.     }    
  28.     
  29. }    

  到这里,还有一个看着不爽的地方,就是categoryService这个成员变量,它一直存在在CategoryAction里,因为CategoryAction中有用到categoryService对象中的方法,所以必须得创建这个对象,并且有set方法才能注入进来。这就导致一个弊端:如果很多Action都需要使用categoryService的话,那就必须在它们的Action里创建这个对象和set方法,而且,如果一个Action中要使用好几个不同的service对象,那就得全部创建,这样就变得很冗杂。

  3.5 抽取service到BaseAction

  针对上面的问题,我们将工程中所有的service对象都抽取到BaseAction中创建,这样其他Action继承BaseAction后,想用什么service就直接拿来用即可:

Java代码
  1. //我将BaseAction中的内容归归类了    
  2. public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {    
  3.     
  4.     //service对象    
  5.     protected CategoryService categoryService;    
  6.     protected AccountService accountService;    
  7.         
  8.     public void setCategoryService(CategoryService categoryService) {    
  9.         this.categoryService = categoryService;    
  10.     }    
  11.     public void setAccountService(AccountService accountService) {    
  12.         this.accountService = accountService;    
  13.     }    
  14.     
  15.     //域对象    
  16.     protected Map<String, Object> request;    
  17.     protected Map<String, Object> session;    
  18.     protected Map<String, Object> application;    
  19.             
  20.     @Override    
  21.     public void setApplication(Map<String, Object> application) {    
  22.         this.application = application;    
  23.     }    
  24.     @Override    
  25.     public void setSession(Map<String, Object> session) {    
  26.         this.session = session;    
  27.     }    
  28.     @Override    
  29.     public void setRequest(Map<String, Object> request) {    
  30.         this.request = request;    
  31.     }    
  32.         
  33.     //ModelDriven    
  34.     protected T model;    
  35.     @Override    
  36.     public T getModel() {    
  37.         ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();    
  38.         Class clazz = (Class)type.getActualTypeArguments()[0];    
  39.         try {    
  40.             model = (T)clazz.newInstance();    
  41.         } catch (Exception e) {    
  42.             throw new RuntimeException(e);    
  43.         }       
  44.         return model;    
  45.     }    
  46. }    

  这样CategoryAction中就更加清爽了:

Java代码
  1. public class CategoryAction extends BaseAction<Category> {    
  2.         
  3.     public String update() {    
  4.         System.out.println("----update----");    
  5.         categoryService.update(model);    
  6.         return "index";    
  7.     }    
  8.         
  9.     public String save() {    
  10.         System.out.println("----save----");    
  11.         System.out.println(model);    
  12.         return "index";    
  13.     }    
  14.         
  15.     public String query() {    
  16.         request.put("categoryList", categoryService.query());     
  17.         session.put("categoryList", categoryService.query());     
  18.         application.put("categoryList", categoryService.query());     
  19.         return "index";    
  20.     }    
  21.     
  22. }    

  有人可能会问,BaseAction中注入了那么多service对象的话不会冗余么?这是不会的,因为就算不写在BaseAction中,Spring容器也是会创建这个对象的,这点没有关系,相反,service对象全放在BaseAction中更加便于其他Action的开发,而且BaseAction不需要配到struts.xml文件中,因为根本就没有哪个JSP会请求BaseAction,它只是让其他Action来继承用的。

  还有一点别忘了:那就是修改在beans.xml中的配置:

XML/HTML代码
  1. <!-- 如果是prototype类型,默认是使用时创建,不是启动时自动创建 -->    
  2. <bean id="baseAction" class="cn.it.shop.action.BaseAction" scope="prototype">    
  3.      <property name="categoryService" ref="categoryService"></property>    
  4.      <property name="accountService" ref="accountService"></property>    
  5. </bean>    
  6.          
  7. <bean id="categoryAction" class="cn.it.shop.action.CategoryAction" scope="prototype" parent="baseAction"/>    

  新加一个baseAction的bean,将工程中所有service对象作为property配好,将原来的categoryAction中的property干掉。

  以后我们如果要写新的xxxAction,直接继承BaseAction即可,如果xxxAction中有用到某个service,直接拿来用即可,只需要在beans.xml文件中加一个xxxAction对应的bean,在struts.xml文件中配置好跳转即可。

  4. 将xml改成注解

  我们可以看到,随着项目越写越大,beans.xml中的配置会越来越多,而且很多配置有冗余,为了更加便于开发,我们现在将xml的配置改成注解的形式,我们先看一下beans.xml中的配置:

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

  这些是我们之前搭建环境以及抽取的时候写的bean,这些都需要转换成注解的形式,下面我们一块一块的换掉:首先替换service部分,这部分有三个:baseService、categoryService和accountService。替换如下:

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

  然后将beans.xml中的相应部分干掉即可。接下来修改ActIon部分,主要有baseAction、categoryAction和accountAction三个,替换如下:

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

SSH电商项目实战之二:基本增删查改、Service和Action的抽取以及使用注解替换xml

  然后再干掉beans.xml中的Action部分的配置即可,最后在beans.xml文件中添加一个如下配置,就可以使用注解了。

XML/HTML代码
  1. <context:component-scan base-package="cn.it.shop.."/>    

  有人可能会问,为什么service和action两个使用注解的时候不一样呢?service中使用的是@Service而action中使用的是@Controller呢?其实是一样的,只是为了区分它们是不同层的bean而已,便于阅读。

转自:倪升武的CSDN博客

 

 

]]>
软件开发http://www.jizhuomi.com/software/761.html#commenthttp://www.jizhuomi.com/http://www.jizhuomi.com/feed.asp?cmt=761http://www.jizhuomi.com/cmd.asp?act=tb&id=761&key=270f2715