【问题标题】:Can I implement lazy loading with jqGrid?我可以用 jqGrid 实现延迟加载吗?
【发布时间】:2012-04-05 02:47:10
【问题描述】:

我有一个包含超过 5000 条数据记录的网格。这些数据每天都在增长。当我使用网格加载页面时,网格显示数据需要将近一分钟,而我必须一次显示 10 行。

是否可以用这个 jqGrid 实现延迟加载?

这是我生成 JSon 字符串的操作:

@RequestMapping(value = "studentjsondata", method = RequestMethod.GET)
public @ResponseBody String studentjsondata(HttpServletRequest httpServletRequest) {
    Format formatter = new SimpleDateFormat("MMMM dd, yyyy");
    String column = "id";
    if(httpServletRequest.getParameter("sidx") != null){
        column = httpServletRequest.getParameter("sidx");
    }
    String orderType = "DESC";
    if(httpServletRequest.getParameter("sord") != null){
        orderType = httpServletRequest.getParameter("sord").toUpperCase();
    }
    int page = 1;
    if(Integer.parseInt(httpServletRequest.getParameter("page")) >= 1){
        page = Integer.parseInt(httpServletRequest.getParameter("page"));
    }
    int limitAmount = 10;
    int limitStart = limitAmount*page - limitAmount;
    List<Student> students = Student.findAllStudentsOrderByColumn(column,orderType,limitStart,limitAmount).getResultList();  
    List<Student> countStudents = Student.findAllStudents();
    double tally = Math.ceil(countStudents.size()/10.0d);
    int totalPages = (int)tally;
    int records = countStudents.size();


    StringBuilder sb = new StringBuilder();
    sb.append("{\"page\":\"").append(page).append("\", \"records\":\"").append(records).append("\", \"total\":\"").append(totalPages).append("\", \"rows\":[");
    boolean first = true;
    for (Student s: students) {
        sb.append(first ? "" : ",");
        if (first) {
            first = false;
        }
        sb.append(String.format("{\"id\":\"%s\", \"cell\":[\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"]}",s.getId(), s.getId(), s.getFirstName(), s.getLastName(),  formatter.format(s.getDateOfBirth().getTime()), s.getGender(), s.getMaritalStatus()));
    }
    sb.append("]}");
    return sb.toString();
}

这是带有 jqGrid 的页面:

