Spring Boot - Hibernate自定义约束不会注入Service

时间:2018-05-07 10:32:20

标签: spring spring-boot hibernate-validator

我会尽量忽略其他细节并简短说明:

@Entity
public class User
    @UniqueEmail
    @Column(unique = true)
    private String email;
}

@Component
public class UniqueEmailValidatior implements ConstraintValidator<UniqueEmail,String>, InitializingBean {

    @Autowired private UserService userService;

    @Override
    public void initialize(UniqueEmail constraintAnnotation) {

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(userService == null) throw new IllegalStateException();
        if(value == null) return false;
        return !userService.isEmailExisted(value);
    }

}

当在Spring(Spring MVC @Valid中进行验证或使用Validator注入@Autowire)时,这将起作用,一切都会好的。 但是只要我使用Spring Data JPA保存实体:

User save = userRepository.save(newUser);

Hibernate将尝试在不注入UniqueEmailValidatior bean的情况下实例化新的UserService。 那么如何让Hibernate使用我的UniqueEmailValidatior组件而不实例化新组件。 我可以使用spring.jpa.properties.javax.persistence.validation.mode=none禁用hibernate验证,但我希望还有另一种方式

更新:这是我的UserService:

   @Autowired private Validator validator;
   @Transactional
    public SimpleUserDTO newUser(UserRegisterDTO user) {
        validator.validate(user);
        System.out.println("This passes");
        User newUser = new User(user.getUsername(),
                passwordEncoder.encode(user.getPassword()),user.getEmail(),
                "USER",
                user.getAvatar());
        User save = userRepository.save(newUser);
        System.out.println("This won't pass");
        return ....
    }

3 个答案:

答案 0 :(得分:4)

我希望Spring Boot会将现有的验证器连接到EntityManager,显然它不会。

您可以使用HibernatePropertiesCustomizer并向现有EntityManagerFactoryBuilder添加属性并注册Validator

注意:我在这里假设你使用的是Spring Boot 2.0

@Component
public class ValidatorAddingCustomizer implements HibernatePropertiesCustomizer {

    private final ObjectProvider<javax.validation.Validator> provider;

    public ValidatorAddingCustomizer(ObjectProvider<javax.validation.Validator> provider) {
        this.provider=provider;
    }

    public void customize(Map<String, Object> hibernateProperties) {
        Validator validator = provider.getIfUnique();
        if (validator != null) {
            hibernateProperties.put("javax.persistence.validation.factory", validator);
        }
    }
}

这样的事情应该用hibernate连接现有的验证器,并且它将利用自动布线。

注意:在返回@Component的实例之前,您无需在验证程序上使用Validator将自动装配构建到验证程序工厂中。

答案 1 :(得分:1)

要将Spring bean注入ConstraintValidator,您需要一个特定的ConstraintValidatorFactory,应该在ValidatorFactory的初始化时传递。

有些事情:

ValidatorFactory validatorFactory = Validation.byDefaultProvider()
    .configure()
    .constraintValidatorFactory( new MySpringAwareConstraintValidatorFactory( mySpringContext ) )
    .build();

MySpringAwareConstraintValidatorFactoryConstraintValidatorFactory,会将{bean}注入ConstraintValidator

我怀疑Spring Data使用的ValidatorFactory在创建时没有注入验证器,这是不幸的。

我想你应该能够覆盖它。或者更好的是,您应该针对Spring Boot / Spring Data打开一个问题,以便他们能够正确地注入ConstraintValidator,因为我们连续第二次在SO上提出这个问题。

答案 2 :(得分:0)

这里的答案非常大。请检查S.O中的这篇文章以帮助您。这应该可以帮助您入门。

Test Custom Validator with Autowired spring Service

问题是hibernate无法知道spring定义。但是,您可以让实体了解任何类型的javax.validation类型。希望这会有所帮助。