自定义标签封装rich:column后JSF无法实现筛选与排序问题
这个问题我太熟悉了——你遇到的核心问题是Facelets模板没有正确传递EL表达式给RichFaces的sortBy和filterBy属性,导致Hibernate把#{item}当成了实体类的属性名,而不是解析成你传入的#{entity.attribute}。
问题根源拆解
当你用<ui:composition>做自定义模板时,模板里的#{item}会被直接解析成当前行entity.attribute的具体值(比如某个字符串或数字),而不是保留#{entity.attribute}这个表达式本身。但RichFaces的rich:column在处理排序和筛选时,需要的是一个动态表达式,这样它才能在操作时针对整个数据集解析这个表达式,而不是用单个行的静态值。所以Hibernate收到的查询条件里找的是名为#{item}的属性,自然会抛出找不到属性的异常。
最佳解决方案:改用JSF复合组件
复合组件(Composite Component)是JSF专门用来封装可复用UI片段的机制,它能正确保留EL表达式的引用,完美解决这个问题。
步骤1:创建复合组件定义文件
在/WEB-INF/resources/mytags/目录下创建filterableSortableColumn.xhtml(mytags是你的自定义标签命名空间对应的资源目录):
<ui:component xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich" xmlns:cc="http://java.sun.com/jsf/composite"> <!-- 定义组件的属性接口 --> <cc:interface> <cc:attribute name="label" required="true" type="java.lang.String" shortDescription="列头显示的文本"/> <cc:attribute name="item" required="true" type="java.lang.Object" shortDescription="绑定的实体属性表达式,比如#{entity.attribute}"/> </cc:interface> <!-- 组件的具体实现 --> <cc:implementation> <rich:column label="#{cc.attrs.label}" sortable="true" selfSorted="true" sortBy="#{cc.attrs.item}" filterBy="#{cc.attrs.item}" filterEvent="onkeyup"> <f:facet name="header"> <h:outputText value="#{cc.attrs.label}"/> </f:facet> <h:outputText value="#{cc.attrs.item}"/> </rich:column> </cc:implementation> </ui:component>
步骤2:使用方式保持不变
你原来的调用代码完全不用改,还是:
<mytags:filterableSortableColumn label="my column name" item="#{entity.attribute}"/>
为什么这个方案有效?
复合组件的cc.attrs.item会保留你传入的#{entity.attribute}这个ValueExpression,而不是解析成具体的值。当RichFaces处理排序/筛选时,它会直接使用这个表达式去查询Hibernate实体的属性,自然就能找到对应的attribute字段了。
备选方案(不推荐):传递表达式字符串
如果你暂时不想改用复合组件,也可以把表达式作为字符串传递,然后在模板里用动态EL解析:
调用方式:
<mytags:filterableSortableColumn label="my column name" itemExpr="attribute" entity="#{entity}"/>
模板代码:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich"> <rich:column label="#{label}" sortable="true" selfSorted="true" sortBy="#{entity[itemExpr]}" filterBy="#{entity[itemExpr]}" filterEvent="onkeyup"> <f:facet name="header"> <h:outputText value="#{label}"/> </f:facet> <h:outputText value="#{entity[itemExpr]}"/> </rich:column> </ui:composition>
不过这种方式需要额外传递实体对象,灵活性差,不如复合组件优雅,只适合临时应急。
内容的提问来源于stack exchange,提问作者Sam

