Validation without @IfInvalid
pbrewer_uk Jun 1, 2006 6:28 AMI understand that the @IfInvalid annotation is no longer the best way to perform validation and that the <s:validate> and <s:validateAll> tags can be used to validate data-entry entity fields on the front-end.
How would I go about validating the entire entity bean (not just the data-entry fields) on form submission?
For example: A user enters two numbers - they're multiplied together and saved in the db. The entered numbers are validated by @Max(5) but the calculated result has a validation of @Max(10). Consequently, entering a value of 4 and 4 stores a result of 16 and yields an exception in the log, but an apparent success on the front-end.
Is there a nice way to validate (or even just catch the exception) and then warn the user?
(As a side note: is the @Valid tag now obsolete too?)
Thanks in advance, Peter.
Exception in log
01-06 11:04:56 ERROR [PhaseListenerManager] Exception in PhaseListener INVOKE_APPLICATION(5) afterPhase java.lang.IllegalStateException: Could not commit transaction at org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener.commitOrRollback(SeamExtendedManagedPersistencePhaseListener.java:98) at org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener.afterPhase(SeamExtendedManagedPersistencePhaseListener.java:52) at org.apache.myfaces.lifecycle.PhaseListenerManager.informPhaseListenersAfter(PhaseListenerManager.java:89) at org.apache.myfaces.lifecycle.LifecycleImpl.invokeApplication(LifecycleImpl.java:345) at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:86) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:137) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:144) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.jboss.seam.servlet.SeamRedirectFilter.doFilter(SeamRedirectFilter.java:23) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.jboss.seam.servlet.SeamExceptionFilter.doFilter(SeamExceptionFilter.java:45) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:868) at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:663) at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) at java.lang.Thread.run(Thread.java:595) Caused by: org.jboss.tm.JBossRollbackException: Unable to commit, tx=TransactionImpl:XidImpl[FormatId=257, GlobalId=null:1149156241562/8, BranchQual=null:1149156241562, localId=0:8], status=STATUS_NO_TRANSACTION; - nested throwable: (org.hibernate.validator.InvalidStateException: validation failed for: uk.co.iblocks.data.NumberTest) at org.jboss.tm.TransactionImpl.commit(TransactionImpl.java:1286) at org.jboss.tm.TxManager.commit(TxManager.java:588) at org.jboss.ejb3.embedded.UserTransactionImpl.commit(UserTransactionImpl.java:90) at org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener.commitOrRollback(SeamExtendedManagedPersistencePhaseListener.java:87) ... 28 more Caused by: org.hibernate.validator.InvalidStateException: validation failed for: uk.co.iblocks.data.NumberTest at org.hibernate.validator.event.ValidateEventListener.validate(ValidateEventListener.java:104) at org.hibernate.validator.event.ValidateEventListener.onPreInsert(ValidateEventListener.java:127) at org.hibernate.action.EntityInsertAction.preInsert(EntityInsertAction.java:139) at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:44) at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139) at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297) at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27) at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:988) at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:337) at org.hibernate.transaction.CacheSynchronization.beforeCompletion(CacheSynchronization.java:59) at org.jboss.tm.TransactionImpl.doBeforeCompletion(TransactionImpl.java:3074) at org.jboss.tm.TransactionImpl.beforePrepare(TransactionImpl.java:2632) at org.jboss.tm.TransactionImpl.commit(TransactionImpl.java:1194)
numberTest.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:t="http://myfaces.apache.org/tomahawk" xmlns:h="http://java.sun.com/jsf/html" xmlns:s="http://jboss.com/products/seam/taglib"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <f:view> <h:form> <s:validateAll> First: <h:inputText id="no1" value="#{numberTest.first}" /><br /> Second: <h:inputText id="no2" value="#{numberTest.second}" /><br/> <h:commandButton id="calculate" action="#{numbersAction.save}" value="save" /><br /> <h:messages showDetail="true" /> </s:validateAll> </h:form> </f:view> </body> </html>
Session bean - NumbersAction.java
@Stateful @Name ( "numbersAction" ) @Scope ( ScopeType.CONVERSATION ) public class NumbersAction implements Numbers { private static final Logger LOG = Logger.getLogger( NumbersAction.class ); @PersistenceContext(type=EXTENDED) private EntityManager em; @Valid @Out(scope = ScopeType.CONVERSATION) @In(required=false) private NumberTest numberTest ; @Begin @Factory ( "numberTest" ) public void initNumberTest () { numberTest = new NumberTest(); } @End ( ifOutcome = "saved" ) public String save() { try { numberTest.setResult(numberTest.getFirst() * numberTest.getSecond()) ; em.persist( numberTest ); FacesMessages.instance().add("Data saved fine.") ; return "saved"; } catch (Exception ex) { FacesMessages.instance().add("Cannot save data.") ; return null ; } } @Remove @Destroy public void destroy() { } }
EntityBean - NumbersTest.java
@Entity @Table(name="NUMBER_TEST") @SequenceGenerator(name="numberTest", sequenceName="NUMBER_TEST_SEQ") public class NumberTest implements Serializable { private static final Logger LOG = Logger.getLogger(NumberTest.class) ; private Integer id ; private Integer first; private Integer second; private Integer result; public NumberTest() { } @Id @GeneratedValue(strategy=GenerationType.AUTO, generator="numberTest") @Column(name="NUMBER_ID") @NotNull public Integer getId() { return this.id ; } protected void setId(Integer id) { this.id = id ; } @Column(name="FIRST") @Max(value=5, message="First value must be less than 5.") public Integer getFirst () { return first; } public void setFirst ( Integer first ) { this.first = first; } @Column(name="SECOND") @Max(value=5, message="Second value must be less than 5.") public Integer getSecond () { return second; } public void setSecond ( Integer second ) { this.second = second; } @Column(name="RESULT") @Max(value=10, message="Result value must be less than 10.") public Integer getResult () { return result; } public void setResult ( Integer result ) { this.result = result; } }