$("#studentGrid").jqGrid({
            url: '/starburst/programmes/studentjsondata',
            datatype: 'json',
            height: 'auto',
            colNames:['id','First Name', 'Last Name', 'Date Of Birth', 'Gender', 'Marital Status'], 
            colModel:[ 
                {name:'id',index:'id', width:15}, 
                {name:'firstName',index:'firstName', width:30, formoptions:{elmprefix:'(*) '}, editable:true, edittype: 'text', editrules:{required:true}},
                {name:'lastName',index:'lastName', width:30, formoptions:{elmprefix:'(*) '}, editable:true, edittype: 'text',editrules:{required:true}},
                {name:'dateOfBirth',index:'dateOfBirth', width:30, formoptions:{elmprefix:'(*) '},editrules:{required:true}, editable:true, edittype: 'text',               
                    editoptions: {
                        dataInit: function(element) {
                            $(element).datepicker({dateFormat: 'MM dd, yy'})
                        }
                    } 
                },                    
                {name:'gender',index:'gender', width:30, formoptions:{elmprefix:'(*) '}, editable:true, editrules:{required:true}, edittype: 'select',
                    editoptions:{value:{}}
                },
                {name:'maritalStatus',index:'maritalStatus', width:30, formoptions:{elmprefix:'(*) '}, editable:true, editrules:{required:true}, edittype: 'select',
                    editoptions:{value:{}}
                }                    
            ],
            rowNum:10,
            autowidth: true,
            pager: '#pager', 
            sortname: 'id', 
            viewrecords: true, 
            sortorder: "desc",
            caption:"Students",
            emptyrecords: "Empty Records",
            subGrid: true,
            /* <![CDATA[ */ 
            onSelectRow: function(id){ 
                if((lastsel != 0)&&(id!==lastsel)){ 
                    $("#studentGrid").jqGrid('collapseSubGridRow', lastsel);                
                } 
                lastsel=id; 
            }/* ]]> */ 
        });
        $("#studentGrid").jqGrid('navGrid', "#pager", {edit:false,add:false,del:false,search:true},{ },{ },{ },
        { 
            sopt:['eq', 'ne', 'lt', 'gt', 'cn', 'bw', 'ew'],
            closeOnEscape: true, 
            multipleSearch: true, 

请参阅下面的查询:

public static TypedQuery<tt.edu.sbcs.model.Student> findAllStudentsOrderByColumn(String column, String orderType, int limitStart, int limitAmount) {
    EntityManager em = Programme.entityManager();
    TypedQuery<Student> q = em.createQuery("SELECT o FROM Student AS o ORDER BY"+" "+column+" "+orderType, Student.class);
    q.setFirstResult(limitStart);//used to skip the first "N"  elements form the result set, it indexes results form zero 
    q.setMaxResults(limitAmount);
    return q;
}

我在上面的操作中设置的限制金额。此值为 10。

【问题讨论】:

  • 你有没有试过看看瓶颈在哪里?在 JS/HTML 渲染中还是在 servlet 处理程序代码中?您已经可以在您的方法中添加一些时间计算并打印执行所需的时间。至少你会知道你需要改进的地方。据我所知, findAllStudents 看起来是一种非常无效的对象计数方式(计数查询应该更有效)。此外,当您获取下一页时,您应该只获取该页面的对象,而不是前一个页面(仅获取对象 10 到 20 而不是 1 到 20)
  • 我正在使用 firebug 来检测执行时间。目前大约是 46 秒。正如您所说,我不确定如何获取对象。您能建议如何实现这一目标吗?
  • 您使用的是什么技术?通过 JDBC 的原始 SQL? JPA?休眠?还有什么?它还取决于 DB 的类型:MySQL、PostgresQL、Oracle、H2、HSQL 等...? Firebug 是一个很好的工具,但您是否尝试过测量 Java 方法的执行时间?当您想要优化时,您需要确保您实际上是在优化一个瓶颈,因此,您必须首先确保问题确实出在您的 Java 代码中。 System.currentTimeInMillis() 在方法的开头和结尾之间的简单区别应该可以做到。
  • 数据库:MySQL,技术:休眠。我现在去看看那个时间。

标签: java javascript jquery spring jqgrid


【解决方案1】:

首先,我发现在 1 分钟内从总共 5000 行中加载 10 行是非常慢。我认为在你的情况下,服务器代码而不是 jqGrid 是瓶颈。

您的代码的第一行非常怀疑

List<Student> countStudents = Student.findAllStudents();

您只需要获得学生的数量,但您似乎获得了所有学生的所有属性,然后在接下来的两行中使用countStudents.size()。最大应该做的事情是这样的

SELECT COUNT(*) FROM dbo.Students

而不是这个意思是你做SELECT * FROM dbo.Students

如果您的代码需要 1 分钟,则可能是您的数据库或函数 findAllStudentsOrderByColumn 的实现存在严重问题。可能您有一些代表实体模型或数据库模型的类。如果您的性能如此糟糕,您必须非常仔细地检查执行数据库访问的代码,或者考虑使用对数据库的更直接访问,您可以直接指定数据库查询。我不是 Java 或 Spring 开发人员,但我可以肯定地说,如果 5000 中的 10 行的请求比 1 秒 多,那么它已经太慢了。

看起来您需要从一个表中返回一些列,包括id。这样你就可以用SELECTlike获取数据了

SELECT TOP(10) id, firstName, lastName, dateOfBirth, gender, maritalStatus
FROM dbo.Students
ORDER BY id

获取第一页数据和类似以下内容

WITH GetAll (id, firstName, lastName, dateOfBirth, gender, maritalStatus) AS (
    SELECT id, firstName, lastName, dateOfBirth, gender, maritalStatus
    FROM dbo.Students
    ORDER BY id
), GetTop (id, firstName, lastName, dateOfBirth, gender, maritalStatus) AS (
    SELECT TOP(20) * FROM GetAll -- skip 2 pages per 10 rows
), GetNext (id, firstName, lastName, dateOfBirth, gender, maritalStatus) AS (
    SELECT TOP(10) a.* FROM GetAll AS a
        LEFT OUTER JOIN GetTop AS t ON t.id = a.id
    WHERE t.id IS NULL
)
SELECT * FROM GetNext

所有下一页。我使用了common table expression (CTE) 语法,但如果您的数据库不支持,您可以使用子查询。

因为您允许按每一列排序,所以您应该在表中的每一列上创建索引以提高排序性能。 (我认为Students表不会被修改,每秒会有很多变化,所以索引不会降低表的性能)。

您应该考虑的另一件事是将序列化更改为 JSON。使用String.format("\"%s\", someString) 是危险的。有些字符必须用\ 字符转义。我的意思是"\。您应该这样做以确保代码安全。通常的做法是使用您的语言中存在的一些标准类进行序列化(例如,请参阅 herehere)。

下一个技巧是使用jsonReader: {cell: ""} 并在表单中返回该行的数据

["%s", "%s", "%s", "%s", "%s", "%s"]

而不是

{"id":"%s", "cell":["%s", "%s", "%s", "%s", "%s", "%s"]}

您不会发送两次id 值,也不会发送字符串"id" "cell" 和其他一些不需要的字符('{'、':'、...)。

在客户端,您应该始终使用gridview: true jqGrid 选项。有 10 行数据你不会看到严重的差异,因为 jqGrid 会很快,但是更多的行差异会很明显。

最后一个建议:你应该使用formatter: 'date'并以ISO 8601格式发送日期:比如2012-03-20

【讨论】:

    【解决方案2】:

    好的,所以这是基于 JPA 的部分答案(但我想将它适应 Hibernate 应该几乎是微不足道的)。你应该能够做这样的事情来只获取必要的对象:

    Query query = em.createQuery("select o from " + "Student"+ " as o order by o.id");
    query.setFirstResult(start);
    query.setMaxResults(end - start);
    return query.getResultList();
    

    算起来,应该这样做:

    Number count = (Number) em.createQuery("select count(id) from " + "Student").getSingleResult();
    if (count == null) {
        count = Integer.valueOf(0);
    }
    return count.intValue();
    

    当我有更多信息时会编辑。

    【讨论】:

    • 我已经编辑了上面的问题。请查阅。另请注意,java 代码运行的时间是 36 秒。
    猜你喜欢
    • 2010-10-25
    • 2018-04-02
    • 2019-11-15
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多