2012-07-15 48 views
7

我正在使用用户注册表单处理Spring MVC + Hibernate + JPA应用程序,并决定使用JSR-303验证程序来检查用户名已经存在于DB:@Autowired bean与@Valid在控制器上工作,但与CRUD存储库失败

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

这是非常简单和验证我的控制器上伟大的工作:

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

我面对目前的问题是,我拿到后,我的User对象验证和我请致电:

userService.save(user); 

其中执行CrudRepository,我得到一个NullPointerException。出于某种原因,UserService在控制器验证期间被注入,但当我呼叫CrudRepository.save()时,不是

我看到了类似的帖子,如这样的: @Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.merge 这: hibernate validator without using autowire 但如果有人以前碰到这个我不知道。我认为注入bean来访问验证器上的数据库是相当普遍的。

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

  1. 这是预期的行为?在致电CrudRepository.save()之前,这些验证是否已被触发?
  2. 我支持处理“手动”休眠事件吗?在这种情况下,pre-insert

回答

3

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

<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的错误:)

显然这个问题的原因是我的验证器使用finder方法(findByUsername)和finder方法触发hibernate刷新,这反过来触发验证。这无限循环,直到你得到最着名的例外。

所以...我通过更改验证器直接使用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); 
      }  
    } 
} 

这其中验证使用这引发了休眠冲洗这又是触发验证造成的StackOverflowError取景功能解决了这个问题。

2

当验证逻辑被调用以响应save方法时,它通过休眠来完成。验证器对象由hibernate创建,所以spring @AutoWired将不起作用。

解决此问题的一个选择是使用@Configurable注释并启用加载时编织,以确保即使在hibernate实例化验证器对象时,spring也会向其中注入依赖关系。

+0

感谢您的建议,我对Spring相当陌生我会为@Configurable做一些Google搜索并回复给大家。谢谢。 – Ulises 2012-07-15 21:45:56

+0

我没有太多的运气,如果你能指点我的地方,我可以阅读更多关于这将是伟大的。谢谢! – Ulises 2012-07-15 22:34:51

+0

http://stackoverflow.com/questions/4703206/spring-autowiring-using-configurable - 这个问题有更多的细节如何设置@Configurable。它用于实体 - 同样也适用于验证器。 – gkamal 2012-07-17 16:41:29

相关问题