JSR303 自定义验证器被调用两次

2024-03-30

我正在使用 Spring MVC 创建一个网站,为了持久性,我使用 Spring Data JPA 和 Hibernate 4 作为我的 JPA 提供程序。目前正在使用 Hibernate Validator 处理验证。我遇到一个问题,我的验证器被调用两次,但我不明白为什么。这是一个问题的主要原因是因为第二次,依赖项没有被自动连接到验证器中,并且我收到了空指针异常。 以下是导致失败的调用顺序:

  1. 提交注册表单后,首先调用 NotDefaultSectValidator,并针对用户对象上的“whereDidYouHearAboutUs”字段成功完成。
  2. 接下来调用 UniqueUsernameValidator 并成功完成“用户名”字段验证。
  3. 控制器上的 'addUserFromForm' 方法启动,并在 bindingResults 对象中发现没有错误。
  4. 然后在 UserService 类上调用“addUser”方法。此方法到达“userRepository.save(user);”行但之后绝不会立即运行“print.ln”行。越过此行需要返回到“NotDefaultSectValidator”断点。这是第二次完成,我重新输入第二个验证器“UniqueUsernameValidator”。这里我得到一个空指针异常,因为由于某种原因 Spring 第二次未能在 DAO 中自动装配。

任何人都可以阐明为什么验证器被调用两次,特别是为什么要跨行“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;
    }

}

我的唯一用户名验证器:

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 https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/configuration.html says:

默认情况下,Bean Validation(和 Hibernate Validator)被激活。创建、更新(以及选择性删除)实体时,会在发送到数据库之前对其进行验证。 Hibernate 生成的数据库模式也反映了在实体上声明的约束。

如果需要,您可以对其进行微调:

AUTO:如果类路径中存在 Bean Validation,则激活 CALLBACK 和 DDL。

CALLBACK:实体在创建、更新和删除时进行验证。如果不存在 Bean Validation 提供程序,则在初始化时会引发异常。

DDL:(非标准,见下文)数据库模式是在创建、更新和删除时验证的实体。如果不存在 Bean Validation 提供程序,则在初始化时会引发异常。

NONE:根本不使用 Bean 验证

由于 @Valid 注释,第一个显然是由 Spring 控制器完成的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JSR303 自定义验证器被调用两次 的相关文章

