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.