3 Replies Latest reply on Dec 30, 2006 12:30 AM by norman.richards

    Object level validation

    awhitford

      I am creating JPA/EJB3 entity objects, and am using Hibernate Validation annotations for some basic constraints. And I definitely like how this validation framework is leveraged by Seam -- DRY!

      But how can I best provide semantic object level validation? For example, imagine that I have a Bond class with several properties for creating the coupons:

      @Entity
      public class Bond
      {
       /** Primary key id */
       @Id
       @GeneratedValue
       private int id;
      
       @NotNull
       @Temporal(TemporalType.DATE)
       private Date issueDate;
      
       @Temporal(TemporalType.DATE)
       private Date datedDate;
      
       @Temporal(TemporalType.DATE)
       private Date firstCouponDate;
      
       @Temporal(TemporalType.DATE)
       private Date penultimateCouponDate;
      
       @Temporal(TemporalType.DATE)
       private Date maturityDate;
      
       @Column(columnDefinition = "tinyint")
       @Enumerated(EnumType.STRING)
       private CouponFrequency couponFrequency;
      
       @Column(length = 13)
       @Enumerated(EnumType.STRING)
       private AccrualMethod accrualMethod;
      }
      


      I need the capability to validate the bond object to ensure the following:

      • issueDate < maturityDate
      • issueDate < firstCouponDate
      • issueDate < penultimateCouponDate
      • datedDate < firstCouponDate
      • datedDate < maturityDate
      • datedDate < penultimateCouponDate
      • firstCouponDate < penultimateCouponDate
      • firstCouponDate < maturityDate
      • penultimateCouponDate < maturityDate

        And this is just the tip of the iceberg. (Note that there are seemingly redundant checks listed because most of the columns are nullable.)

        What is the best practice for enforcing object level validation?

        I'm looking for a strategy whereby a validation routine could be executed to create a list of invalid values, which them Seam could report to the user -- just like other Hibernate Validation annotation restrictions like @Min, @Max, @Range, etc.

        Thanks!


        • 1. Re: Object level validation

          You can easily add extra conditions:

           @AssertTrue(message="second number must be larger than the first")
           public boolean checkNumbers() {
           System.out.println("number1=" + number1 + " number2=" + number2);
           return number2 > number1;
           }
          


          Hibernate will check the condition at persist() time. Unfortunately, it doesn't handle it a way that is very friendly to your application. (from the perspective of being transparent) You'll have to add a bit of code to the application for that. Here's an overridden persist on EntityHome:

           @Transactional
           @Override
           public String persist()
           {
           ClassValidator validator = Validators.instance().getValidator(YourEntity.class, "yourEntity");
           InvalidValue[] ivs = validator.getInvalidValues(getInstance());
           if (ivs.length>0) {
           facesMessages.add(ivs);
           return null;
           }
          
           return super.persist();
           }
          




          • 2. Re: Object level validation
            awhitford

            I can see someone has a similar issue:
            http://opensource.atlassian.com/projects/hibernate/browse/ANN-513

            I though about using AssertTrue, but it felt forced, especially when I would need to use several if I wanted to actually link errors to certain fields...

            Is there a way that I can create a ClassValidator for the Bond class, register it with Hibernate so that it always validates Bond objects before persisting? (I found little documentation on doing something like this, but did see a post that hinted at this notion.)

            • 3. Re: Object level validation


              You can override the Validators component to add your own createValidator() strategy. But I don't think that's really what you are after. If you think about what I posted, the problem is not in creating a class validator or having Hibernate call it. Hibernate does exactly that by default in JBoss. Add a validator to your entity and it WILL get called. The problem is in having the validation exception handled gracefully. Without the code I showed above, persist just throws a validation exception and there isn't a whole lot of you can do about it.