我紧跟着this blog的帖子,尝试实现一个自定义验证器来验证复合主键约束,但失败并显示:
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);
}
}
我想念什么?谢谢。
答案 0 :(得分:3)
抱歉在讨论中迟到了。
首先创建一个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);
}
}
现在你可以像这样拥有 bean:
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();
}
}
就是这样。
答案 1 :(得分:0)
默认情况下,StorePoolValidator
需要添加一个构造函数为空才能初始化验证器。
public StorePoolValidator() {
}
更新
为了使用存储库,您可以将Validator作为服务添加。那么答案是这样的:
@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();
}
}
答案 2 :(得分:0)
Spring框架自动检测实现ConstraintValidator
接口的所有类。该框架实例化它们并连接所有依赖项,就像该类是常规的Spring bean。因此,您无需放置@Service
和@Autowired
批注。
More details here.