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