0

可能重复:
为什么 selectOneMenu 将 ItemLabel 发送给转换器?

我有一个 JSF 页面,上面有两个p:selectOneMenu,它们都代表非常简单的实体。

ListType 实体:

@Entity
@Table( name = "LISTTYPES" )
@NamedQueries
(
    {
        @NamedQuery( name  = "...",
                     query = "SELECT lty " +
                             "FROM ListType lty " +
                             "WHERE lty.name = :name "),
    }
)
public class ListType implements ClientDependent, Serializable
{
    @Id
    @Column( name = "LISTTYPE_ID" )
    private Long               id;

    @Basic( optional = false )
    @Column( name = "LIST_NO" )
    private Long               nbr;

    @Basic( optional = false )
    @Column( name = "LIST_NAME" )
    private String             name;

    @Column( name = "LIST_DESC" )
    private String             description;

    ...
}

监控原因实体:

@Entity
@Table( name = "MONITORINGREASONS" )
@NamedQueries
(
    {
        @NamedQuery( name  = "...",
                     query = "SELECT mtr " +
                             "FROM MonitoringReason mtr " +
                             "WHERE mtr.code = :code"),
    }
)
public class MonitoringReason implements Serializable
{
    @Id
    @Column( name = "MONITORINGREASON_ID" )
    private Long               id;

    @Basic( optional = false )
    @Column( name = "MONITORINGREASON_NO" )
    private String             code;

    @Basic( optional = false )
    @Column( name = "MONITORINGREASON_NAME" )
    private String             name;

    @Basic( optional = false )
    @Column( name = "MONITORINGREASON_DESC" )
    private String             description;

    ...
}

简单实体。请注意,我省略了两个实体都正确实现的 equals + hashCode 方法。

以下是一些示例数据:

列表类型数据:

LISTTYPE_ID  LIST_NO  LIST_NAME           LIST_DESC
      1         3       'WL'        'Watch List'
      0         4       'RL'        'Restricted List'
      2         5       'Emb'       'Embargo'
      7         7       'NRL'       'Not Recommended List'
   5009        14       'GWL'       'Global Watch List'
   5010        15       'GRL'       'Global Restricted List'
   5011        16       'PIL'       'Permanent Insider List'

监控原因数据:

MONITORINGREASON_ID  MONITORINGREASON_NO  MONITORINGREASON_NAME  MONITORINGREASON_DESC
        3                   'Ah'           'Ad-Hoc'              'Ad-Hoc'
        6                   'Al'           'Autom. Liste'        'Automatische Liste'
        4                   'Be'           'Beobachtung'         'Beobachtung'
        7                   'CLC'          'Limit Changes'       'Limit Changes'
        1                   'Fus'          'Fusion'              'Fusion'
        5                   'Li'           'Liste'               'Liste'
        2                   'Res'          'Research'            'Research Unternehmen'

对上述实体的命名查询用于由转换器查找实体:

ListTypeConverter.java:

@Named
@RequestScoped
public class ListTypeConverter implements Converter
{
    @Inject
    @SeamLogger
    private Logger log;

    @Inject
    private SessionHelper sessionHelper;

    @Inject
    private ListTypeService listTypeService;

    @Override
    public Object getAsObject( FacesContext ctx, UIComponent comp, String identifier )
    {
        this.log.info( getClass().getSimpleName() + ".getAsObject: " + identifier );

        if ( identifier == null )
        {
            return null;
        }

        ListType listType = this.listTypeService.findByClientIdAndName( this.sessionHelper.getLoginUser().getClientId(), identifier );

        this.log.info( "Returning " + listType.getName() +  "!" );

        return listType;
    }

    ...
}

监控ReasonConverter.java:

@Named
@RequestScoped
public class MonitoringReasonConverter implements Converter
{
    @Inject
    @SeamLogger
    private Logger log;

    @Inject
    private SessionHelper sessionHelper;

    @Inject
    private MonitoringReasonService monitoringReasonService;

    @Override
    public Object getAsObject( FacesContext ctx, UIComponent comp, String identifier )
    {
        this.log.info( getClass().getSimpleName() + ".getAsObject: " + identifier );

        if ( identifier == null )
        {
            return null;
        }

        MonitoringReason monitoringReason = this.monitoringReasonService.findByClientIdAndCode( this.sessionHelper.getLoginUser().getClientId(), identifier );

        this.log.info( "Returning " + monitoringReason.getName() +  "!" );

        return monitoringReason;
    }

    ...
}

如您所见,两个简单的 CDI 转换器,几乎是副本。唯一不同的是列表类型是按名称查询的,监控原因是按代码查询的。(请参阅命名查询,我检查了服务是否调用了正确的查询)

它们在 JSF 页面中使用,例如:

