我正在使用Spring MVC创建一个网站,并且为了持久性,我将Spring Data JPA与Hibernate 4用作我的JPA提供程序。目前正在使用Hibernate Validator处理验证。我有一个问题,我的验证器被两次调用,我不知道为什么。这是一个问题的主要原因是因为在第二轮中,依赖关系没有自动装配到验证器中,并且我收到了空指针异常。以下是导致失败的调用顺序:
非常感谢
这是我的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 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; } }
我的注册控制器中的相关方法:
@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(); }
我的服务等级:
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)); } }
我的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; } }
我的uniqueUsername验证器:
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; } }
我的用户资料库:
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); }
最后是我的persistence-context.xml文件
<!-- Data source properties --> <util:properties id="dataSourceSettings" location="classpath:datasource.properties" /> <!-- Pooled data source using BoneCP --> <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> <!-- JPA entity manager factory 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"/>
当你将bean发送到数据存储区时,也许第二次验证是由hibernate完成的。要关闭它,请将其添加到你的persistence.xml中:
<property name="javax.persistence.validation.mode" value="none"/>
https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/configuration.html说:
默认情况下,Bean验证(和hibernate验证器)被激活。创建,更新(和可选地删除)实体后,在将其发送到数据库之前先对其进行验证。Hibernate生成的数据库模式还反映了在实体上声明的约束。
你可以根据需要进行微调:
自动:如果在类路径中存在Bean验证,则激活CALLBACK和DDL。
回调:实体在创建,更新和删除时均经过验证。如果不存在Bean验证提供程序,则在初始化时引发异常。
DDL :(不是标准的,请参见下文)数据库架构是在创建,更新和删除时经过验证的实体。如果不存在Bean验证提供程序,则在初始化时引发异常。
无:完全不使用Bean验证