p:selectOneMenu
非常に単純なエンティティを表す2 つの JSF ページがあります。
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;
...
}
MonitoringReason エンティティ:
@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;
}
...
}
MonitoringReasonConverter.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;
}
...
}
ご覧のとおり、ほとんどコピーに近い 2 つの単純な 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>
OK、ここも同じ、基本的に同じコピーされたコードです。唯一の違いは、リスト タイプがUI ラベルとしてitemLabel="#{ltp.description}"
使用され、監視理由itemLabel="#{mtr.name}"
が UI ラベルとして使用されることです。
今起こっていることは、私が期待していたことではありません。
リスト・タイプ・コンバーターとモニター理由コンバーターの両方が、JSF から ID として名前を取得します。これは、リスト型コンバーターでは名前の付いたクエリを使用してそれぞれのエンティティを検索するため、問題にはなりません。これは、選択の変更を実行するときに発生します。
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 でない場合)