<p:selectOneMenu id="list-type"
                 value="#{complianceCaseManager.selectedListType}"
                 converter="#{listTypeConverter}">
    <f:selectItems value="#{listTypeManager.selectableListTypes}"
                   var="ltp"
                   itemValue="#{ltp}"
                   itemLabel="#{ltp.description}" />
    <p:ajax process="@this" update="@form" />
</p:selectOneMenu>

<p:selectOneMenu id="monitoring-reason"
                 value="#{complianceCaseManager.selectedMonitoringReason}"
                 converter="#{monitoringReasonConverter}">
    <f:selectItems value="#{monitoringReasonManager.selectableMonitoringReasons}"
                   var="mtr"
                   itemValue="#{mtr}"
                   itemLabel="#{mtr.name}" />
    <p:ajax process="@this" update="@form" />
</p:selectOneMenu>

好的,这里也一样,复制的代码基本相同。唯一的区别是列表类型使用itemLabel="#{ltp.description}"和监控原因itemLabel="#{mtr.name}"作为 UI 标签。

现在发生的事情不是我所期望的:

列表类型监控原因转换器都从 JSF 获取名称作为标识符。这对于列表类型转换器来说不是问题,因为它使用带有名称的查询来查找相应的实体。执行选择更改时会发生这种情况:

2012-12-18 22:51:23,923 [http-thread-pool-8181(5)] INFO  de.company.project.ListTypeConverter     - ListTypeConverter.getAsObject: RL
2012-12-18 22:51:23,927 [http-thread-pool-8181(5)] INFO  de.company.project.ListTypeConverter     - Returning RL!

但是,当使用监控原因选择时,会记录以下内容:

2012-12-18 22:52:02,091 [http-thread-pool-8181(2)] INFO  de.company.project.MonitoringReasonConverter     - MonitoringReasonConverter.getAsObject: Fusion
2012-12-18 22:52:02,100 [http-thread-pool-8181(2)] ERROR de.company.project.MonitoringReasonService       - Monitoring reason not found for client ID = 1 with code Fusion
javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.throwNoResultException(EJBQueryImpl.java:1307)
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getSingleResult(EJBQueryImpl.java:778)
    at de.company.project.MonitoringReasonService.findByClientIdAndCode(MonitoringReasonService.java:92)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1052)
    at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1124)
    at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:5388)
    at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:619)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)
    at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:571)
    at org.jboss.weld.ejb.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:42)
    at sun.reflect.GeneratedMethodAccessor7832.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:861)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)
    at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:571)
    at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doAround(SystemInterceptorProxy.java:162)
    at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:144)
    at sun.reflect.GeneratedMethodAccessor7831.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:861)
    at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)
    at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:370)
    at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:5360)
    at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:5348)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:214)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:89)
    at $Proxy2626.findByClientIdAndCode(Unknown Source)
    at de.company.project.__EJB31_Generated__MonitoringReasonService__Intf____Bean__.findByClientIdAndCode(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:267)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:263)
    at org.jboss.weld.bean.proxy.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:110)
    at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:105)
    at de.company.project.MonitoringReasonService$Proxy$_$$_Weld$Proxy$.findByClientIdAndCode(MonitoringReasonService$Proxy$_$$_Weld$Proxy$.java)
    at de.company.project.MonitoringReasonConverter.getAsObject(MonitoringReasonConverter.java:63)
    at de.company.project.MonitoringReasonConverter$Proxy$_$$_WeldClientProxy.getAsObject(MonitoringReasonConverter$Proxy$_$$_WeldClientProxy.java)
    at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getConvertedValue(HtmlBasicInputRenderer.java:171)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.convertSelectOneValue(MenuRenderer.java:201)
    at com.sun.faces.renderkit.html_basic.MenuRenderer.getConvertedValue(MenuRenderer.java:318)
    at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.getConvertedValue(SelectOneMenuRenderer.java:55)
    at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030)
    .
    .
    .

  1. 为什么在转换过程中要同时使用 name 属性
  2. 是什么决定了 name 属性应该用于 getAsObject 方法字符串值,而不是 - 正如我一直假设的那样 - 传递到的属性itemLabel="..."?怎么控制??
  3. 这些信息在 JSF 规范中隐藏在哪里?(如果不是 PrimeFaces 在这里做一些覆盖)
4

2 回答 2

1

我认为这将是转换器的问题。我发现了一个类似的问题,检查一下它可能会有所帮助。为什么 selectOneMenu 将 ItemLabel 发送给转换器?

于 2012-12-19T08:13:33.200 回答
0

The answer is ultimately that my converter's getAsString() method defines the strings that getAsObject receives. I had always assumed that the value is determined by JSF, e.g. the itemLabel="...", which isn't the case.

I kind of makes sense this way when thinking about it, but I'm rather shocked that I have missed this information for my 1.5+ years of reading and programming JSF...

于 2012-12-19T09:51:16.023 回答