Hibernate Validator in Spring Boot using different ConstraintValidatorManager

喜夏-厌秋 提交于 2020-06-28 08:26:23

问题


I am facing an issue that has been mentioned before with Spring Boot vs. Hibernate Validation, where autowiring of dependencies inside custom Constraint Validators is not working. From my own debugging, I have noticed that when entity-level validation occurs, Hibernate loads a different ConstraintValidatorManager compared to when Hibernate is performing bean validation for form submits. The latter works fine, the former leads to dependencies of the custom Constraint Validator being null. It seems as if Hibernate is loading one manager from the root context and one from the servlet context. This would explain Hibernate not having any knowledge of the existence of the dependencies autowired in the custom Constraint Validator. If this is true however, I do not understand what is going on, or how to make Hibernate/JPA aware of the Spring context and it's beans.

I am hoping someone could point me in the right direction? I have tried all of the below answers, and much more (e.g. different library versions, configuration methods, static bean loading through a utils class, etc.):

Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration

Autowired gives Null value in Custom Constraint validator

Also I have been through the Reference guide for Spring Boot specifically several times, without much luck. There are several cases that mention their Hibernate validation working fine, both for regular bean submits, as well as during entity persisting. Unfortunately, I seem unable to retrieve their exact (Java) configuration they used, but it seems they are using default configuration. I am starting to wonder if this is a specific Spring Boot issue (although it is stated a combination of Spring Validation and Hibernate Validation should work out-of-the-box).

Adding anything like below bean does not solve the issue (default factory being SpringConstraintValidatorFactory ofcourse):

@Bean
public LocalValidatorFactoryBean validator()
{
    LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
    bean.setValidationMessageSource(messageSource());
    return bean;
}

Nor does including a bean definition for a Hibernate validator as such:

Autowired gives Null value in Custom Constraint validator

There are many different ways of loading and injecting the desired bean, but if Hibernate is not at all aware of the beans loaded in the context (because it is using a different context?), how to proceed?

Thanks in advance.

UPDATE: Gradle file

buildscript {
    ext {
        springBootVersion = '2.1.5.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = '<hidden>'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-tomcat:2.1.5.RELEASE')

    implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
    implementation('org.springframework.boot:spring-boot-starter-security')
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation('org.springframework.boot:spring-boot-starter-mail')
    implementation('org.springframework.session:spring-session-core')

    annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')

    implementation('org.postgresql:postgresql')

    // https://mvnrepository.com/artifact/org.jboss.aerogear/aerogear-otp-java
    implementation('org.jboss.aerogear:aerogear-otp-java:1.0.0')

    implementation('com.github.mkopylec:recaptcha-spring-boot-starter:2.2.0')
    implementation('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.5')
    implementation('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:3.0.4.RELEASE')

    implementation('javax.enterprise:cdi-api:2.0')

    runtimeOnly('org.springframework.boot:spring-boot-devtools')

    testImplementation('org.springframework.boot:spring-boot-starter-test')
    testImplementation('org.springframework.security:spring-security-test')
    testImplementation 'org.mockito:mockito-core:2.27.0'
}

回答1:


There is a way to tell Hibernate to use the same validator by setting javax.persistence.validation.factory

@Configuration
@Lazy
class SpringValidatorConfiguration {

    @Bean
    @Lazy
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(final Validator validator) {
        return new HibernatePropertiesCustomizer() {

            @Override
            public void customize(Map<String, Object> hibernateProperties) {
                hibernateProperties.put("javax.persistence.validation.factory", validator);
            }
        };
    }
}

That way everything works fine.




回答2:


Regarding the fix, just to summarize a more extensive/integrated answer for others that are/were dealing with these sorts of issues, my configuration now contains all of these beans:

@Bean
public LocalValidatorFactoryBean validator()
{
    LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean();
    validatorFactory.setValidationMessageSource(messageSource());
    return validatorFactory;
}

@Bean
public MessageSource messageSource()
{
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasename("classpath:messages");
    messageSource.setDefaultEncoding("UTF-8");
    return messageSource;
}

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() 
{
    MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
    methodValidationPostProcessor.setValidator(validator());
    return methodValidationPostProcessor;
}

@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer()
{
    return properties ->
    {
        properties.put("javax.persistence.validation.factory", validator());
        // Add more properties here such as validation groups (see comment for SO example)
    };
}

For an example on adding hibernate validation groups to tease apart validation of different life-cycle events (e.g. bean vs. entity), see Hibernate validations on save (insert) only



来源:https://stackoverflow.com/questions/56542699/hibernate-validator-in-spring-boot-using-different-constraintvalidatormanager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!