Bean を永続化する前に検証したいフィールドがいくつかあります。
私はinputText、selectOne、SelectManyListBox、...を持っています
私の問題は、検証が行われているものとそうでないものがあることです...
例のために。私は持っている
<tr>
<td><h:outputLabel value="#{msg.subscriptionFormFieldPhone}:" /></td>
<td><h:inputText
id="phone"
value="#{detailModel.afterObject.phone}"
disabled="#{detailModel.mode == detailModel.viewMode or (detailModel.mode == detailModel.editMode and !loggedUser.hasPermission('CMN backoffice permission id'))}" />
<h:messages for="phone" style="color:red; font-size:12px;" />
</td>
</tr>
...
<tr>
<td><h:outputLabel value="#{msg.subscriptionFormFieldFund}:" /></td>
<td>
<table>
<tr>
<td><h:inputText
id="fund"
value="#{detailModel.afterObject.fund}" disabled="true">
<f:converter converterId="ch.ethz.id.cmn.FundConverter" />
</h:inputText>
<h:messages for="fund" style="color:red; font-size:12px;" />
</td>
<td valign="top">
<!-- add button -->
<h:commandButton styleClass="waiButton"
value="#{lakshmiMsg.commonButtonAddExisting}"
action="#{detailController.gotoFundSelection()}"
rendered="#{(detailModel.mode == detailModel.createMode and detailModel.afterObject.fund == null) or ((detailModel.mode != detailModel.viewMode and detailModel.afterObject.fund == null))}"
disabled="#{detailModel.mode == detailModel.multiEditMode}" />
<!-- delete button -->
<h:commandButton styleClass="waiButton"
value="#{lakshmiMsg.commonButtonDelete}"
action="#{detailController.deleteFund()}"
rendered="#{((detailModel.mode == detailModel.editMode) and loggedUser.hasPermission('CMN backoffice permission id')) or (detailModel.mode == detailModel.createMode and detailModel.afterObject.fund != null)}"
disabled="#{detailModel.mode == detailModel.multiEditMode}" />
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><h:outputLabel value="#{msg.subscriptionFormFieldOwner}:" /></td>
<td>
<table>
<tr>
<td><h:inputText id="owner"
value="#{detailModel.afterObject.owner}"
disabled="true">
<f:converter converterId="ch.ethz.id.cmn.UserConverter"></f:converter>
</h:inputText>
<h:messages for="owner" style="color:red; font-size:12px;" />
</td>
<td valign="top">
<!-- add button -->
<h:commandButton styleClass="waiButton"
value="#{lakshmiMsg.commonButtonAddExisting}"
action="#{detailController.gotoUserSelection()}"
rendered="#{(detailModel.mode == detailModel.createMode and detailModel.afterObject.owner == null) or ((detailModel.mode != detailModel.viewMode and detailModel.afterObject.owner == null))}"
immediate="true"
disabled="#{detailModel.mode == detailModel.multiEditMode}" />
<!-- delete button -->
<h:commandButton styleClass="waiButton"
value="#{lakshmiMsg.commonButtonDelete}"
action="#{detailController.deleteOwner()}"
rendered="#{((detailModel.mode == detailModel.editMode) and loggedUser.hasPermission('CMN backoffice permission id')) or (detailModel.mode == detailModel.createMode and detailModel.afterObject.owner != null)}"
immediate="true"
disabled="#{detailModel.mode == detailModel.multiEditMode}" />
</td>
</tr>
</table>
</td>
</tr>
...
<tr>
<td valign="top">
<h:outputLabel value="#{msg.subscriptionFormFieldDataOptions}:" /></td>
<td>
<table>
<tr>
<td>
<h:selectManyListbox
id="dataOptions"
value="#{detailModel.selectedDataOptions}"
size="5"
disabled="#{detailModel.mode == detailModel.viewMode or (detailModel.mode == detailModel.editMode and !loggedUser.hasPermission('CMN backoffice permission id'))}">
<f:selectItems value="#{detailModel.afterObject.dataOptions}"
var="sdo" itemLabel="#{sdo.dataOptionName}"
itemValue="#{sdo}" />
</h:selectManyListbox>
<h:messages for="dataOptions" style="color:red; font-size:12px;" />
</td>
...
</tr>
</table>
</td>
</tr>
...
私の Bean (detailModel.afterObject にバインドされている) では、フィールドに @NotNull と @Size(min=1) のタグを付けました。
@NotNull( message = "{validation.emptyPhoneNumber}" )
@Pattern( regexp = "^(((41)(\\s?)(-?))|(0))(7)(\\d{1})(\\s?)(-?)(\\d{3})(\\s?)(-?)(\\d{2})(\\s?)(-?)(\\d{2})", message = "{validation.notValidPhoneNumber}" )
private String phone;
@NotNull( message = "{validation.emptyFund}" )
@Size(min=1, message = "{validation.emptyFund}" )
private String fund;
@NotNull( message = "{validation.emptyOwner}" )
@Size(min=1, message = "{validation.emptyOwner}" )
private String owner;
@ManyToMany( fetch = FetchType.LAZY, cascade = {} )
@JoinTable( name = "CMN_MAP_SUBSCRIPTION_TO_DATAOPTION" )
@NotNull( message = "{validation.emptyDataOption}" )
@Size(min=1, message = "{validation.emptyDataOption}" )
private Set< DataOption > dataOptions;
最初の inputText (電話) が検証され、空のままにするか、パターンが一致しない場合、エラー メッセージが表示されますが、他のものについては (明らかに) 検証がありません。空のままにすると、メッセージは表示されません...
唯一の違いは、資金と所有者の inputFileds にはコンバーターがあり、最後は set にマップされる selectManyListBox であり、他のすべては String にマップされることです。
永続性が失敗すると、ログにエラー メッセージが表示され、そこに ConstraintViolationException が表示されますが、検証時にスタック トレースの代わりにページにメッセージを表示することを要求しない理由がわかりません。ログ...
[exec] [#|2013-09-17T11:26:49.901+0200|WARNING|glassfish3.1.2|javax.enterprise.resource.jta.com.sun.enterprise.transaction|_ThreadID=420;_ThreadName=Thread-2;|DTX5014: Caught exception in beforeCompletion() callback:
[exec] javax.persistence.PersistenceException: error during managed flush
[exec] at org.hibernate.ejb.AbstractEntityManagerImpl$CallbackExceptionMapperImpl.mapManagedFlushFailure(AbstractEntityManagerImpl.java:1515)
[exec] at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion(SynchronizationCallbackCoordinatorImpl.java:109)
[exec] at org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:53)
[exec] at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:435)
[exec] at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:855)
[exec] at com.sun.enterprise.transaction.UserTransactionImpl.commit(UserTransactionImpl.java:208)
[exec] at ch.ethz.id.wai.lakshmi.engine.common.TransactionHelper.commitTransaction(TransactionHelper.java:74)
[exec] at ch.ethz.id.wai.lakshmi.stdcmp.boo.processing.BusinessObjectOrderProcessing.processOrder(BusinessObjectOrderProcessing.java:121)
[exec] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[exec] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
[exec] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[exec] at java.lang.reflect.Method.invoke(Method.java:601)
[exec] at ch.ethz.id.wai.lakshmi.engine.ejb.LakshmiServerBean.invokeWorkflowStep(LakshmiServerBean.java:850)
[exec] at ch.ethz.id.wai.lakshmi.engine.ejb.LakshmiServerBean.executeNextWorkflowStep(LakshmiServerBean.java:796)
[exec] at ch.ethz.id.wai.lakshmi.engine.ejb.LakshmiServerBean.executeNextWorkflowStep(LakshmiServerBean.java:707)
[exec] at ch.ethz.id.wai.lakshmi.engine.ejb.LakshmiServerBean.continueWorkflow(LakshmiServerBean.java:627)
[exec] at ch.ethz.id.wai.lakshmi.engine.ejb.LakshmiServerBean.processLakshmiComponentResponse(LakshmiServerBean.java:576)
[exec] at ch.ethz.id.wai.lakshmi.engine.ejb.LakshmiServerBean.onMessage(LakshmiServerBean.java:397)
[exec] at sun.reflect.GeneratedMethodAccessor967.invoke(Unknown Source)
[exec] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[exec] at java.lang.reflect.Method.invoke(Method.java:601)
[exec] at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1052)
[exec] at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1124)
[exec] at com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod(BaseContainer.java:4180)
[exec] at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:5368)
[exec] at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:5348)
[exec] at com.sun.ejb.containers.MessageBeanContainer.deliverMessage(MessageBeanContainer.java:1099)
[exec] at com.sun.ejb.containers.MessageBeanListenerImpl.deliverMessage(MessageBeanListenerImpl.java:81)
[exec] at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:171)
[exec] at com.sun.proxy.$Proxy409.onMessage(Unknown Source)
[exec] at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:260)
[exec] at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:114)
[exec] at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:497)
[exec] at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:540)
[exec] Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [ch.ethz.id.wai.cmn.bo.Subscription] during persist time for groups [javax.validation.groups.Default, ]
[exec] List of constraint violations:[
[exec] ConstraintViolationImpl{interpolatedMessage='Please specify a fund', propertyPath=fund, rootBeanClass=class ch.ethz.id.wai.cmn.bo.Subscription, messageTemplate='{validation.emptyFund}'}
[exec] ConstraintViolationImpl{interpolatedMessage='Please specify a fund', propertyPath=fund, rootBeanClass=class ch.ethz.id.wai.cmn.bo.Subscription, messageTemplate='{validation.emptyFund}'}
[exec] ConstraintViolationImpl{interpolatedMessage='Please specify a owner', propertyPath=owner, rootBeanClass=class ch.ethz.id.wai.cmn.bo.Subscription, messageTemplate='{validation.emptyOwner}'}
[exec] ConstraintViolationImpl{interpolatedMessage='{validation.emptyDataOption}', propertyPath=dataOptions, rootBeanClass=class ch.ethz.id.wai.cmn.bo.Subscription, messageTemplate='{validation.emptyDataOption}'}
[exec] ]
[exec] at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:159)
[exec] at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:94)
[exec] at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:185)
[exec] at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:81)
[exec] at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
[exec] at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
[exec] at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:275)
[exec] at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
[exec] at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
[exec] at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
[exec] at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
[exec] at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion(SynchronizationCallbackCoordinatorImpl.java:104)
[exec] ... 32 more
[exec] |#]
ヘルプ、ヒント、ヒントは大歓迎です...
アップデート:
電話のinputTextと資金のinputTextにValidatorを作成して追加しました。
<f:validator validatorId="ch.ethz.id.cmn.validator.SubscriptionValidator" />
そして、validate メソッドにブレークポイントを設定しました。
さて、電話の場合はメソッドが呼び出されますが、ファンドの場合はそうではありません!
私はとても混乱しています。私のhtmlコードの何かが検証を妨げています...
更新 2:
を削除するdisabled="true"
と、機能します。それはとても欲しいですか、それともバグですか?フィールドが無効になっている場合でも機能させる方法はありますか?
更新 3:
今、私は本当に混乱しています...無効をすべて読み取り専用に置き換えた後(selectManyListBoxを除く)、永続化した後にページ内の値を編集しようとするまで、うまく機能するようになりました。資金と所有者のフィールドを変更/削除しませんでしたが、検証エラー メッセージが表示されます... Bean のゲッター メソッドにブレークポイントを配置すると、値が設定されます。つまり、null ではありません。では、なぜ @NotNull はまだ文句を言うのでしょうか? これは、Bean の注釈を削除し、必要なメッセージと必要なメッセージを html に追加した場合にも発生します。