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;
}
}