JSP 标签库显示 MySQL 带分组和小计的汇总查询

JSP tag library to display MySQL rollup query with grouping and subtotals

我需要使用 JSP 将几个 table 显示为 HTML,来自 MySQL GROUP BY a,b,c WITH ROLLUP 查询。我正在寻找一个好的标签库来实现这一点。我找到了 DisplayTag,但它最后一次更新是在 2008 年。而且我更喜欢使用 MySQL 计算的小计,这对于 DisplayTag 来说似乎很棘手。

MySQL 按 adding extra rows 对组字段设置为 NULL 的结果集进行小计。

有更好的选择吗?打印 table 很重要,分页和排序会很好,但我可以没有它们。禁止任何形式的编辑。

我写了自己的快速标记。请注意,它需要 MySQL 返回的汇总数据结构,尚未用其他任何东西对其进行测试。

用法示例:

<xxx:rollupTable cssClass="data" data="${data}">
    <xxx:rollupColumn title="Person" align="left" group="true" fieldName="personName" groupFieldName="personId" tooltipLink="person"/>
    <xxx:rollupColumn title="City" align="left" group="true" fieldName="cityName" groupFieldName="cityId" tooltipLink="city"/>
    <xxx:rollupColumn title="Price" align="right" format="#,##0.000" fieldName="price"/>
    <xxx:rollupColumn title="Amount" align="right" format="#,##0" fieldName="amount"/>
</xxx:rollupTable>

列标签的作用不大,只是将列定义添加到 table 标签中以备后用。

package xxx.tags;

import...

public class RollupTableColumnTag extends SimpleTagSupport {
    private String title;
    private boolean group = false;
    private boolean sum = false;
    private String fieldName;       // field name to output
    private String groupFieldName;  // field name to test for rollup level changes
    private String align;
    private String format;
    private String tooltipLink;

    private DecimalFormat formatter;

    public void doTag() throws IOException, JspTagException {
        RollupTableTag parent = (RollupTableTag)findAncestorWithClass(this, RollupTableTag.class);
        if (parent == null) {
            throw new JspTagException("Parent tag not found.");
        }
        parent.addColumnDefinition(this);
    }

    public void setFormat(String format) {
        formatter = new DecimalFormat(format);
        this.format = format;
    }

    public DecimalFormat getFormatter() {
        return formatter;
    }
    // other getters and setters are standard, excluded
}

table 标签完成了实际的艰苦工作:

package xxx.tags;

import ...

public class RollupTableTag extends BodyTagSupport {
    protected String cssClass;
    protected List<Map> data;

    protected List<RollupTableColumnTag> columns;
    protected List<Integer> groups;

    public void setCssClass(String cssClass) {
        this.cssClass = cssClass;
    }

    public void setData(List data) {
        this.data = (List<Map>)data;
    }

    public int doStartTag() throws JspException {
        columns = new ArrayList<RollupTableColumnTag>();
        groups = new ArrayList<Integer>();
        return EVAL_BODY_BUFFERED;
    }

