【问题标题】:Iterate a Multimap with JSP使用 JSP 迭代 Multimap
【发布时间】:2011-06-10 04:57:47
【问题描述】:

我正在尝试编写一个显示多台服务器备份状态的备份仪表板。这个想法是显示一个带有 JSP 的表,该表在列中具有最近几天的日期,在行中具有服务器名称。在这个穷人的表格中,我写了是/否值。

+------------+------------+------------+------------+
+ Host Name  | 2011-06-10 | 2011-06-09 | 2011-06-08 |
+------------+------------+------------+------------+
| web01      |     Y      |      Y     |     N      |
+------------+------------+------------+------------+
| web02      |     Y      |      Y     |     Y      |
+------------+------------+------------+------------+

每台服务器都有自己的备份并将状态保存到 Amazon SimpleDb 中,我编写了一个 Java 方法来检索最近几天的这些信息,签名如下:

/**
 * List MySQL backups of the last howManyDays days. It starts from today 
 * included at index 0 and goes back in the past until we have a list of 
 * howManyDays days, even if some day doesn't have any data. Return a list of 
 * dates, each of which contains a list of backup jobs executed by servers in 
 * that day.
 * 
 * @param howManyDays
 *         how many days of backup to show
 * @return a Map where each key is the date in ISO format (2011-06-10) and each
 *         element is a backupJob which is represented by a Map where the key is 
 *         the server name (ex. web01, web01) and the value is "Y" if all was 
 *         fine, otherwise it contains the error message.
 */
public Multimap<String, Map<String, String>> listMysqlBackups(int howManyDays);

Multimap 是 Google Guava Multimap,因为我每天有多个备份。示例输出:

{2011-06-10=[{web06=Y}, {web05=Y}], 2011-06-08=[{web05=Y}, {web06=Y}], 
 2011-06-09=[{web05=Y}, {web06=Y}], 2011-06-07=[{web05=Y}, {web06=Y}]} 

我不知道如何在 JSP 中使用这些信息。我试过 foreach:

<c:forEach items="${backups}" var="backup" varStatus="backupId">
    ${backup.key}
</c:forEach>

答案是:

javax.servlet.ServletException: javax.servlet.jsp.JspTagException: Don't know 
how to iterate over supplied "items" in <forEach>

现在我在想,如果我用一个过于复杂的返回值来打自己的脚,我是否应该返回一个简单的 HashMap ArrayList,其中每个 HashMap 都包含所有需要的信息(日期、主机名、消息)。如果你们认为这是一种更好的方法,我重写提取数据的 Java 方法没有任何问题,但是现在每个单元都需要遍历所有 ArrayList 以获取元素(这可能没问题,因为 6 个服务器乘以 7 days 只有 42 个元素)。

您将如何解决这个问题?

