Les validateurs personnalisés JSR303 appelés deux fois

Je crée un site Web à l’aide de Spring MVC et, par souci de persévérance, j’utilise Spring Data JPA avec Hibernate 4 comme fournisseur JPA. La validation est en cours de traitement avec Hibernate Validator. J’ai un problème qui fait que mes validateurs sont appelés deux fois et je ne comprends pas pourquoi. La raison principale en est que la deuxième fois, les dépendances ne sont pas automatiquement transférées dans le validateur et que je reçois une exception de pointeur null. Voici la séquence des appels ayant abouti à l’échec:

  1. Le formulaire d’inscription est soumis. NotDefaultSectValidator est d’abord appelé et se termine correctement pour le champ ‘whereDidYouHearAboutUs’ de l’object utilisateur.
  2. UniqueUsernameValidator est appelé next et se termine avec succès pour la validation du champ ‘nom d’utilisateur’.
  3. La méthode ‘addUserFromForm’ sur le contrôleur démarre et ne trouve aucune erreur dans l’object bindingResults.
  4. La méthode ‘addUser’ est ensuite appelée sur la classe UserService. Cette méthode atteint la ligne ‘userRepository.save (user);’ mais ne lance ensuite jamais la ligne ‘print.ln’ immédiatement après. Pour franchir cette ligne, revenez au point d’arrêt ‘NotDefaultSectValidator’. Cela se termine pour la deuxième fois et je ressaisis le deuxième validateur ‘UniqueUsernameValidator’. Ici, j’obtiens une exception de pointeur null car, pour une raison quelconque, Spring ne parvient pas à transférer automatiquement le fichier dans le DAO cette deuxième fois.

Quelqu’un peut-il expliquer pourquoi les validateurs sont appelés deux fois et, en particulier, pourquoi franchir la ligne ‘userRepository.save (user);’ retourne dans ces validateurs?

Merci beaucoup

Voici ma classe user.java

package com.dating.domain; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Table; import javax.persistence.Transient; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import org.hibernate.annotations.Type; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; import org.joda.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; import com.dating.annotation.NotDefaultSelect; import com.dating.annotation.UniqueUsername; @Entity @Table(name = "dating.user") public class User { @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "username", unique = true) @NotEmpty @Pattern(regexp = "^[a-zA-Z0-9]*$") @UniqueUsername private Ssortingng username; @Column(name = "password", nullable = false) @NotEmpty @Size(min = 8) private Ssortingng password; @Column(name = "first_name", nullable = false) @NotEmpty private Ssortingng firstName; @Column(name = "last_name", nullable = false) @NotEmpty private Ssortingng lastName; @Transient private Ssortingng fullName; @Column(name = "email", nullable = false) @NotEmpty @Email private Ssortingng email; @Column(name = "gender", nullable = false) @NotEmpty private Ssortingng gender; @Column(name = "date_of_birth", nullable = false) @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") @DateTimeFormat(pattern = "dd/MM/yyyy") private LocalDate dateOfBirth; @Column(name = "join_date", nullable = false) @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") private LocalDate joinDate; @Column(name = "where_did_you_hear_about_us", nullable = false) @NotDefaultSelect private Ssortingng whereDidYouHearAboutUs; @Column(name = "enabled") private boolean enabled; @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinTable(name = "dating.user_roles", joinColumns = { @JoinColumn(name = "user_id", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) }) private Set roles = new HashSet(); @Column(name = "created_time", nullable = false) @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") private LocalDate createdTime; @Column(name = "modification_time", nullable = false) @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate") private LocalDate modificationTime; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Ssortingng getUsername() { return username; } public void setUsername(Ssortingng username) { this.username = username; } public Ssortingng getPassword() { return password; } public void setPassword(Ssortingng password) { this.password = password; } public Ssortingng getFirstName() { return firstName; } public void setFirstName(Ssortingng firstName) { this.firstName = firstName; } public Ssortingng getLastName() { return lastName; } public void setLastName(Ssortingng lastName) { this.lastName = lastName; } public Ssortingng getFullName() { return firstName + " " + lastName; } public void setFullName(Ssortingng fullName) { this.fullName = fullName; } public Ssortingng getEmail() { return email; } public void setEmail(Ssortingng email) { this.email = email; } public Ssortingng getGender() { return gender; } public void setGender(Ssortingng gender) { this.gender = gender; } public LocalDate getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(LocalDate dateOfBirth) { this.dateOfBirth = dateOfBirth; } public LocalDate getJoinDate() { return joinDate; } public void setJoinDate(LocalDate joinDate) { this.joinDate = joinDate; } public Ssortingng getWhereDidYouHearAboutUs() { return whereDidYouHearAboutUs; } public void setWhereDidYouHearAboutUs(Ssortingng whereDidYouHearAboutUs) { this.whereDidYouHearAboutUs = whereDidYouHearAboutUs; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Set getRoles() { return roles; } public void setRoles(Set roles) { this.roles = roles; } public void addRole(Role role) { roles.add(role); } public LocalDate getCreatedTime() { return createdTime; } public void setCreatedTime(LocalDate createdTime) { this.createdTime = createdTime; } public LocalDate getModificationTime() { return modificationTime; } public void setModificationTime(LocalDate modificationTime) { this.modificationTime = modificationTime; } @PreUpdate public void preUpdate() { modificationTime = new LocalDate(); } @PrePersist public void prePersist() { LocalDate now = new LocalDate(); createdTime = now; modificationTime = now; } } 