随机推荐

  • java中2组最接近的和

    我在解决这个问题时遇到一些问题 给定一个数组ints 将输入分为 2 组 使其总和尽可能接近 2 组的长度必须相等 或者如果输入是奇数长度 则一组可以比另一组多 1 然后先打印较低的总和 然后打印较高的总和 前任 输入 gt 4 6 17
  • 从序列化的 post_meta (PHP) 中的数组中过滤一些值

    我在 WordPress 中设置了自定义帖子类型 艺术家 我有一个插件可以序列化来自艺术家的元信息 并且我需要按通道过滤此信息 channel s 6 trance 它也在序列化元 show data 中 a 1 s 32 57fba1b1
  • 在c#中比较RGB颜色

    我正在尝试找到一种方法来比较两种颜色以找出它们的相似程度 我似乎找不到有关该主题的任何资源 因此我希望在这里得到一些指示 理想情况下 我希望得到一个分数来表明它们有多相似 例如 0 到 100 其中 100 相等 0 完全不同 Thanks
  • BeautifulSoup:从锚标记中提取文本

    我想提取 来自以下 src 的文本image tag and 锚标记的文本位于div类数据 我成功地提取了 img src 但在从锚标记中提取文本时遇到了问题 a class title href http www amazon com N
  • 上传文件限制

    如何限制上传文件 例如 如果数据库已有 5 个条目 则不应采用第 6 个条目 并展示您只能拥有 5 个文件 我的代码
  • 如何在 MATLAB 中创建用于播放、暂停、快进和快退视频的 GUI?

    我是 MATLAB 新手 我正在尝试创建一个 GUI 来逐帧播放 暂停 快进和快退 avi 视频 目前 我可以通过切换按钮播放和暂停视频 但是当我再次按下播放时 视频从零帧开始播放 我意识到我需要存储下次按播放时使用的帧号 但我不知道该怎么
  • 为什么我会收到 InvalidDnDOperationException?

    I m sorry I don t like asking why am I getting exception questions on StackOverflow bit ironic now that I think of it bu
  • 存储指向堆栈值的指针(Golang)

    我正在尝试用 Go 进行实验 看看如果我将指向变量的指针存储在堆栈上 然后在原始变量离开作用域后访问该变量 会发生什么情况 package main import fmt var p chan bool a temp struct type
  • 错误:GDK_BACKEND 与可用显示器不匹配;使用 Crontab 运行 Selenium

    我正在尝试使用 cron 运行 selenium import os from selenium import webdriver from selenium webdriver firefox options import Options
  • 发送 HTML 电子邮件 asp

    我想在电子邮件中添加一些 html 我已经尝试过以下方法 vFromName someone vFromAddress someemail vTo recipient vSubject someSubject vBodyofemail ta
  • Client = Discord.client() TypeError: 'module' 对象不可调用

    为什么我会得到TypeError module object is not callable用我的代码 import discord from discord ext commands import Bot from discord ext
  • Google Cloud Endpoints:verifyToken:签名长度不正确

    今天早上 从我的 Android 应用程序向我的 Google Cloud Endpoint 发出的每个 API 请求都开始出现以下异常 com google api server spi auth GoogleIdTokenUtils v
  • 测量任务之间的响应时间

    我正在编写一个程序 用Python 它返回一些数据 我想知道如何测量请求和答案之间的响应时间 用于性能分析 然后我会将其存储在某个地方 有一种更好更有效的方法来做到这一点 或者只是插入例如time ctime 在请求和另一个请求之前time
  • 使用 TPL 时如何管理线程本地存储 (TLS)?

    我想将日志记录上下文信息存储在 TLS 中 以便可以在入口点设置一个值 并使该值在所有结果堆栈中可用 这工作得很好 但我还使用了 TPL 和 ThreadPool 那么问题就变成了如何将 TLS 数据迁移到其他线程 我可以自己完成这一切 但
  • 如何为具有大图像目录的博客设置 Jekyll,以避免在生成的站点中重复该目录?

    我正在考虑使用 Jekyll 构建一个网站 该网站将成为一个包含大量图像 以及其他大型媒体文件 的博客 创建图像目录 然后根据帖子中的需要链接到它们是很容易的 但是 据我了解 生成站点时 所有图像数据将被复制到保存静态文件的生成的 site
  • 如何在 IntelliJ IDEA 中搜索特定代码块?

    我如何搜索withinIntelliJ IDEA 中的特定代码块或选择 I got used to using this feature in Eclipse In Eclipse you can just double click on
  • 为什么 ViewModelProvider 在屏幕旋转时创建视图模型的新实例?

    我试图实现分页 但每次旋转视图模型的屏幕构造函数都会被调用 从而触发 loadInitial 从我的 DataSource 类中的网络获取新数据 感谢帮助 ViewModel def lifecycle version 2 2 0 impl
  • 有关新 Windows 10 错误的信息:ERROR_CLOUD_FILE_ACCESS_DENIED

    打开文件进行读取时遇到新的 Windows 10 错误代码CreateFile 我们得到错误395 但关于其含义或如何解决的信息很少 Windows 10 SDK的错误详细信息如下 错误编号395 误差常数ERROR CLOUD FILE
  • 获取中位数对应的索引

    我有一个带有一列的 pandas 数据框 我想知道中位数的索引 也就是说 我这样确定中位数 df 中位数 这给了我中值 但我想知道该行的索引 这个可以确定吗 对于长度不均匀的列表 我可以搜索具有该值的索引 但对于均匀的列表长度 这是行不通的
  • JSR303 自定义验证器被调用两次

    我正在使用 Spring MVC 创建一个网站 为了持久性 我使用 Spring Data JPA 和 Hibernate 4 作为我的 JPA 提供程序 目前正在使用 Hibernate Validator 处理验证 我遇到一个问题 我的