I am building a website using Spring MVC and for saving. I am using Spring Data JPA with Hibernate 4 as my JPA provider. Verification is currently being performed using the Hibernate Validator. I have a problem when my validators are called twice and I canโt understand why. The main reason this problem is because the second time the dependencies were not automatically included in the validator, and I get a null pointer exception. The following is the sequence of calls leading to failure:
- The registration form is registered, and first NotDefaultSectValidator is called and succeeds for the 'whereDidYouHearAboutUs' field of the user object.
- The UniqueUsernameValidator property is called next and succeeds to validate the username field.
- The addUserFromForm method on the controller runs and does not detect errors in the bindingResults object.
- Then, the addUser method is called in the UserService class. This method reaches the line userRepository.save (user); but it never runs the string "print.ln" right after that. Skipping this line returns to the "NotDefaultSectValidator" breakpoint. This completes a second time, and I re-enter the second validator "UniqueUsernameValidator". Here I get a null pointer exception, because for some reason Spring is not running Autowire in the DAO this second time.
Can anyone shed some light on why validators call twice and, in particular, why go through the line 'userRepository.save (user);' returns to these validators?
Thank you very much
Here is my user.java class
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 String username; @Column(name = "password", nullable = false) @NotEmpty @Size(min = 8) private String password; @Column(name = "first_name", nullable = false) @NotEmpty private String firstName; @Column(name = "last_name", nullable = false) @NotEmpty private String lastName; @Transient private String fullName; @Column(name = "email", nullable = false) @NotEmpty @Email private String email; @Column(name = "gender", nullable = false) @NotEmpty private String 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 String 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<Role> roles = new HashSet<Role>(); @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 String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getFullName() { return firstName + " " + lastName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getGender() { return gender; } public void setGender(String 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 String getWhereDidYouHearAboutUs() { return whereDidYouHearAboutUs; } public void setWhereDidYouHearAboutUs(String whereDidYouHearAboutUs) { this.whereDidYouHearAboutUs = whereDidYouHearAboutUs; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> 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; } }
The corresponding method in my registration controller:
@RequestMapping(value = "/register", method = RequestMethod.POST) public String addUserFromForm(@Valid User user, BindingResult bindingResult, RedirectAttributes ra) { if (bindingResult.hasErrors()) { return "user/register"; } userService.addUser(user); // Redirecting to avoid duplicate submission of the form return "redirect:/user/" + user.getUsername(); }
My class of 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(String username) { return userRepository.findByUsername(username); } @Override public Iterable<User> getAllUsers() { return userRepository.findAll(); } @Override public void updateDetails(User user) { userRepository.save(user); } @Override public Iterable<User> lastNameIsLike(String searchTerm) { return userRepository.findAll(UserSpecifications .lastNameIsLike(searchTerm)); } }
My validator NotDefaultSelect:
package com.dating.validator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import com.dating.annotation.NotDefaultSelect; public class NotDefaultSelectValidator implements ConstraintValidator<NotDefaultSelect, String> { @Override public void initialize(NotDefaultSelect constraint) { } @Override public boolean isValid(String 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; } }
My unique username verification id:
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<UniqueUsername, String> { @Autowired private UserRepository userRepository; @Override public void initialize(UniqueUsername constraint) { } @Override public boolean isValid(String username, ConstraintValidatorContext ctx) { if (username == null || userRepository.findByUsername(username) == null) { return true; } return false; } }
My 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<User, Long>, JpaSpecificationExecutor<User> { //Automatic query creation from method name public User findByUsername(String username); }
Finally, persistence-context.xml
<util:properties id="dataSourceSettings" location="classpath:datasource.properties" /> <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close"> <property name="driverClass" value="#{dataSourceSettings['jdbc.driverClass']}" /> <property name="jdbcUrl" value="#{dataSourceSettings['jdbc.url']}" /> <property name="username" value="#{dataSourceSettings['jdbc.username']}" /> <property name="password" value="#{dataSourceSettings['jdbc.password']}" /> <property name="idleConnectionTestPeriodInMinutes" value="60" /> <property name="idleMaxAgeInMinutes" value="240" /> <property name="maxConnectionsPerPartition" value="30" /> <property name="minConnectionsPerPartition" value="10" /> <property name="partitionCount" value="3" /> <property name="acquireIncrement" value="5" /> <property name="statementsCacheSize" value="100" /> <property name="releaseHelperThreads" value="3" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan" value="com.dating.domain" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">#{dataSourceSettings['hibernate.dialect']}</prop> <prop key="hibernate.hbm2ddl.auto">#{dataSourceSettings['hibernate.hbm2ddl.auto']} </prop> <prop key="hibernate.show_sql">#{dataSourceSettings['hibernate.show_sql']}</prop> <prop key="hibernate.format_sql">#{dataSourceSettings['hibernate.format_sql']}</prop> <prop key="hibernate.use_sql_comments">#{dataSourceSettings['hibernate.use_sql_comments']} </prop> </props> </property> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <context:annotation-config /> <jpa:repositories base-package="com.dating.repository"/>