8

ユーザー登録フォームを使用して Spring MVC + Hibernate + JPA アプリに取り組んでおり、JSR-303 バリデーターを使用して、ユーザー名が 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;        
    }
}

それは非常に簡単で、検証は私のコントローラーでうまくいきました:

....
    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 および this: hibernate validator without autowire が、誰かが以前にこれに遭遇したかどうか疑問に思っていました。バリデーターでデータベースにアクセスするために Bean を注入することはかなり一般的だと思います。

回避策として、null のチェックを追加しましたuserServiceが、適切ではありません。

  1. これは予想される動作ですか?これらの検証は、呼び出す前に起動することが想定されていCrudRepository.save()ますか?
  2. 「手動で」休止状態のイベントを処理することになっていますか? この場合pre-insert
4

2 に答える 2

3

Spring にバリデーター Bean を使用するように指示することで、この問題を解決することになりましたEntityManagerFactoryBean(より正確には、hibernate は 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 エラーがスローされました:)

どうやらこの問題の原因は、バリデーターがファインダー メソッド ( findByUsername) を使用していて、ファインダー メソッドが休止状態のフラッシュをトリガーし、それが検証をトリガーしたことでした。これは、最も有名な例外が発生するまで無期限にループします。

だから...バリデーターを変更して(CRUDリポジトリの代わりに)EntityManagerを直接使用し、一時的に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 を引き起こした問題が解決されます。

于 2012-12-17T23:53:01.717 に答える
2

save メソッドに応答して検証ロジックが呼び出されると、休止状態によって実行されます。バリデータ オブジェクトは休止状態によって作成されるため、スプリング @AutoWired は機能しません。

これを修正する 1 つのオプションは、 @Configurable アノテーションを使用し、ロード時のウィービングを有効にして、hibernate がインスタンス化した場合でも、バリデータ オブジェクトのスプリングが依存関係を注入できるようにすることです。

于 2012-07-15T17:14:00.823 に答える