本文介绍了@Autowired bean在控制器上与@Valid一起使用,但对于CRUD存储库失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用带有用户注册表的Spring MVC + Hibernate + JPA应用程序工作,我决定使用JSR-303验证程序来检查数据库中是否已存在用户名:

I'm working on a Spring MVC + Hibernate + JPA app with a user registration form and I decided to use a JSR-303 validator to check whether the username already existed in the DB:

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;        
    }
}

这非常简单,验证在我的控制器上效果很好:

It is very straightforward and validation worked great on my controller:

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

我目前面临的问题是,在我验证了User对象并致电后:

The current problem I'm facing is that after I get my User object validated and I call:

userService.save(user);

实现了CrudRepository的我得到了NullPointerException.由于某些原因,UserService是在控制器验证期间注入的,但在我调用CrudRepository.save()时是不是的.

Which implements CrudRepository, I get a NullPointerException. For some reason UserService is injected during validation on the controller but not when I call CrudRepository.save().

我看到了类似的帖子,例如: @ConstraintValidator中的Autowired bean null,由Sessionfactory.getCurrentSession调用时.merge 还有这个:休眠验证器,无需使用自动装配但我想知道以前是否有人遇到过这种情况.我认为注入bean来访问验证器上的数据库是相当普遍的.

I saw similar posts such as this:@Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.mergeand this:hibernate validator without using autowirebut I was wondering if someone has run into this before. I would think that injecting beans to access the database on a validator is fairly common.

作为一种解决方法,我在userService上添加了一个null检查,但感觉不正确.

As a workaround I added a check for null on userService but it doesn't feel right.

  1. 这是预期的行为吗?是否可以在调用CrudRepository.save()之前触发这些验证?
  2. 我应该处理手动"休眠事件吗?在这种情况下pre-insert
  1. Is this expected behaviour? Are these validations supossed to fire before calling CrudRepository.save()?
  2. Am I suppossed to handle "manually" hibernate events? In this case pre-insert

推荐答案

我最终通过指示Spring的EntityManagerFactoryBean使用我的验证器bean(更准确地说,休眠将使用Spring的验证器)解决了这个问题:

I ended up solving this issue by instructing Spring's EntityManagerFactoryBean to use my validator bean (more accurately, hibernate will now be using Spring's 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错误:)

However, this threw a StackOverflow error :)

显然,此问题的原因是我的验证器正在使用finder方法(findByUsername),并且finder方法触发了休眠刷新,这又触发了验证.这将无限循环,直到您得到最著名的异常为止.

Apparently the cause of this issue was that my validator was using a finder method (findByUsername) and finder methods trigger a hibernate flush, which in turn triggers a validation. This loops indefinitely until you get the most famous exception there is.

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

So...I fixed this by changing the validators to use the EntityManager directly (instead of the CRUD repository) and temporarily change the FlushModeType to COMMIT. Here is the example:

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);
           }    
    }
}

这解决了验证器使用查找程序功能触发休眠刷新的问题,而休眠刷新又触发了验证器,从而导致StackOverflowError.

This solves the issue where the validator was using the finder function which triggered a hibernate flush which in turn was triggering the validator causing a StackOverflowError.

这篇关于@Autowired bean在控制器上与@Valid一起使用,但对于CRUD存储库失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-25 00:25