8 Replies Latest reply on Jul 3, 2007 12:37 PM by pmuir

    validation / exception handling patterns with EntityHome

    mbertelsen

      We've got some simple CRUD operations that we're doing just by declaring an entity-query and entity-home in components.xml, and building a list page and a form page that bind to them. It works great, but one of our classes has a unique constraint on one of the fields. Right now, if you enter data on the edit form and submit (this is bound to {#entityHome.persist}, the persist method throws a SQLException which is handled by the normal seam exception handling - I get a generic error page for the SQLException.

      I'd rather get a validation failure for the field *before* the persist fails, so I can show it on the edit form next to the field that needs to be unique, rather than handling it with an error page. It'd be less nice but acceptable to show the error as a general Faces Message. I guess I'd need to write a class that extends EntityHome and implements this behavior, but I thought I'd check if there was a better way.

      We have a similar problem with delete when a foreign key is violated - I'd like to know what the recommendation is for handling the exception - Should I build a custom error page for SQLException, and try to parse the exception / message there to determine the key that failed? Or is extending EntityHome and throwing a custom exception a better way? Is there another better way?

        • 1. Re: validation / exception handling patterns with EntityHome
          pmuir

          You could write a validator for this

          @Name("uniqueValidator")
          @Validator
          @Transactional
          public class UniqueValidator {
          
           @In EntityManager entityManager;
          
           public void validate(FacesContext facesContext, UIComponent component, Object value)
           throws ValidatorException {
           Foo foo = (Foo) value;
           Foo foo1 = entityManager.createQuery("select f from Foo f where f.uniqueProperty = :uniqueProperty").setParameter("uniqueProperty, foo.getUniqueProperty()).getSingleResult();
           if (foo1 != null && !foo1.getId().equals(foo.getId)) {
           throw new ValidatorException("Unique Constraint Violation");
           }
           }
          }


          In fact, we could have a generic version of this in Seam as part of s:validate.

          • 2. Re: validation / exception handling patterns with EntityHome
            matt.drees

            By the way, I think it's pretty slick that you can use Seam components as validators like that.

            • 3. Re: validation / exception handling patterns with EntityHome
              mbertelsen

              From reading the 1.2.1.GA docs, it appears that using JSF validators is deprecated in favor of using Hibernate validators.

              Can a Hibernate validator also be a seam component similar to the above?

              • 4. Re: validation / exception handling patterns with EntityHome
                pmuir

                No. *All* hibernate validators are tied into your views using s:validate

                • 5. Re: validation / exception handling patterns with EntityHome
                  mbertelsen

                  Is it possible to implement a hibernate validator with this functionality (run a query to determine uniqueness)? It's not clear from looking at the hibernate validator docs how i would inject a persistence context to use.

                  BTW, I got the above strategy to work, but had to change a few things to get it to validate the unique property field itself. It wasn't clear how to set up the view to validate the *entire* object.

                  @Name("clusterConfigUniqueValidator")
                  @Validator
                  @Transactional
                  public class ClusterConfigUniqueNameValidator implements javax.faces.validator.Validator, Serializable{
                   @In
                   EntityManager entityManager;
                  
                   @In EntityHome<ClusterConfig> clusterConfigHome;
                  
                   public void validate(FacesContext facesContext, UIComponent component,
                   Object value) throws ValidatorException {
                   String name = (String) value;
                   ClusterConfig cc = clusterConfigHome.getInstance();
                   try {
                   ClusterConfig cc1 = (ClusterConfig) entityManager.createQuery("select cc from ClusterConfig cc where cc.name = :name")
                   .setParameter("name", name).getSingleResult();
                   if (cc1 != null && !cc1.getId().equals(cc.getId())) {
                   throw new ValidatorException(new FacesMessage("Name must be unique"));
                   }
                   } catch (NoResultException nre) {
                   // that's fine - this name is unique.
                   }
                   }
                  
                  }
                  
                  And then on the edit page:
                  
                   <s:decorate id="nameDecoration" template="layout/textField.xhtml">
                   <ui:define name="label">Name:</ui:define>
                   <h:inputText fieldIdBase="name" id="name" value="#{clusterConfig.name}" required="true">
                   <f:validator validatorId="clusterConfigUniqueValidator"/>
                   <s:validate/>
                   </h:inputText>
                   </s:decorate>
                  
                  


                  Also, what about dealing with SQLException when you delete something and violate a foreign key constraint? Does anybody have a recommendation for how to avoid this gracefully? Should I not display the delete link/button on my list page if it's referenced (which would require an extra query per entity listed)? Write a custom exception handler in pages.xml to catch SQLException and a custom error page/handler that somehow parses the message?

                  • 6. Re: validation / exception handling patterns with EntityHome
                    pmuir

                    That looks correct to me (I was assuming you would have a converted object arriving at the validator, not just a string representing it). You can't really validate the whole object using this scheme as you need a different validator for each property. If we do a generic one, then a s:validateAll will apply it.

                    • 7. Re: validation / exception handling patterns with EntityHome
                      dunks80

                      Pete,
                      What is your suggestion for writing a validator that validates the property against other properties values within the object (ie is this property > than another property in the object)? I thought maybe i could use hibernate validator's bean level validation but based on your last post it doesn't look like this option would work. Should such object level validation even be performed at the validation phase of the jsf lifecycle or is it better suited for another phase down the lifecycle?

                      • 8. Re: validation / exception handling patterns with EntityHome
                        pmuir

                        Currently its best to do this in the application phase. This will be addressed in JSF 2. I've got some ideas about how to add this in before that, but I need time to work on them.