从jsr-303自定义验证器访问数据库

时间:2011-07-06 11:04:24

标签: java spring validation bean-validation

我在我的应用程序上下文中使用基于弹簧的验证与hibernate验证器结合使用:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        ....
        <property name="jpaPropertyMap">
            <map>
                <entry key="javax.persistence.validation.factory" value-ref="validator" />    
            </map>
        </property>
    </bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

我已经实现了一个自定义验证器,它使用弹簧注入的DAO访问数据库以检查特定对象的有效性约束。这会导致java.lang.StackOverflowError,因为每次从验证器中从数据库加载对象时都会调用验证,从而导致无限循环。为了解决这个问题,我尝试在我的实体管理器上设置刷新模式,使用以下代码在验证器中提交:

entityManager.setFlushMode(FlushModeType.COMMIT);

这导致hibernate的“集合不是由flush()处理”异常。

是否有一个从自定义验证器访问数据库的最佳实践示例,这将允许我解决这两个问题?

2 个答案:

答案 0 :(得分:1)

经过多次实验,看来最好的方法是直接在代码中使用EntityManagerFactory。在验证器类的initialize(...)方法中,我有以下代码:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu_name");
entityManager = emf.createEntityManager(); 

缺点是您没有获得Spring的DI功能,但您仍然可以访问数据库。

答案 1 :(得分:0)

我也遇到过这个问题,这是我解决它的方法: @Autowired bean works with @Valid on controller but fails with CRUD repository

简而言之,我也得到了EntityManagerFactory对象的引用。但是,在调用我的服务方法之前,我将setFlushMode设置为FlushModeType.COMMIT。最后我将其设置回FlushModeType.AUTO

以下是一个例子:

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