Hi,
I'll try to discribe this situation as good as possible. It's a proof of concept I'm making for my own use based on a client's project which is implemented in old Struts, old EJB2 and a quite bad design.
I'm trying, for educational use and using a more complex project, to make a PoC for my client using new standardized technologies as JPA/Hibernate, JSF, and hopefully a more straightforward future-proof changeable design (which the current one isn't).
I use a custom constraint for Hibernate Validator to validate the format of an Belgian accountnumber.
The annotation and validator class are the following:
Code:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ValidatorClass(RekeningNummerValidator.class)
public @interface RekeningNummer {
   public static final String DEFAULT_REGEX = "\\d{3}-\\d{7}-\\d{2}";
   // Maximum rekeningnummer
   // 000-0000000-00
   String regex() default DEFAULT_REGEX;
   String message() default "{validator.rekeningnummer.invalid}";
}
Code:
public class RekeningNummerValidator implements Validator<RekeningNummer> {
   private static final Integer EERSTE_BLOK = 0;
   private static final Integer TWEEDE_BLOK = 1;
   private static final Integer CONTROLECIJFERS = 2;
   private static final Double MODULO = new Double(97);
   private String regex;
   /**
    * @return the regex
    */
   public String getRegex() {
      return regex;
   }
   /**
    * @param regex
    *            the regex to set
    */
   public void setRegex(String regex) {
      this.regex = regex;
   }
   /**
    * @param parameters
    */
   public void initialize(RekeningNummer parameters) {
      this.regex = parameters.regex();
   }
   /**
    * @param value
    * @return
    */
   public boolean isValid(Object value) {
      if (value == null) {
         return true;
      } else if (value instanceof String) {
         String valueString = (String) value;
         if (StringUtils.isBlank(valueString)) {
            return true;
         } else {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(valueString);
            if (matcher.matches()) {
               String[] rekeningNummers = valueString.split("-");
               Double rest = Double.valueOf(rekeningNummers[CONTROLECIJFERS]);
               Double getal = Double.valueOf(rekeningNummers[EERSTE_BLOK] + rekeningNummers[TWEEDE_BLOK]);
               if (rest.equals(getal % MODULO)) {
                  return true;
               }
            }
         }
      }
      return false;
   }
}
I have an Entity Entiteit which is the superclass of another Entity Operator (a farmer) with is also mapped as an Entity.
The Entiteit Entity has been marked with the annotation @Inheritance(strategy = InheritanceType.SINGLE_TABLE) to be able to save all kinds of Entiteiten in the same table.
An Entiteit has an Embedded class OndernemingInformatie (CompanyInformation, like accountnumbers, firm numbers, ...). I have put the Annotation RekeningNummer to validate the format of an account number on a property of the OndernemingInformatie class.
When I create and persist an Operator object, which is a subclass of Entiteit, the Validator isn't called.
When I create and persist an Entiteit object, the Validator is fired and the isValid() method is called.
When I make the Entiteit class a @MappedSuperclass, the validator is used also when Operator is used.
Apparently there's a difference in inheriting properties and defined annotations when using an @Entity of an @MappedSuperclass.
The following code (lots) is used:
Code:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Entiteit extends DataElement implements AlgemeenElement {
...
   private OndernemingInformatie ondernemingInformatie;
...
   /**
    * @return the ondernemingInformatie
    */
   @Embedded
   public OndernemingInformatie getOndernemingInformatie() {
      return ondernemingInformatie;
   }
...
   /**
    * @param ondernemingInformatie
    *            the ondernemingInformatie to set
    */
   public void setOndernemingInformatie(OndernemingInformatie ondernemingInformatie) {
      this.ondernemingInformatie = ondernemingInformatie;
   }
...
}
Code:
@Entity
public class Operator extends Entiteit implements AlgemeenElement {
...
}
Test code (after creating an Operator object):
Code:
...
       /**
    * Test het persisteren van een Operator object.
    */
   @Test
   public void testPersistOperator() {
      Operator operator = this.generateOperator();
      try {
         PersistenceUtils.beginTransaction();
         PersistenceUtils.getEntityManager().persist(operator);
         PersistenceUtils.commitTransaction();
         Assert.assertNotNull(operator.getId());
         operator = PersistenceUtils.getEntityManager().find(Operator.class, operator.getId());
      } catch (InvalidStateException e) {
         PersistenceUtils.rollbackTransaction();
         logger.error(e.getMessage(), e);
         ValidatorUtils.logInvalidValues(e);
         Assert.fail(e.getMessage());
      } catch (PersistenceException e) {
         PersistenceUtils.rollbackTransaction();
         logger.error(e.getMessage(), e);
         Assert.fail(e.getMessage());
      }
   }
...
Can anyone explain to me why the custom validator class for validating an Operator object is not used when I declare the Entiteit class as an Entity, and why it is called if I declare the Entiteit as a MappedSuperclass - but then I can't use Entiteit seperately.
Now i've declared it as a MappedSuperclass and as abstract. I might not need it seperately but it can occur that I need it.
Thanks in advance.