javax.validation.ValidationException:HV000064:无法实例化ConstraintValidator

问题描述 投票:0回答:5

我关注了这篇博客帖子并尝试实现自定义验证器来验证复合主键约束,但失败了:

javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: com.directory.domain.model.validators.StorePoolValidator.
    at com.directory.domain.repositories.StorePoolRepositoryTest.shouldReturnPoolByStore(StorePoolRepositoryTest.java:30)
Caused by: java.lang.NoSuchMethodException: com.directory.domain.model.validators.StorePoolValidator.<init>()
    at com.directory.domain.repositories.StorePoolRepositoryTest.shouldReturnPoolByStore(StorePoolRepositoryTest.java:30)

这里是验证器的注解接口代码:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
@Target({FIELD})
@Constraint(validatedBy = StorePoolValidator.class)
public @interface UniqueStorePoolConstraint {
    String message() default "Store pool validation failed";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

这是验证器类:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;

public class StorePoolValidator implements ConstraintValidator<UniqueStorePoolConstraint, StorePoolId> {

    private StorePoolRepository storePoolRepository;

    public StorePoolValidator(StorePoolRepository storePoolRepository) {
        this.storePoolRepository = storePoolRepository;
    }

    @Override
    public void initialize(UniqueStorePoolConstraint constraintAnnotation) {

    }

    @Override
    public boolean isValid(StorePoolId id, ConstraintValidatorContext context) {
        final List<StorePool> storePools = storePoolRepository.findAllByStoreNumber(id.getThirdNumber());

        return storePools.isEmpty();
    }
}

这是实体类:

@Entity
@Getter
@Setter
@Builder
@AllArgsConstructor
@Table(name = "STORE_POOLS")
public class StorePool implements Serializable {

    public StorePool() {
    }

    @EmbeddedId
    @UniqueStorePoolConstraint
    private StorePoolId id;
}

及其主键类:

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

@Getter
@Setter
@Builder
@ToString
@Embeddable
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class StorePoolId implements Serializable {

    @Column(name = "pool", nullable = false)
    @NotNull
    private Integer pool;

    @Column(name = "third_number", nullable = false)
    @NotNull
    private Integer thirdNumber;
}

我尝试运行以下测试:

@RunWith(SpringRunner.class)
@DataJpaTest
public class StorePoolRepositoryTest {

    @Autowired
    private StorePoolRepository storePoolRepository;

    @Autowired
    private TestEntityManager entityManager;

    @Test
    public void shouldReturnPoolByStore() {
        final StorePool storePool = StorePoolBuilder.buildStorePool(1, 1);
        entityManager.persist(storePool);
        entityManager.flush();

        final List<StorePool> storePools = storePoolRepository.findAllByStoreNumber(1);

        assertThat(storePools).containsExactly(storePool);
    }
}

我错过了什么?谢谢你。

spring-boot
5个回答
5
投票

抱歉迟到了。

首先创建一个ContextProvider,它是您需要的bean的提供者。

@Component
public class ContextProvider implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ContextProvider.applicationContext = applicationContext;
    }

    public static Object getBean(Class cls) {
        return ContextProvider.applicationContext.getBean(cls);
    }

}

现在你可以这样吃豆子了:

ContextProvider.getBean(StorePoolRepository.class);

这解决了 NPE,您可以使用任何组件进行验证。

在您的情况下StorePoolValidator

@Service
public class StorePoolValidator implements ConstraintValidator<UniqueStorePoolConstraint, StorePoolId> {

    private StorePoolRepository storePoolRepository;

    @Override
    public void initialize(UniqueStorePoolConstraint constraintAnnotation) {
         this.storePoolRepository = (StorePoolRepository) ContextProvider.getBean(StorePoolRepository.class) 
    }

    @Override
    public boolean isValid(StorePoolId id, ConstraintValidatorContext context) {
        final List<StorePool> storePools = storePoolRepository.findAllByStoreNumber(id.getThirdNumber());

        return storePools.isEmpty();
    }
}

就是这样。


3
投票

我遇到了基本上相同的问题,但是通过模拟测试,这是我解决它的方法:

  1. 这是我的自定义约束验证的样子:

    接口:

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = UniqueUsernameValidator.class)
    public @interface UniqueUsername {
    
     String message() default "This username is in use";
    
     Class<?>[] groups() default {};
    
     Class<? extends Payload>[] payload() default {};
    
    }
    

    实施:

     @RequiredArgsConstructor
     public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
    
         private final UserRepository repository;
    
         @Override
         public boolean isValid(String username, ConstraintValidatorContext constraintValidatorContext) {
             return !repository.existsById(username);
         }
    }
    
  2. @SpringBootTest

    注释你的测试类
  3. 通过Spring配置设置MockMvc:

    @ExtendWith(MockitoExtension.class)
    @SpringBootTest
    public class RegistrationTest {
    
        @Autowired
        private WebApplicationContext wac;
    
        private MockMvc mockMvc;
    
        @BeforeEach
        void setUp() {
            this.mockMvc = MockMvcBuilders
                    .webAppContextSetup(wac)
                    .alwaysDo(print())
                    .build();
        }
    
        // your tests
    
    }
    

2
投票

需要添加一个默认为空的构造函数

StorePoolValidator
才能初始化验证器。

public StorePoolValidator() {
}

更新

为了使用存储库,您可以将验证器添加为服务。那么答案是这样的:

@Service
public class StorePoolValidator implements ConstraintValidator<UniqueStorePoolConstraint, StorePoolId> {

    @Autowired
    private StorePoolRepository storePoolRepository;

    @Override
    public void initialize(UniqueStorePoolConstraint constraintAnnotation) {

    }

    @Override
    public boolean isValid(StorePoolId id, ConstraintValidatorContext context) {
        final List<StorePool> storePools = storePoolRepository.findAllByStoreNumber(id.getThirdNumber());

        return storePools.isEmpty();
    }
}

1
投票

Spring 框架自动检测所有实现

ConstraintValidator
接口的类。框架实例化它们并连接所有依赖项,就像该类是常规 Spring bean 一样。所以你不需要添加
@Service
@Autowired
注释。 更多详情请点击这里。


0
投票

我收到了同样的错误,解决方案是在 application.properties 中禁用 Hibernate 验证: spring.jpa.properties.javax.persistence.validation.mode=none

© www.soinside.com 2019 - 2024. All rights reserved.