Ce texte est consacré à différentes approches de la validation des données: sur quels pièges un projet peut-il tomber et quelles méthodes et technologies doivent être suivies lors de la validation des données dans les applications Java.

J'ai souvent vu des projets dont les créateurs n'ont pas pris la peine de choisir une approche de validation des données. Les équipes ont travaillé sur le projet sous une pression incroyable sous la forme de délais et d'exigences vagues, et en conséquence, elles n'ont tout simplement pas eu le temps d'une validation précise et cohérente. Par conséquent, leur code de validation est dispersé partout: dans les extraits de code Javascript, les contrôleurs d'écran, dans les bacs de logique métier, les entités de domaine, les déclencheurs et les contraintes de base de données. Ce code était plein de déclarations if-else, il a jeté un tas d'exceptions et a essayé de comprendre où cette donnée particulière est validée là-bas ... En conséquence, au fur et à mesure que le projet se développe, il devient difficile et coûteux de se conformer aux exigences (souvent assez déroutant), et uniformité des approches de validation des données.
Existe-t-il un moyen simple et élégant de valider les données? Existe-t-il un moyen qui nous protégera du péché d'illisibilité, un moyen qui réunira toute la logique de validation, et qui a déjà été créé pour nous par les développeurs de frameworks Java populaires?
Oui, il existe un tel moyen.
Pour nous, les développeurs de la plateforme CUBA , il est très important que vous utilisiez les meilleures pratiques. Nous pensons que le code de validation devrait:
- Être réutilisable et suivre le principe DRY;
- Soyez naturel et compréhensible;
- Placé là où le développeur s'attend à le voir;
- Être capable de vérifier les données de différentes sources: interface utilisateur, appels SOAP, REST, etc.
- Travailler dans un environnement multi-thread sans problèmes;
- Appelé automatiquement dans l'application, sans qu'il soit nécessaire d'exécuter des vérifications manuellement;
- Pour donner à l'utilisateur des messages clairs et localisés dans des boîtes de dialogue concises;
- Suivez les normes.
Voyons comment cela peut être implémenté à l'aide d'un exemple d'application écrit à l'aide du cadre de la plateforme CUBA. Cependant, puisque CUBA est basé sur Spring et EclipseLink, la plupart des techniques utilisées ici fonctionneront sur toute autre plate-forme Java prenant en charge les spécifications JPA et Bean Validation.
Validation à l'aide de contraintes de base de données
La façon la plus courante et la plus évidente de valider les données est peut-être d'utiliser des restrictions au niveau de la base de données, par exemple, l'indicateur requis (pour les champs dont la valeur ne peut pas être vide), la longueur de la chaîne, les index uniques, etc. Cette méthode convient le mieux aux applications d'entreprise, car ce type de logiciel est généralement strictement axé sur le traitement des données. Cependant, même ici, les développeurs font souvent des erreurs en fixant des limites séparément pour chaque niveau de l'application. Le plus souvent, la raison réside dans la répartition des responsabilités entre les développeurs.
Prenons un exemple que la plupart d'entre nous connaissent, certains même d'après notre propre expérience ... Si la spécification indique qu'il devrait y avoir 10 caractères dans le champ du numéro de passeport, il est très probable que cela sera vérifié par tout le monde: un architecte DB en DDL, un développeur backend dans l'entité correspondante et Services REST, et enfin, le développeur de l'interface utilisateur directement sur le côté client. Ensuite, cette exigence change et le champ passe à 15 caractères. Les Devops modifient les valeurs des contraintes dans la base de données, mais rien ne change pour l'utilisateur, car côté client la restriction est la même ...
Tout développeur sait comment éviter ce problème - la validation doit être centralisée! Dans CUBA, une telle validation se trouve dans les annotations d'entité JPA. Sur la base de ces méta-informations, CUBA Studio va générer le script DDL correct et appliquer les validateurs côté client appropriés.

