我正在使用Spring MVC创建一个网站,并且为了持久性我使用Spring Data JPA和Hibernate 4作为我的JPA提供程序。目前正在使用Hibernate Validator处理验证。我有一个问题,我的验证器被调用两次,我无法弄清楚为什么。这是一个问题的主要原因是因为第二轮,依赖关系没有自动连接到验证器,我得到一个空指针异常。 以下是导致失败的呼叫序列:
任何人都可以阐明验证器被调用两次的原因,特别是为什么要跨越'userRepository.save(user);'回到这些验证器?
非常感谢
这是我的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;
}
}
我的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);
}
最后是我的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"/>
答案 0 :(得分:7)
当您将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 Validation(和Hibernate Validator)已激活。创建,更新(并可选地删除)实体时,会在发送到数据库之前对其进行验证。 Hibernate生成的数据库模式也反映了在实体上声明的约束。
如果需要,您可以对其进行微调:
AUTO:如果类路径中存在Bean Validation,则激活CALLBACK和DDL。
CALLBACK:在创建,更新和删除时验证实体。如果不存在Bean Validation提供程序,则会在初始化时引发异常。
DDL :(非标准,见下文)数据库模式是在创建,更新和删除时验证实体。如果不存在Bean Validation提供程序,则会在初始化时引发异常。
NONE:根本不使用Bean验证
第一个显然是由Spring控制器完成的,因为@Valid注释。
答案 1 :(得分:0)
您只需将此属性添加到application.property文件中即可禁用休眠验证spring.jpa.properties.javax.persistence.validation.mode=none