126 votes

Comment puis-je valider deux champs ou plus en combinaison ?

J'utilise la validation JPA 2.0/Hibernate pour valider mes modèles. J'ai maintenant une situation où la combinaison de deux champs doit être validée :

public class MyModel {
    public Integer getValue1() {
        //...
    }
    public String getValue2() {
        //...
    }
}

Le modèle est invalide si les deux getValue1() y getValue2() sont null et valide dans le cas contraire.

Comment puis-je effectuer ce type de validation avec JPA 2.0/Hibernate ? Avec un simple @NotNull les deux getters doivent être non nuls pour passer la validation.

2 votes

126voto

Pascal Thivent Points 295221

Pour la validation de propriétés multiples, vous devez utiliser des contraintes au niveau de la classe. A partir de Sneak Peek de la validation des haricots partie II : contraintes personnalisées :

Contraintes au niveau des classes

Certains d'entre vous ont exprimé des inquiétudes sur la possibilité d'appliquer une contrainte couvrant plusieurs ou d'exprimer des contraintes qui dépendent de plusieurs propriétés. L'exemple classique est celui de la de l'adresse. Les adresses ont des règles complexes :

  • un nom de rue est quelque peu standard et doit certainement avoir une limite de longueur
  • la structure du code postal dépend entièrement du pays
  • la ville peut souvent être mise en corrélation avec un code postal et un contrôle d'erreur peut être effectué. d'erreurs (à condition qu'un service de soit accessible)
  • En raison de ces interdépendances, une simple contrainte au niveau de la propriété n'est pas suffisante. ne fait pas l'affaire

La solution proposée par le haricot est double :

  • il offre la possibilité de forcer l'application d'un ensemble de contraintes avant qu'un projet ne soit mis en œuvre. avant un autre ensemble de contraintes grâce à l'utilisation l'utilisation de groupes et de séquences de groupes. Ce sujet sera abordé dans le prochain article du blog
  • il permet de définir des contraintes au niveau de la classe

Les contraintes au niveau des classes sont régulières régulières (duo annotation / duo d'implémentation) qui s'appliquent à une classe plutôt qu'à une propriété. classe plutôt que sur une propriété. En d'autres termes autrement, les contraintes de niveau classe reçoivent l'instance de l'objet (plutôt que plutôt que la valeur de la propriété) en isValid .

@AddressAnnotation 
public class Address {
    @NotNull @Max(50) private String street1;
    @Max(50) private String street2;
    @Max(10) @NotNull private String zipCode;
    @Max(20) @NotNull String city;
    @NotNull private Country country;

    ...
}

@Constraint(validatedBy = MultiCountryAddressValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddressAnnotation {
    String message() default "{error.address}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

public class MultiCountryAddressValidator implements ConstraintValidator<AddressAnnotation, Address> {
    public void initialize(AddressAnnotation constraintAnnotation) {
    // initialize the zipcode/city/country correlation service
    }

    /**
     * Validate zipcode and city depending on the country
     */
    public boolean isValid(Address object, ConstraintValidatorContext context) {
        if (!(object instanceof Address)) {
            throw new IllegalArgumentException("@AddressAnnotation only applies to Address objects");
        }
        Address address = (Address) object;
        Country country = address.getCountry();
        if (country.getISO2() == "FR") {
            // check address.getZipCode() structure for France (5 numbers)
            // check zipcode and city correlation (calling an external service?)
            return isValid;
        } else if (country.getISO2() == "GR") {
            // check address.getZipCode() structure for Greece
            // no zipcode / city correlation available at the moment
            return isValid;
        }
        // ...
    }
}

Les règles avancées de validation des adresses n'ont pas été intégrées à l'objet et mises en œuvre par l'objet MultiCountryAddressValidator . En accédant à l'instance de l'objet, la classe en accédant à l'instance de l'objet, la classe ont une grande souplesse d'utilisation. flexibilité et peuvent valider plusieurs propriétés propriétés corrélées. Notez que l'ordre l'ordonnancement n'est pas pris en compte dans l'équation ici, nous y reviendrons dans la prochain article.

Le groupe d'experts a discuté de plusieurs soutien aux propriétés multiples nous pensons que l'approche des contraintes au niveau de la classe au niveau de la classe offre à la fois suffisamment de simplicité et de flexibilité par rapport à d'autres approches au niveau des propriétés impliquant des dépendances. Vos commentaires sont les bienvenus.

53voto

Pour fonctionner correctement avec Validation des haricots L'exemple fourni dans l'ouvrage de Pascal Thivent, intitulé répondre pourrait être réécrite comme suit :

@ValidAddress
public class Address {

    @NotNull
    @Size(max = 50)
    private String street1;

    @Size(max = 50)
    private String street2;

    @NotNull
    @Size(max = 10)
    private String zipCode;

    @NotNull
    @Size(max = 20)
    private String city;

    @Valid
    @NotNull
    private Country country;

    // Getters and setters
}

public class Country {

    @NotNull
    @Size(min = 2, max = 2)
    private String iso2;

    // Getters and setters
}

@Documented
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = { MultiCountryAddressValidator.class })
public @interface ValidAddress {

    String message() default "{com.example.validation.ValidAddress.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

public class MultiCountryAddressValidator 
       implements ConstraintValidator<ValidAddress, Address> {

    public void initialize(ValidAddress constraintAnnotation) {

    }

    @Override
    public boolean isValid(Address address, 
                           ConstraintValidatorContext constraintValidatorContext) {

        Country country = address.getCountry();
        if (country == null || country.getIso2() == null || address.getZipCode() == null) {
            return true;
        }

        switch (country.getIso2()) {
            case "FR":
                return // Check if address.getZipCode() is valid for France
            case "GR":
                return // Check if address.getZipCode() is valid for Greece
            default:
                return true;
        }
    }
}

16voto

J.F. Points 5455

Vous pouvez utiliser @javax.validation.constraints.AssertTrue des validations comme celle-ci :

public class MyModel {

    private String value1;
    private String value2;

    @AssertTrue(message = "Values are invalid")
    private boolean isValid() {
        return value1 != null || value2 != null;
    }
}

1 votes

Merci pour cette solution. Au départ, je voulais utiliser @ScriptAssert mais certains haricots ont de nombreuses règles à valider, ce qui est beaucoup plus intuitif : une méthode de validation par règle.

13voto

Hardy Points 6274

Un validateur personnalisé au niveau de la classe est la meilleure solution si vous souhaitez respecter la spécification de la validation des haricots, par exemple aquí .

Si vous souhaitez utiliser une fonctionnalité du validateur Hibernate, vous pouvez utiliser @ScriptAssert qui est fourni depuis Validator-4.1.0.Final. A l'exception de sa JavaDoc :

Les expressions script peuvent être écrites dans n'importe quel script. pour lequel un JSR 223 ("Scripting for the JavaTM Platform") peut être trouvé sur le chemin d'accès (classpath).

Exemple :

@ScriptAssert(lang = "javascript", script = "_this.value1 != null || _this != value2)")
public class MyBean {
  private String value1;
  private String value2;
}

0voto

Langage de programmation : Java

Voici une solution qui m'a aidé.

Exigences :

  1. Sur l'interface utilisateur, il y a une table qui contient une liste d'objets qui est mappée à plusieurs tables/objets avec une relation de type fk.

  2. Maintenant, la validation est que sur plusieurs fichiers, il n'y a que 3 colonnes qui ne peuvent pas être dupliquées. Je veux dire que la combinaison de 3 ne peut pas être dupliquée.

Note : Comme je travaille sur un Custom Framework en Java, il n'y a pas d'option pour utiliser HashCode ou equals. Si j'utilise l'itération de l'index du tableau, cela augmentera la complexité du temps, ce que je ne souhaite pas.

Solution :

J'ai préparé une chaîne, qui est une chaîne personnalisée contenant l'ID de FK1#ID de FK2#ID de FK3. Ex : la chaîne aura la forme suivante -> 1000L#3000L#1300L#

Cette chaîne, nous allons l'ajouter à un ensemble en utilisant add() de l'ensemble qui renverra false si un doublon apparaît.

Sur la base de ce drapeau, nous pouvons envoyer un message de validation.

Cela m'a aidé. Dans certains scénarios et certaines restrictions, la DS peut ne pas être utile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X