2012-07-15 23 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

相關問題