    public int doEndTag() throws JspException {
        try {
            JspWriter writer = pageContext.getOut();

            if (data.size() == 0) {
                writer.println("<P>No data.</P>");
                return EVAL_PAGE;
            }

            int nLevels = groups.size();
            int nNormalRowCount = 0;
            boolean[] bStartGroup = new boolean[nLevels];
            String[] sSummaryTitle = new String[nLevels];
            for (int i=0;i<nLevels;i++) {
                bStartGroup[i] = true;
            }

            writer.println("<TABLE class=\"" + cssClass + "\">");
            writer.println("<THEAD><TR>");
            for (RollupTableColumnTag column : columns) {
                writer.print("<TH");
                if (column.getAlign() != null) {
                    writer.print(" align=\"" + column.getAlign() + "\"");
                }
            writer.print(">" + column.getTitle() + "</TH>");
            }
            writer.println("</TR></THEAD>");
            writer.println("<TBODY>");
            for (Map dataRow : data) {
                StringBuffer out = new StringBuffer();
                out.append("<TR>");
                // grouping columns always come first
                String cellClass = null;
                for (int i=0;i<nLevels-1;i++) {
                    if (bStartGroup[i]) {
                        Object dataField = dataRow.get(columns.get(groups.get(i)).getFieldName());
                        sSummaryTitle[i] = dataField == null ? "" : dataField.toString();
                    }
                }

                int nLevelChanges = 0;
                for (int i=0;i<nLevels;i++) {
                    if (dataRow.get( columns.get(groups.get(i)).getGroupFieldName() ) == null) {
                        if (i>0) {
                            bStartGroup[i-1] = true;
                        }
                        nLevelChanges++;
                    } 
                }
                int nTotalLevel = nLevels - nLevelChanges;

                if (nLevelChanges == nLevels) {         // grand total row
                    cellClass = "grandtotal";
                    addCell(out, "Grand Total:", null, cellClass, nLevelChanges);
                } else if (nLevelChanges > 0) {         // other total row
                    boolean isOneLiner = (nNormalRowCount == 1);
                    nNormalRowCount = 0;
                    if (isOneLiner) continue;   // skip one-line sums
                    cellClass = "total"+nTotalLevel;
                    for (int i=0;i<nLevels-nLevelChanges-1;i++) {
                        addCell(out,"&nbsp;",null,cellClass, 1);
                    }
                    addCell(out, sSummaryTitle[nLevels-nLevelChanges-1] + " total:", null, cellClass, nLevelChanges+1);
                } else {                                // normal row
                    for (int i=0;i<nLevels;i++) {
                        if (bStartGroup[i]) {
                            RollupTableColumnTag column = columns.get(groups.get(i)); 
                            Object cellData = dataRow.get(column.getFieldName());
                            String displayVal = cellData != null ? cellData.toString() : "[n/a]";
                            if (column.getTooltipLink() != null && !column.getTooltipLink().isEmpty() && cellData != null) {
                                String tooltip = column.getTooltipLink();
                                int dataid = Integer.parseInt(dataRow.get(column.getGroupFieldName()).toString());
                                displayVal = "<div ajaxtooltip=\"" + tooltip + "\" ajaxtooltipid=\"" + dataid + "\">" + displayVal + "</div>";
                            }
                            addCell(out, displayVal, column.getAlign(), null, 1);
                        } else {
                            addCell(out,"&nbsp;", null, null, 1);
                        }
                    }
                    for (int i=0;i<nLevels-1;i++) {
                        bStartGroup[i] = false;
                    }
                    nNormalRowCount++;
                }
                // other columns
                for (RollupTableColumnTag column : columns) {
                    if (!column.isGroup()) {
                        Object content = dataRow.get(column.getFieldName());
                        String displayVal = "";
                        if (content != null) {
                            if (column.getFormat() != null) {
                                float val = Float.parseFloat(content.toString());
                                displayVal = column.getFormatter().format(val);
                            } else {
                                displayVal = content.toString();
                            }
                        }
                        addCell(out,displayVal,column.getAlign(),cellClass,1);
                    }
                }
                out.append("</TR>");
                // empty row for better readability
                if (groups.size() > 2 && nLevelChanges == groups.size() - 1) {
                    out.append("<TR><TD colspan=\"" + columns.size() + "\">&nbsp;</TD>");
                }
                writer.println(out);
            }
            writer.println("</TBODY>");
            writer.println("</TABLE>");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return EVAL_PAGE;
    }

    public void addCell(StringBuffer out, String content, String align, String cssClass, int colSpan) {
        out.append("<TD");
        if (align != null) {
            out.append(" align=\"" + align + "\"");
        }
        if (cssClass != null) {
            out.append(" class=\"" + cssClass + "\"");
        }
        if (colSpan > 1) {
            out.append(" colspan=\"" + colSpan + "\"");
        }
        out.append(">");
        out.append(content);
        out.append("</TD>");
    }

    public void addColumnDefinition(RollupTableColumnTag cd) {
        columns.add(cd);
        if (cd.isGroup()) groups.add(columns.size()-1);
    }
}