现在的项目中,存在着许多的列表选择数据,比如在一个界面中有许多的选择项。通过这些选择项是由一个select下拉列表来进行选择的,而加载这些下拉列表,除普通的在action中主动获取之外,另外一种方式进行ajax方式在界面加载时加载。
在我们的项目中,现在运用的即是在界面加载时,通过ajax方式来加载相应的列表数据,这样的好处即是不需要在表单加载时,由处理表单的action来生成相应的列表数据,而是交给产生这个列表数据的action来加载数据。现在存在的主要问题就是,每个列表的数据来源不一样,这就导致了需要写不同的加载代码来加载这些数据,而每个加载数据的action代码分布在不同的处理action中(比如用户数据由UserAction处理,字典数据由DictionaryAction处理),而每个加载逻辑还需要根据不同的参数进行不同的service转发,而经以dao来获取数据。在界面上,相应的js加载代码也不尽相同,但大体逻辑均是相同(即获取数据,填充列表)。
解决问题的方法
即是将所有的通用数据加载逻辑进行统一封装,前台js入口统一,后台逻辑统一。比如,通过load[User]用于加载用户数据,load[Dictionary]用于加载字典数据,用不同的参数来进行相应的数据加载。
大致思路
界面上传递几个关键性的参数(加载类,过滤参数,显示参数,以及排序参数),统一传递给action,action对加载类作判断,再对过滤参数进行参数处理(过滤非法参数),然后从数据库中进行数据加载(考虑排序),之后再对显示参数进行参数处理(过滤非法参数),最后将信息通过ajax的方法传递给界面,由界面进行列表数据构建并展现。
界面实现效果
1,用户选择一个加载数据类型,
2,选择之后,根据选择的数据类,再由action将这个类可以供显示的字段列举出来,以选择要显示的字段信息。
3,再在参数传递中,填入传递的过滤参数,比如username=bbbb,即限制加载的类的属性username应该为bbbb,最后点击(加载数据)按钮,由action处理,将数据展现在下面的table列表中。
以上的展现只是一个实现上的展现,而下边的展现,则是应用上的展现。
4,当点击(加载项目)或(加载用户)按钮时,相应的select便会根据相应的请求参数,加载相应的数据了。如下所示
在实际的项目中,相应列表数据的加载,均是已知条件或条件已经可以确定了,所以实际的加载时,并不会点击某个按钮来加载,而是直接在启动函数(如jquery.onload)中写相应的数据加载代码,通过一个js函数调用,并可以直接加载相应的数据,而不需要再重新为不同的加载类型和参数重新定制后台代码了。
技术实现
数据模型
我们构建以下这些数据模型,用于封装相应的参数及数据信息。
1,Map<String, String> p:参数信息,由界面传递过来的参数。如p['username'](用户名)或['role.id'](角色id)
2,Set<String> f:界面显示字段信息,最终哪些字段将会展现给界面,而不是全部数据均展现
3,String order:排序字段,即结果按照哪个字段进行排序
4,boolean by = true:排序是按照升序还是降序
5,Class<? extends Entityable> clazz:请求的加载数据类型
由于是采用struts2,所以相应的set和get还是少不了的。
加载数据限制
为避免加载的数据过多(实际上,能够用select列表封装的数据,条目肯定不会太多),我们在action中对加载的数据条目进行了限制,即是通过一个page参数,作相应限制,并最终传递给dao以通过hibernate进行数据加载限制。如下代码:
@Override public void createPage() { super.createPage(); page.setTotalCount(1);//不进行总数加载 page.setPageSize(100);//最多加载100条数据 }
action加载逻辑
加载逻辑,即是首先创建分页参数(即限制条目,再将相应的参数传递给service进行处理,并返回加载数据结果,再处理显示字段参数,最后将结果通过ajax显示在界面上。相应的主要代码如下:
createPage(); entityableList = domainLoadService.loadDomain(clazz, p, order, by, page); //准备输出字段信息 ...... //处理显示字段,过滤掉不被支持的字段,如无效字段,非simpleType类型 //输出信息,通过ajax方式将结果中指定字段信息以json形式输出到界面上 AjaxSupport.sendSuccessTextOnly(null, entityableList, f.toArray(new String[f.size()]));
service处理逻辑
处理逻辑首先分析传递参数,即进行非法参数过滤(不存在的字段参数,以及非简单类型参数),然后处理排序参数(是否需要排序),再根据加载类型,过滤参数,排序参数通过detachedCriteria进行条件构建,最后联合page参数一起由dao进行数据加载。大致代码如下:
Map<String, Object> paramData = analyzeParam(clazz, p); logger.debug("valid param->" + paramData); boolean validOrder = StringUtils.hasText(order) && BeanUtils.getSimplePropertyType(clazz, order, true, true) != null; //准备构建条件语句 DetachedCriteria detachedCriteria = DetachedCriteria.forClass(clazz); for(Map.Entry<String, Object> e : paramData.entrySet()) detachedCriteria.add(Restrictions.eq(e.getKey(), e.getValue())); if(validOrder) detachedCriteria.addOrder(by ? Order.asc(order) : Order.desc(order)); return baseDAO.listByCriteria(detachedCriteria, page);
主要的难点在分析传递参数中,首先要排除空参数(即空字符串),然后再看这个参数字段是否存在于相应的类中(参数是否有效),再检查这个参数字段是否是简单类型(不支持复合类型,如list,实体类等),最后根据参数类型进行相应的数据类型转换,将最终有效的参数重新封装。相应代码如下:
private Map<String, Object> analyzeParam(Class<? extends Entityable> clazz, Map<String, String> p) { Map<String, Object> paramMap = new HashMap<String, Object>(); for(Map.Entry<String, String> e : p.entrySet()) { String key = e.getKey(); String value = e.getValue(); //如果无值,则忽略 if(!StringUtils.hasText(value)) continue; //如果找不到对应的属性信息,则忽略 Class<?> type = BeanUtils.getSimplePropertyType(clazz, key, true, true); if(type == null) continue; Object v; try { v = typeConverter.convertIfNecessary(value, type); } catch(Exception ignore) { continue; } paramMap.put(key, v); } return paramMap; }
前端js展现
在前台通过js来进行select数据加载即是很容易了,我们封装了一个叫loadDomain的函数,用于访问action,接收数据再填充到select。如下所示:
function loadDomain(select, clazz, valueKey, contentKey) { $(select).invoke("/develop/domainLoad/do/listAjax", { clazz:clazz,f:[valueKey,contentKey] }, function(re) { Gtip.util.fillSelect(this, re["result"], valueKey, contentKey); }); }
其中select表示在加载的select对象,clazz表示要加载哪个数据类,valueKey指在option中的value值所在的字段,contentKey表示在option中的text所在的字段。
示例:
如在前台的(加载项目中),我需要调用如下代码,即可完成数据的加载。
loadDomain($("#itemSelect"), "com.greejoy.develop.domain.Item", "id", "value");
总结:这个东西还是很简单的,主要就是处理简单的动态数据加载,以减少一些数据加载代码到处写的问题。因为大部分的select列表数据加载的条件均是很简单的,所以这里面也没有考虑比如>,<,like,in这些参数语法,而是使用了最简单的=。其次在处理过程中,对联连参数进行了相当的支持(如可以使用p["role.id"]来表示加载角色id为xxx的用户列表),在数据处理上使用了spring的typeConverter来进行处理。其他一些处理,则是依赖于struts2(如map参数传递)以及ajax的数据返回(数据字段过滤)了。
转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/%e8%bf%90%e7%94%a8struts2%e5%92%8cajax%e8%bf%9b%e8%a1%8c%e5%8a%a8%e6%80%81%e5%af%b9%e8%b1%a1%e6%95%b0%e6%8d%ae%e7%9a%84%e5%8a%a0%e8%bd%bd.html