La méthode appropriée dans mon contrôleur d’enregistrement:

 @RequestMapping(value = "/register", method = RequestMethod.POST) public Ssortingng addUserFromForm(@Valid User user, BindingResult bindingResult, RedirectAtsortingbutes ra) { if (bindingResult.hasErrors()) { return "user/register"; } userService.addUser(user); // Redirecting to avoid duplicate submission of the form return "redirect:/user/" + user.getUsername(); } 

Ma classe de service:

 package com.dating.service.impl; import javax.transaction.Transactional; import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import com.dating.domain.Role; import com.dating.domain.User; import com.dating.repository.RoleRepository; import com.dating.repository.UserRepository; import com.dating.repository.specification.UserSpecifications; import com.dating.service.UserService; @Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Transactional @Override public void addUser(User user) { user.setJoinDate(new LocalDate()); user.setEnabled(true); Role role = roleRepository.findByName(Role.MEMBER); if (role == null) { role = new Role(); role.setName(Role.MEMBER); } user.addRole(role); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); user.setPassword(encoder.encode(user.getPassword())); userRepository.save(user); System.out.println("User Saved"); } @Override public User getUserByUsername(Ssortingng username) { return userRepository.findByUsername(username); } @Override public Iterable getAllUsers() { return userRepository.findAll(); } @Override public void updateDetails(User user) { userRepository.save(user); } @Override public Iterable lastNameIsLike(Ssortingng searchTerm) { return userRepository.findAll(UserSpecifications .lastNameIsLike(searchTerm)); } } 

Mon validateur NotDefaultSelect:

 package com.dating.validator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import com.dating.annotation.NotDefaultSelect; public class NotDefaultSelectValidator implements ConstraintValidator { @Override public void initialize(NotDefaultSelect constraint) { } @Override public boolean isValid(Ssortingng selectedValue, ConstraintValidatorContext ctx) { if (selectedValue == null) { return false; } if (selectedValue.equals("") || selectedValue.equals("0") || selectedValue.equalsIgnoreCase("default") || selectedValue.equalsIgnoreCase("please select")) { return false; } return true; } } 

Mon unique validateur d’utilisateur:

 package com.dating.validator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.springframework.beans.factory.annotation.Autowired; import com.dating.annotation.UniqueUsername; import com.dating.repository.UserRepository; public class UniqueUsernameValidator implements ConstraintValidator { @Autowired private UserRepository userRepository; @Override public void initialize(UniqueUsername constraint) { } @Override public boolean isValid(Ssortingng username, ConstraintValidatorContext ctx) { if (username == null || userRepository.findByUsername(username) == null) { return true; } return false; } } 

Mon UserRepository:

 package com.dating.repository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; import com.dating.domain.User; //Spring Data JPA Marker interfaces being extended for automatic CRUD repository creation public interface UserRepository extends CrudRepository, JpaSpecificationExecutor { //Automatic query creation from method name public User findByUsername(Ssortingng username); } 

Enfin, mon fichier persistence-context.xml

                           #{dataSourceSettings['hibernate.dialect']} #{dataSourceSettings['hibernate.hbm2ddl.auto']}  #{dataSourceSettings['hibernate.show_sql']} #{dataSourceSettings['hibernate.format_sql']} #{dataSourceSettings['hibernate.use_sql_comments']}           

Peut-être que la deuxième validation est effectuée par hibernation lorsque vous envoyez votre bean au magasin de données. Pour le désactiver, ajoutez ceci à votre persistence.xml:

  

https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/configuration.html dit:

Par défaut, la validation des beans (et Hibernate Validator) est activée. Lorsqu’une entité est créée, mise à jour (et éventuellement supprimée), elle est validée avant d’être envoyée à la firebase database. Le schéma de firebase database généré par Hibernate reflète également les contraintes déclarées sur l’entité.

Vous pouvez ajuster cela si nécessaire:

AUTO: si la validation de bean est présente dans le chemin de classe, CALLBACK et DDL sont activés.

RAPPEL: les entités sont validées lors de la création, de la mise à jour et de la suppression. Si aucun fournisseur de validation de bean n’est présent, une exception est générée au moment de l’initialisation.

DDL: (non standard, voir ci-dessous) les schémas de firebase database sont des entités validées lors de la création, de la mise à jour et de la suppression. Si aucun fournisseur de validation de bean n’est présent, une exception est générée au moment de l’initialisation.

NONE: la validation de bean n’est pas utilisée du tout

Le premier est évidemment effectué par votre contrôleur Spring en raison de l’annotation @Valid.