运用struts2和ajax进行动态对象数据的加载

    现在的项目中,存在着许多的列表选择数据,比如在一个界面中有许多的选择项。通过这些选择项是由一个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

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

邮箱地址不会被公开。 必填项已用*标注