Si les annotations changent, CUBA mettra à jour les scripts DDL et générera les scripts de migration, de sorte que la prochaine fois que vous déploierez le projet, de nouvelles restrictions basées sur JPA entreront en vigueur à la fois dans l'interface et dans la base de données d'application.
Malgré la simplicité et l'implémentation au niveau de la base de données, ce qui donne une fiabilité absolue à cette méthode, la portée des annotations JPA est limitée aux cas les plus simples qui peuvent être exprimés dans la norme DDL et n'incluent pas les déclencheurs de base de données ou les procédures stockées. Ainsi, les contraintes basées sur JPA peuvent rendre un champ d'entité unique ou obligatoire ou définir une longueur de colonne maximale. Vous pouvez même définir une restriction unique sur la combinaison de colonnes à l'aide de l'annotation @UniqueConstraint
. Mais c'est probablement tout.
Quoi qu'il en soit, dans les cas nécessitant une logique de validation plus complexe, comme la vérification d'une valeur minimale / maximale dans un champ, la validation avec une expression régulière ou la réalisation d'une vérification personnalisée spécifique à votre application uniquement, l'approche connue sous le nom de «validation de bean» est appliquée. .
Validation du bean
Tout le monde sait que c'est une bonne pratique de suivre des normes qui ont un long cycle de vie, dont l'efficacité a été prouvée sur des milliers de projets. Java Bean Validation est une approche documentée dans JSR 380, 349 et 303 et leurs applications: Hibernate Validator et Apache BVal .
Bien que cette approche soit familière à de nombreux développeurs, elle est souvent sous-estimée. C'est un moyen facile d'intégrer la validation des données même dans les projets hérités, qui vous permet de créer des validations de manière claire, simple, fiable et aussi proche que possible de la logique métier.
L'utilisation de Bean Validation offre au projet de nombreux avantages:
- La logique de validation se situe à côté du domaine: la définition des restrictions pour les champs et les méthodes du bac se fait de manière naturelle et véritablement orientée objet.
- La norme de validation de bean nous donne des dizaines d' annotations de validation dès la sortie de la boîte , par exemple:
@NotNull
, @Size
, @Min
, @Max
, @Pattern
, @Email
, @Past
, pas tout à fait standard @URL
, @Length
, le @Length
le plus puissant et bien d'autres encore . - La norme ne nous limite pas aux annotations toutes faites et nous permet de créer les nôtres. Nous pouvons également créer une nouvelle annotation en combinant plusieurs autres, ou la définir en utilisant une classe Java distincte comme validateur.
Par exemple, dans l'exemple ci-dessus, nous pouvons définir l'annotation de niveau de la classe @ValidPassportNumber
pour vérifier que le numéro de passeport correspond au format en fonction de la valeur du champ country
. - Les contraintes peuvent être définies non seulement sur des champs ou des classes, mais également sur des méthodes et leurs paramètres. Cette approche est appelée «validation par contrat» et sera discutée un peu plus loin.
Lorsque l'utilisateur soumet les informations saisies, la plate-forme CUBA (comme certains autres cadres) démarre automatiquement la validation du bean, de sorte qu'elle affiche instantanément un message d'erreur si la validation échoue, et nous n'avons pas besoin d'exécuter les validateurs de bac manuellement.
Revenons à l'exemple avec le numéro de passeport, mais cette fois nous allons le compléter avec plusieurs restrictions de l'entité Personne:
- Le champ du
name
doit contenir au moins 2 caractères et doit être valide. (Comme vous pouvez le voir, l'expression rationnelle n'est pas simple, mais "Charles Ogier de Batz de Castelmore Comte d'Artagnan" passera le test, mais "R2D2" ne le sera pas); height
(hauteur) doit être dans l'intervalle suivant: 0 < height <= 300
cm;- Le champ de l'
email
doit contenir une chaîne qui correspond au format de l'e-mail correct.
Avec toutes ces vérifications, la classe Person ressemblera à ceci:
@Listeners("passportnumber_PersonEntityListener") @NamePattern("%s|name") @Table(name = "PASSPORTNUMBER_PERSON") @Entity(name = "passportnumber$Person") @ValidPassportNumber(groups = {Default.class, UiCrossFieldChecks.class}) @FraudDetectionFlag public class Person extends StandardEntity { private static final long serialVersionUID = -9150857881422152651L; @Pattern(message = "Bad formed person name: ${validatedValue}", regexp = "^[AZ][az]*(\\s(([az]{1,3})|(([az]+\\')?[AZ][az]*)))*$") @Length(min = 2) @NotNull @Column(name = "NAME", nullable = false) protected String name; @Email(message = "Email address has invalid format: ${validatedValue}", regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$") @Column(name = "EMAIL", length = 120) protected String email; @DecimalMax(message = "Person height can not exceed 300 centimeters", value = "300") @DecimalMin(message = "Person height should be positive", value = "0", inclusive = false) @Column(name = "HEIGHT") protected BigDecimal height; @NotNull @Column(name = "COUNTRY", nullable = false) protected Integer country; @NotNull @Column(name = "PASSPORT_NUMBER", nullable = false, length = 15) protected String passportNumber; ... }
Person.java
Je pense que l'utilisation d'annotations telles que @NotNull
, @DecimalMin
, @Length
, @Pattern
et similaires est assez évidente et ne nécessite aucun commentaire. Examinons de plus près l'implémentation de l'annotation @ValidPassportNumber
.
Notre tout nouveau @ValidPassportNumber
vérifie que Person#passportNumber
correspond au modèle d'expression régulière pour chaque pays spécifié par le champ Person#country
.
Tout d'abord, regardons la documentation (les manuels CUBA ou Hibernate sont très bien), selon elle, nous devons marquer notre classe avec cette nouvelle annotation et lui passer le paramètre groups
, où UiCrossFieldChecks.class
signifie que cette validation doit être exécutée au niveau croisé. validations - après avoir vérifié tous les champs individuels, et Default.class
enregistre la restriction dans le groupe de validation par défaut.
La description de l'annotation ressemble à ceci:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = ValidPassportNumberValidator.class) public @interface ValidPassportNumber { String message() default "Passport number is not valid"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
ValidPassportNumber.java
Ici, @Target(ElementType.TYPE)
indique que le but de cette annotation d'exécution est la classe, et @Constraint(validatedBy = … )
détermine que la validation est effectuée par la classe ValidPassportNumberValidator
qui implémente l'interface ConstraintValidator<...>
. Le code de validation lui-même se trouve dans la isValid(...)
, qui effectue la vérification proprement dite de manière assez simple:
public class ValidPassportNumberValidator implements ConstraintValidator<ValidPassportNumber, Person> { public void initialize(ValidPassportNumber constraint) { } public boolean isValid(Person person, ConstraintValidatorContext context) { if (person == null) return false; if (person.country == null || person.passportNumber == null) return false; return doPassportNumberFormatCheck(person.getCountry(), person.getPassportNumber()); } private boolean doPassportNumberFormatCheck(CountryCode country, String passportNumber) { ... } }
ValidPassportNumberValidator.java
C’est tout. Avec la plateforme CUBA, nous n'avons pas besoin d'écrire autre chose qu'une ligne de code qui fera fonctionner notre validation personnalisée et donnera des messages d'erreur à l'utilisateur.
Rien de compliqué, non?
Voyons maintenant comment tout cela fonctionne. Ici, CUBA a d'autres nishtyaki: il montre non seulement à l'utilisateur un message d'erreur, mais met également en évidence dans les champs rouges qui n'ont pas passé la validation du bean:

N'est-ce pas une solution élégante? Vous obtenez un affichage adéquat des erreurs de validation dans l'interface utilisateur en ajoutant seulement quelques annotations Java aux entités du domaine.
Pour résumer la section, énumérons brièvement les avantages de la validation du bean pour les entités:
- C'est compréhensible et lisible;
- Vous permet de définir des contraintes de valeur directement dans les classes d'entités;
- Il peut être personnalisé et complété;
- Intégré dans les ORM populaires, et les vérifications sont exécutées automatiquement avant que les modifications ne soient enregistrées dans la base de données;
- Certains frameworks exécutent également la validation du bean automatiquement lorsque l'utilisateur envoie des données à l'interface utilisateur (et sinon, il est facile d'appeler manuellement l'interface
Validator
); - Bean Validation est une norme reconnue et regorge de documentation sur Internet.
Mais que faire si vous devez définir une restriction sur une méthode, un constructeur ou une adresse REST pour valider les données provenant d'un système externe? Ou si vous devez vérifier de manière déclarative les valeurs des paramètres de la méthode, sans écrire de code ennuyeux avec de nombreuses conditions if-else dans chaque méthode testée?
La réponse est simple: la validation du bean s'applique également aux méthodes!
Validation par contrat
Il faut parfois dépasser la validation de l'état du modèle de données. De nombreuses méthodes peuvent bénéficier de la validation automatique des paramètres et de la valeur de retour. Cela peut être nécessaire non seulement pour vérifier les données allant aux adresses REST ou SOAP, mais aussi dans les cas où nous voulons noter les conditions préalables et postconditions des appels de méthode pour nous assurer que les données entrées ont été vérifiées avant l'exécution du corps de la méthode ou que la valeur de retour est dans la plage attendue, ou par exemple, nous avons juste besoin de décrire de manière déclarative les plages de valeurs des paramètres d'entrée pour améliorer la lisibilité du code.
En utilisant la validation du bean, des restrictions peuvent être appliquées aux paramètres d'entrée et aux valeurs de retour des méthodes et constructeurs pour vérifier les conditions préalables et postconditions de leurs appels dans n'importe quelle classe Java. Ce chemin présente plusieurs avantages par rapport aux méthodes traditionnelles de vérification de la validité des paramètres et des valeurs de retour:
- Il n'est pas nécessaire d'effectuer des vérifications manuellement dans un style impératif (par exemple, en lançant
IllegalArgumentException
, etc.). Vous pouvez définir des contraintes de manière déclarative et rendre le code plus compréhensible et expressif; - Les contraintes peuvent être configurées, réutilisées et configurées: vous n'avez pas besoin d'écrire de logique de validation pour chaque vérification. Moins de code signifie moins de bugs.
- Si la classe, la valeur de retour de la méthode ou son paramètre est marqué avec l'annotation
@Validated
, les vérifications seront automatiquement effectuées par la plateforme à chaque appel de la méthode. - Si l'exécutable est marqué avec l'annotation
@Documented
, ses pré et postconditions seront incluses dans le JavaDoc généré.
En utilisant la «validation de contrat», nous obtenons un code clair, compact et facile à maintenir.
Pour un exemple, regardons l'interface du contrôleur REST d'une application CUBA. L'interface PersonApiService
vous permet d'obtenir une liste de personnes à partir de la base de données à l'aide de la méthode getPersons()
et d'ajouter une nouvelle personne à l'aide de l' addNewPerson(...)
.
Et n'oubliez pas que la validation du bean est héritée! En d'autres termes, si nous annotons une certaine classe, ou champ, ou méthode, toutes les classes qui héritent de cette classe ou implémentent cette interface seront soumises à la même annotation de validation.
@Validated public interface PersonApiService { String NAME = "passportnumber_PersonApiService"; @NotNull @Valid @RequiredView("_local") List<Person> getPersons(); void addNewPerson( @NotNull @Length(min = 2, max = 255) @Pattern(message = "Bad formed person name: ${validatedValue}", regexp = "^[AZ][az]*(\\s(([az]{1,3})|(([az]+\\')?[AZ][az]*)))*$") String name, @DecimalMax(message = "Person height can not exceed 300 cm", value = "300") @DecimalMin(message = "Person height should be positive", value = "0", inclusive = false) BigDecimal height, @NotNull CountryCode country, @NotNull String passportNumber ); }
PersonApiService.java
Ce morceau de code est-il suffisamment clair?
_ (À l'exception de l'annotation @RequiredView(“_local”)
, qui est spécifique à la plate-forme CUBA et vérifie que l'objet Person
retourné contient tous les champs de la table PASSPORTNUMBER_PERSON
) ._
L' @Valid
définit que chaque objet de collection retourné par la méthode getPersons()
doit également être validé par rapport aux restrictions de la classe Person
.
Dans une application CUBA, ces méthodes sont disponibles aux adresses suivantes:
- / app / rest / v2 / services / passportnumber_PersonApiService / getPersons
- / app / rest / v2 / services / passportnumber_PersonApiService / addNewPerson
Ouvrons l'application Postman et assurons-nous que la validation fonctionne comme il se doit:

Comme vous l'avez peut-être remarqué, le numéro de passeport n'est pas validé dans l'exemple ci-dessus. En effet, ce champ nécessite une vérification croisée des paramètres de la méthode addNewPerson
, car le choix d'un modèle d'expression régulière pour valider passportNumber
dépend de la valeur du champ country
. Cette validation croisée est un analogue complet des restrictions d'entité au niveau de la classe!
La validation croisée des paramètres est prise en charge par JSR 349 et 380. Vous pouvez lire la documentation de mise en veille prolongée pour savoir comment implémenter votre propre validation croisée des méthodes de classe / interface.
Validation du bean extérieur
Il n'y a pas de perfection dans le monde, la validation du bean a donc ses inconvénients et ses limites:
- Parfois, il suffit de vérifier l'état d'un graphe complexe d'objets avant d'enregistrer les modifications dans la base de données. Par exemple, vous devez vous assurer que tous les éléments d'une commande client sont placés dans un seul colis. C'est une opération assez difficile, et la réaliser à chaque fois que l'utilisateur ajoute de nouveaux articles à la commande n'est pas une bonne idée. Par conséquent, une telle vérification peut être nécessaire une seule fois: avant d'enregistrer l'objet
Order
et ses sous-objets OrderItem
dans la base de données. - Certains contrôles doivent être effectués à l'intérieur d'une transaction. Par exemple, le système de magasin électronique doit vérifier s'il y a suffisamment de copies des marchandises pour exécuter la commande avant de les enregistrer dans la base de données. Un tel contrôle ne peut être effectué qu'à l'intérieur d'une transaction, car Le système est multithread et la quantité de marchandises en stock peut changer à tout moment.
La plateforme CUBA propose deux mécanismes de validation des données avant validation appelés écouteurs d'entité et écouteurs de transaction . Examinons-les plus en détail.
Listeurs d'entités
Les écouteurs d'entité dans CUBA sont très similaires aux PreInsertEvent
, PreUpdateEvent
et PredDeleteEvent
que JPA propose au développeur. Les deux mécanismes vous permettent de vérifier les objets d'entité avant et après leur stockage dans la base de données.
Dans CUBA, il est facile de créer et de connecter un écouteur d'entité, pour cela, vous avez besoin de deux choses:
- Créez un bean géré qui implémente l'une des interfaces d'écoute d'entité. 3 interfaces sont importantes pour la validation:
BeforeDeleteEntityListener<T>
,
BeforeInsertEntityListener<T>
,
BeforeUpdateEntityListener<T>
- Ajoutez l'annotation
@Listeners
à l'objet entité que vous prévoyez de suivre.
Et c'est tout.
Par rapport à la norme JPA (JSR 338, section 3.5), les interfaces d'écoute de la plateforme CUBA sont typées, vous n'avez donc pas besoin de convertir un argument avec un type d' Object
un type d'entité pour commencer à travailler avec lui. La plate-forme CUBA ajoute des entités liées ou des appelants EntityManager la possibilité de charger et de modifier d'autres entités. Toutes ces modifications invoqueront également l'écouteur d'entité correspondant.
La plate-forme CUBA prend également en charge la «suppression logicielle» , une approche où au lieu de supprimer réellement les enregistrements de la base de données, ils sont uniquement marqués comme supprimés et deviennent inaccessibles pour une utilisation normale. Ainsi, pour la suppression BeforeDeleteEntityListener
, la plate-forme appelle les BeforeDeleteEntityListener
/ AfterDeleteEntityListener
, tandis que les implémentations standard appellent les PreUpdate
/ PostUpdate
.
Regardons un exemple. Ici, le bean @Listeners
événement se connecte à la classe d'entité avec une seule ligne de code: l'annotation @Listeners
, qui prend le nom de la classe d'écoute:
@Listeners("passportnumber_PersonEntityListener") @NamePattern("%s|name") @Table(name = "PASSPORTNUMBER_PERSON") @Entity(name = "passportnumber$Person") @ValidPassportNumber(groups = {Default.class, UiCrossFieldChecks.class}) @FraudDetectionFlag public class Person extends StandardEntity { ... }
Person.java
L'implémentation de l'écouteur elle-même ressemble à ceci:
@Component("passportnumber_PersonEntityListener") public class PersonEntityListener implements BeforeDeleteEntityListener<Person>, BeforeInsertEntityListener<Person>, BeforeUpdateEntityListener<Person> { @Override public void onBeforeDelete(Person person, EntityManager entityManager) { if (!checkPassportIsUnique(person.getPassportNumber(), person.getCountry(), entityManager)) { throw new ValidationException( "Passport and country code combination isn't unique"); } } @Override public void onBeforeInsert(Person person, EntityManager entityManager) {
PersonEntityListener.java
Les auditeurs d'entités sont un excellent choix si:
- Il est nécessaire de vérifier les données à l'intérieur de la transaction avant que l'objet entité ne soit stocké dans la base de données;
- Il est nécessaire de vérifier les données dans la base de données pendant le processus de validation, par exemple, pour vérifier qu'il y a suffisamment de produit en stock pour accepter la commande;
- Vous devez regarder non seulement l'objet entité, tel que
Order
, mais également les entités liées, par exemple, OrderItems
pour l'entité Order
; - Nous voulons suivre les opérations d'insertion, de mise à jour ou de suppression uniquement pour certaines classes d'entités, par exemple, uniquement pour les
OrderItem
Order
et OrderItem
, et nous n'avons pas besoin de vérifier les changements dans d'autres classes d'entités pendant la transaction.
Auditeurs de transactions
Les écouteurs de transaction CUBA agissent également dans le contexte des transactions, mais, par rapport aux écouteurs d'entité, ils sont appelés pour chaque transaction de base de données.
Cela leur donne un super pouvoir:
- rien ne peut leur échapper.
Mais la même chose est déterminée par leurs lacunes:
- ils sont plus difficiles à écrire;
- ils peuvent réduire considérablement les performances;
- Ils doivent être écrits très attentivement: un bogue dans l'écouteur de transactions peut interférer même avec le chargement initial de l'application.
Ainsi, les écouteurs de transactions sont une bonne solution lorsque vous devez inspecter différents types d'entités à l'aide du même algorithme, par exemple, en vérifiant toutes les données pour détecter la fraude informatique avec un seul service qui dessert tous vos objets métier.

Jetez un œil à un exemple qui vérifie si l'entité a une annotation @FraudDetectionFlag
et, le cas échéant, démarre un détecteur de fraude. Je répète: gardez à l'esprit que cette méthode est appelée sur le système avant de valider chaque transaction de base de données , donc le code doit essayer de vérifier le moins d'objets possible.
@Component("passportnumber_ApplicationTransactionListener") public class ApplicationTransactionListener implements BeforeCommitTransactionListener { private Logger log = LoggerFactory.getLogger(ApplicationTransactionListener.class); @Override public void beforeCommit(EntityManager entityManager, Collection<Entity> managedEntities) { for (Entity entity : managedEntities) { if (entity instanceof StandardEntity && !((StandardEntity) entity).isDeleted() && entity.getClass().isAnnotationPresent(FraudDetectionFlag.class) && !fraudDetectorFeedAndFastCheck(entity)) { logFraudDetectionFailure(log, entity); String msg = String.format( "Fraud detection failure in '%s' with id = '%s'", entity.getClass().getSimpleName(), entity.getId()); throw new ValidationException(msg); } } } ... }
ApplicationTransactionListener.java
Pour devenir un écouteur de transactions, un bean géré doit implémenter l'interface BeforeCommitTransactionListener
et la méthode beforeCommit
. Les écouteurs de transactions se lient automatiquement au démarrage de l'application. CUBA enregistre toutes les classes qui implémentent BeforeCommitTransactionListener
ou AfterCompleteTransactionListener
comme écouteurs de transactions.
Conclusion
La validation du bean (JPA 303, 349 et 980) est une approche qui peut servir de base fiable pour 95% des cas de validation de données rencontrés dans un projet d'entreprise. Le principal avantage de cette approche est que la majeure partie de la logique de validation est concentrée directement dans les classes de modèle de domaine. Par conséquent, il est facile à trouver, à lire et à entretenir. Spring, CUBA et de nombreuses autres bibliothèques prennent en charge ces normes et effectuent automatiquement des vérifications de validation lors de la réception de données sur la couche d'interface utilisateur, de l'appel de méthodes validées ou du stockage de données via ORM, de sorte que la validation Bean ressemble souvent à de la magie du point de vue du développeur.
Certains développeurs de logiciels considèrent que la validation au niveau des classes du modèle sujet n'est pas naturelle et trop complexe, ils disent que la validation des données au niveau de l'interface utilisateur est une stratégie assez efficace. Cependant, je crois que de nombreux points de validation dans les composants et les contrôleurs d'interface utilisateur ne sont pas l'approche la plus rationnelle. , , , , , listener' .
, , :
- JPA , , DDL.
- Bean Validation — , , , . , .
- bean validation, . , , REST.
- Entity listeners: , Bean Validation, . , . Hibernate .
- Transaction listeners — , , . , , .
PS: , Java, , , .
Liens utiles
Texte masquéNormes et leur mise en œuvre