You need to enable JavaScript to run this app. Spring整合JSF+PrimeFaces时RowEdit事件监听器未触发问题排查
优惠活动
大模型
产品
解决方案
定价
更多

Spring整合JSF+PrimeFaces时RowEdit事件监听器未触发问题排查

Spring + JSF 集成PrimeFaces可编辑表格RowEdit事件失效问题排查

问题现场

问题现象截图

项目核心代码

实体类 Game.java

package org.xtremebiker.jsfspring.model;

import org.springframework.stereotype.Component;
import java.util.Date;

@Component(value = "Game")
public class Game {
    private int id;
    private String name;
    private int budget;
    private int profit;
    private int number;
    private int genre;
    private int platform;
    private int status;
    private Date start;
    private Date end;

    // 构造方法、getter/setter 已省略

    public void update(Game new_game) {
        this.name = new_game.name;
        this.budget = new_game.budget;
        this.profit = new_game.profit;
        this.number = new_game.number;
        this.genre = new_game.genre;
        this.platform = new_game.platform;
        this.status = new_game.status;
        this.start = new_game.start;
        this.end = new_game.end;
    }
}

页面托管Bean Games.java

package org.xtremebiker.jsfspring.view;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.primefaces.event.RowEditEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;
import org.xtremebiker.jsfspring.model.Game;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

@Component(value = "games")
@SessionScope
public class Games {
    private List<Game> All_games = new ArrayList<Game>();

    public Games() {
        System.out.println("Created!");
        All_games.add(new Game(1, "One", 1000, 500, 8, 1, 2, 4, new Date(), new Date()));
    }

    public void onRowEdit(RowEditEvent event) {
        Game new_game = (Game) event.getObject();
        All_games.get(new_game.getId()).update(new_game);
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "INFO", "X/Y edit successful!");
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

    public List<Game> getAll_games() {
        return All_games;
    }

    public void setAll_games(List<Game> all_games) {
        All_games = all_games;
    }
}

视图页面 hello.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core" 
      xmlns:p="http://primefaces.org/ui"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"/>
    <style>
        .container {
            display: flex;
            justify-content: space-between;
            width: 100%;
        }
    </style>
</h:head>
<h:body>
    <div class="container">
        <nav id="sidebarMenu" style="padding-right: 5px; flex: 1 1 auto;">
            <p:menu toggleable="true">
                <p:submenu label="Игры">
                    <p:menuitem value="Список игр" url="http://www.youtube.com" />
                    <p:menuitem value="Добавить игру" url="http://www.youtube.com" />
                </p:submenu>
                <p:menuitem value="Список сотрудников" url="http://www.youtube.com" />
                <p:menuitem value="Список кандидатов" url="http://www.youtube.com" />
            </p:menu>
        </nav>
        <div style="flex: 1 1 auto;">
            <p:dataTable value="#{games.all_games}" editable="true" var="game">
                <p:ajax event="rowEdit" listener="#{games.onRowEdit}" />
                <p:column headerText="Название">
                    <p:cellEditor>
                        <f:facet name="output">
                            <h:outputText value="#{game.name}" />
                        </f:facet>
                        <f:facet name="input">
                            <h:inputText value="#{game.name}" />
                        </f:facet>
                    </p:cellEditor>
                </p:column>
                <p:column headerText="Бюджет">
                    <h:outputText value="#{game.budget}"></h:outputText>
                </p:column>
                <p:column headerText="Прибыль">
                    <h:outputText value="#{game.profit}"></h:outputText>
                </p:column>
                <p:column headerText="Оценка">
                    <h:outputText value="#{game.number}"></h:outputText>
                </p:column>
                <p:column headerText="Жанр">
                    <h:outputText value="#{game.genre}"></h:outputText>
                </p:column>
                <p:column headerText="Платформа">
                    <h:outputText value="#{game.platform}"></h:outputText>
                </p:column>
                <p:column headerText="Статус разработки">
                    <h:outputText value="#{game.status}"></h:outputText>
                </p:column>
                <p:column headerText="Начало разработки">
                    <h:outputText value="#{game.start}">
                        <f:convertDateTime pattern="dd.MM.yyyy " />
                    </h:outputText>
                </p:column>
                <p:column headerText="Дата релиза">
                    <h:outputText value="#{game.end}">
                        <f:convertDateTime pattern="dd.MM.yyyy " />
                    </h:outputText>
                </p:column>
                <p:column style="width:35px">
                    <p:rowEditor />
                </p:column>
            </p:dataTable>
        </div>
    </div>
</h:body>
</html>

事件未触发的核心原因与修复方案

  • 实体类错误注册为Spring单例Bean:Game.java标注了@Component注解,Spring默认Bean为单例作用域,会导致DataTable遍历时所有行绑定到同一个共享对象,编辑状态下的EL绑定、序列化逻辑异常,直接阻断RowEdit事件流程。修复方式:移除Game类上的@Component注解,将Game作为普通POJO在业务Bean中手动实例化即可。
  • 业务逻辑触发未捕获的索引越界异常:onRowEdit方法中通过All_games.get(new_game.getId())取列表元素,测试数据id为1,但List索引从0开始,仅存1条数据时会直接抛出IndexOutOfBoundsException,异常被JSF异常拦截器吞掉后,前端无任何执行反馈,表现为事件未被调用。修复方式:直接使用event.getObject()返回的编辑后对象同步列表数据,不需要手动按id匹配索引。
  • 可编辑列配置不完整:当前表格仅“Название”列配置了p:cellEditor的输出/输入facet,其余列无编辑能力。PrimeFaces可编辑表格的行编辑校验流程要求,所有参与编辑的列必须完成facet配置,否则点击确认按钮时会因校验不通过直接终止事件传播。修复方式:对需要编辑的列补充p:cellEditor及对应facet配置。

内容的提问来源于stack exchange,提问作者user376237

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.12 15:45:03