java - @Autowired bean 在 Controller 上与@Valid 一起工作,但在 CRUD 存储库上失败

标签 java spring spring-mvc

我正在开发一个带有用户注册表单的 Spring MVC + Hibernate + JPA 应用程序,我决定使用 JSR-303 validator 来检查用户名是否已存在于数据库中:

public class UniqueUsernameValidator implements ConstraintValidator<VerifyUniqueUsername, String> {

    @Autowired
    UserService userService;

    @Override
    public void initialize(VerifyUniqueUsername constraintAnnotation) {     
    }

    @Override
    public boolean isValid(String username, ConstraintValidatorContext context) {                       

        return  username!=null &&  userService.findByUsername(username) == null;        
    }
}

它非常简单,并且在我的 Controller 上验证效果很好:

....
    public String signup(@Valid @ModelAttribute("newUser") User user, BindingResult newUserBeanResult)
.....

我目前面临的问题是,在我验证了我的 User 对象后,我调用了:

userService.save(user);

它实现了 CrudRepository,我得到了一个 NullPointerException。出于某种原因,UserService 在 Controller 验证期间被注入(inject),但当我调用 CrudRepository.save()没有

我看过类似的帖子,例如: @Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.merge 还有这个: hibernate validator without using autowire 但我想知道以前是否有人遇到过这个问题。我认为注入(inject) bean 以访问 validator 上的数据库是相当普遍的。

作为一种解决方法,我在 userService 上添加了 null 检查,但感觉不对。

  1. 这是预期的行为吗?这些验证是否应该在调用 CrudRepository.save() 之前触发?
  2. 我应该“手动”处理 hibernate 事件吗?在这种情况下预插入

最佳答案

我最终通过指示 Spring 的 EntityManagerFactoryBean 使用我的 validator bean 解决了这个问题(更准确地说,hibernate 现在将使用 Spring 的 validator ):

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>        
        <property name="packagesToScan" value="some.packages"/>
        <property name="jpaPropertyMap">
            <map>
                <entry key="javax.persistence.validation.factory" value-ref="validator"  />           
            </map>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.show_sql">true</prop>              
            </props>        
        </property>
    </bean>  

但是,这引发了 StackOverflow 错误 :)

显然,造成此问题的原因是我的 validator 使用了查找器方法 (findByUsername) 并且查找器方法触发了 hibernate 刷新,这反过来又触发了验证。这无限循环,直到你得到最著名的异常。

所以...我通过将 validator 更改为直接使用 EntityManager(而不是 CRUD 存储库)并暂时将 FlushModeType 更改为 COMMIT 来解决此问题。这是示例:

public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {

    @PersistenceContext
    private EntityManager em;

    @Autowired
    UserService userService;

    @Override
    public void initialize(UniqueUsername constraintAnnotation) {       
    }

    @Override
    public boolean isValid(String username, ConstraintValidatorContext context) {
        try { 
            em.setFlushMode(FlushModeType.COMMIT);          
            return userService.findByUsername(username) == null;

            } finally { 
           em.setFlushMode(FlushModeType.AUTO);
           }    
    }
}

这解决了 validator 使用 finder 函数触发 hibernate 刷新的问题,该刷新又触发 validator 导致 StackOverflowError。

关于java - @Autowired bean 在 Controller 上与@Valid 一起工作,但在 CRUD 存储库上失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11490075/

相关文章:

java - 线段之间的相交问题

spring - 如何将本地Spring Cloud任务项目jar文件注册到Spring Cloud数据流仪表板

java - 查看 Spring MVC 中的逻辑

java - Tomcat 或 Jetty 上的 Spring Web 应用程序 Controller 内基于事件的编程

spring - 当作为 spring bean 的 SubClass 调用也是 spring bean 的 SuperClass 的方法时,SuperClass 中的 autowired 字段为 null

java - AbstractSecurityWebApplicationInitializer 与 AbstractAnnotationConfigDispatcherServletInitializer

java - 使用 Spring MVC 3.0 执行内联行编辑的最便捷方法

java - 什么时候在 X509KeyManager 中调用 getPrivateKey?

java - getServletContext().getRealPath() 在 Controller (NPE)中不起作用,但在 jsp 中起作用

java - 从 Java 中的扫描仪中读取 double 无法正常工作