【问题讨论】:

    标签: java jsp jstl guava


    【解决方案1】:

    JSTL forEach 标签不支持Multimaps。它只能遍历标准集合/映射/数组。

    当我需要在 JSP 中迭代 Multimap 时,我使用它的 asMap() 视图。这让我可以使用forEach,因为它知道如何迭代Map 接口。

    如下所示:

    public Multimap<String, Map<String, String>> listMysqlBackups(int howManyDays) {
        // ...
    }
    
    public Map<String, Collection<Map<String, String>>> getListMysqlBackupsAsMap() {
        return listMysqlBackups(this.numberOfDays).asMap();
    }
    
    
    <c:forEach var="backup" items="${bean.listMysqlBackupsAsMap}">
        <c:set var="dateISO" value="${backup.key}/>
        <c:set var="backupJobs" value="${backup.value}/> <!-- a Collection<Map<String,String>> -->
        <c:forEach var="backupJob" items="${backupJobs}">
            <!-- do something with each backup job (Map<String, String>) for the current date -->
        </c:forEach>
    </c:forEach>
    

    如果可以使用 JSP EL 2.1,则不需要额外的 getter。您可以简单地在 JSP 中调用asMap() 来获取Map 视图。


    说了这么多,我不确定您使用Multimap 是否真的能达到您想要的效果。 Multimap&lt;String, Map&lt;String, String&gt;&gt; 将每个键映射到Map&lt;String,String&gt;集合。 在你的情况下,这意味着你有:

    2011-06-09
        --> Collection
            --> Map<String, String>
            --> Map<String, String>
            --> Map<String, String>
    2011-06-10
        --> Collection
            --> Map<String, String>
            --> Map<String, String>
            --> Map<String, String>
    

    我不确定这就是你想要的。我想你想要一个Map&lt;String, Map&lt;String, String&gt;&gt;

    另一种解决方案是使用 Guava 的 Table

    【讨论】:

    • 如果我使用我所说的日期+主机名的键(我什至不需要下划线),我可以下一个简单的 Map 并且已经足够了,并且我不需要循环来获取元素。表看起来也很有趣,但是 JSP forEach 可以处理它吗?否则它会增加复杂性,而目的是降低复杂性。
    • 我接受您对有用和详细解释的回答,尽管我决定使用下面解释的解决方案,因为只有一个 HashMap 更简单,而不是必须遍历三个级别。感谢您的帮助。
    【解决方案2】:

    只是总结一下我所做的事情,并没有声称自己是我回答问题的最佳解决方案。我很想知道使用 google Table 集合是否可以让事情变得更简单。

    将listMysqlBackups的返回类型改为简单的HashMap。

    /**
     * List the MySQL backups of the last howManyDays days. It starts from today and 
     * goes back in the past until we have a list of howManyDays days, even if some 
     * day doesn't have any data. Return a Map with each index as the ISO date 
     * underscore the server name. Key example: 2011-06-11_web01
     * 
     * @param howManyDays
     *         how many days of backup to show
     * @return a Map where each key is the date in ISO format and each element is
     *         a backupJob which is represented by a Map where the key is the server
     *         name (ex. web01, web01) and the value is "Y" if all was fine, 
     *         otherwise it contains the error message.
     */
    public Map<String, String> listMysqlBackups(int howManyDays)  
    

    添加了返回日期列表和服务器列表的新方法。

    public static List<String> listDatesFromToday(int howManyDays) {
        List<String> dates = new ArrayList<String>();
        String currentDay = DateHelper.getCurrentDateAsIso();
        while (howManyDays > dates.size()) {
            dates.add(currentDay);
            currentDay = DateHelper.previousDay(currentDay);
        }
        return dates;
    }
    
    public static List<String> listHosts() {
        return ImmutableList.of("web05", "web06");
    }
    

    使用嵌套循环显示表格。由于我构建密钥的方式,我可以直接查找密钥,而无需在地图中搜索它们。

    <table class="dataTable">
        <tr>
        <th></th>
        <c:forEach items="${days}" var="day">
        <th>${day}${host}</th>
        </c:forEach>
        </tr>
    <c:forEach items="${hosts}" var="host">
        <tr>
        <th>${host}</th>
        <c:forEach items="${days}" var="day">
        <c:set var="key" value="${day}_${host}"/>
        <td> ${backups[key]}  </td>
        </c:forEach>
        </tr>
    </c:forEach>
    </table>
    

    我认为这个解决方案很简单,我对此很满意,但如果你们认为 Google 集合 Table 可以使代码更简单、更短、更简洁,我会很高兴听到。

    【讨论】:

      【解决方案3】:

      我认为你应该尝试嵌套 for 循环

      例如

      <c:forEach items="${webs}" var="web" varStatus="webId">
          <c:forEach items="${web.backups}" var="backup" varStatus="backupId">
              ${backup.key}
          </c:forEach>
      </c:forEach>
      

      【讨论】:

      • 是的,但问题是我什至无法在 Multimap 上编写外部循环,因为 JSP forEach 不知道如何迭代它,如上面显示的错误消息所示。
      • 你能试试任何其他地图,比如HashMap,而不是MultiMap
      • 不具有以下结构,因为每个键都有多个值,因此要么更改数据结构,例如使用我解释的 ArrayList,要么我不能使用 HashMap 或我将覆盖键的值。或者我可以使用带有组合键日期 + 下划线 + 主机名的 HashMap。
      • 实际上考虑我可以创建一个返回日期列表的方法,一个返回要监视的服务器列表的方法,将结果返回为带有上述组合键和嵌套循环的 HashMap在这两个 ArrayList 上,我可以使用索引从 HashMap 中检索值,而无需进一步循环。看起来很合理,现在我尝试实现它。
      猜你喜欢
      • 2015-04-30
      • 2021-08-12
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-06
      • 1970-01-01
      • 2012-09-01
      相关资源
      最近更新 更多