提问者:小点点

JavaHibernate验证器需要一个或另一个


这是我之前问题的扩展。我实现了Dennis R的答案,并且正在使用hibernate-validator。有没有办法要求在json请求中指定一个或另一个字段,但不能同时指定两个?从我之前的帖子中,在Request类中,我希望用户传入id或代码,但不能同时传入。

我发现这个资源对我来说可能是正确的解决方案,但我不完全理解那里发生了什么,为什么它有效,坦率地说,它看起来太冗长了。这是唯一的方法吗?


共3个答案

匿名用户

正如我之前评论的那样,在这里遵循Nicko的回答,您可以使用以下代码实现您想要的:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
public @interface FieldMatch {

    String message() default "something is wrong!";

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

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

    /**
     * @return The first field
     */
    String first();

    /**
     * @return The second field
     */
    String second();

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        FieldMatch[] value();
    }

    public static class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {

        private String firstFieldName;
        private String secondFieldName;

        @Override
        public void initialize(FieldMatch fieldMatch) {
            firstFieldName = fieldMatch.first();
            secondFieldName = fieldMatch.second();
        }

        public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
            try {
                final Object firstObj = getProperty(object, firstFieldName);
                final Object secondObj = getProperty(object, secondFieldName);

                if(firstObj == null && secondObj == null || firstObj != null && secondObj != null) {
                    return false;
                }
            } catch (final Exception ignore) {
                // ignore
            }
            return true;
        }

        private Object getProperty(Object value, String fieldName) {
            Field[] fields = value.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.getName().equals(fieldName)) {
                    field.setAccessible(true);
                    try {
                        return field.get(value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }
    }

}

用法:

@FieldMatch.List({
        @FieldMatch(first = "name", second = "people"),
        @FieldMatch(first = "age", second = "abc")
})
public class Foo {

    private String name;
    private List<String> people;
    private int age;
    private Boolean abc; 
}

唯一的区别是,您不想检查内容是否相等,只要一个字段为空,另一个字段不为空。

编辑:

要按照注释的要求在ExceptionHandler上获取对象,只需将异常包装在自定义异常周围,并在抛出时传递对象,即:

public class CustomException extends Exception {

    private String message;
    private Object model;

    public CustomException(String message, Object model) {
        super(message);
        this.model = model;
    }

    public Object getModel() {
        return model;
    }
}

有了这个,你可以简单地得到如下结果:

@ExceptionHandler(CustomException.class)
public ModelAndView handleCustomException(CustomException ex) {
    Object obj = ex.getModel();
    //do whatever you have to
}

匿名用户

天哪在我看来,这些链接的参考文献过于复杂。存在注释:

@org.hibernate.annotations.Check

我经常遇到同样的情况,我想执行这种类型的验证,我有一个或另一个字段,或者我有两个或两个都没有。。。

@Entity
@org.hibernate.annotations.Check(constraints = "(field1 IS NULL OR field2 IS NULL) AND (field1 IS NOT NULL OR field2 IS NOT NULL)")
public class MyEntity{
    String field1;
    Double field2;
}

这将在数据库中创建一个检查约束,该约束将强制执行该约束。它将验证从Hibernate和您的代码转移到数据库(这也将防止任何在您的hibernate配置之外访问您的数据库的应用程序破坏此约束)。

此注释的创建不会自动在您的数据库上执行约束的创建,但如果/当您创建约束时,它也会通知hibernate它。

在Postgres中,该约束类似于:ALTER TABLE my\u entity ADD constraint my\u entity\u check check((field1为NULL或field2为NULL)和(field1不为NULL或field2不为NULL));

Postgres检查约束

Oracle检查约束

如果您无法生成精确的SQL,请创建注释,然后允许hibernate针对空数据库自动生成数据库模式,它将向您显示正确的SQL。但是有了注释,hibernate也知道约束,因此如果您允许hibernate为任何自动化测试等生成模式,则可以自动生成...

匿名用户

您可以使用Group作为替代方案。例如,这是请求。java:

public class Request {

public interface IdOrCodeValidationGroup {}

    @NotNull
    @NotEmpty
    private String id;

    @Digits(integer=4, fraction=0)
    private double code;

    @NotNull
    @NotEmpty
    private String name;

    @AssertTrue(groups = IdOrCodeValidationGroup.class)
    private boolean idOrCodeFilled;

    public Request(String id, double code, String name) {
        this.id = id;
        this.code = code;
        this.name = name;
    }

    public boolean isIdOrCodeFilled() {
        if (id == null && code > 0) {
            idOrCodeFilled = true;
        } else if (id != null && code == 0) {
            idOrCodeFilled = true;
        } else idOrCodeFilled = false;
        return idOrCodeFilled;
    }
}

然后像这样使用验证器:

@Test
public void testValidation() {
    // Of course all of this valid. No group at all.
    final Request request = new Request("ID-001", 111, "Data 1");
    final Set<ConstraintViolation<Request>> fails = this.validator.validate(request);
    Assert.assertTrue(fails.isEmpty());
}

@Test
public void testValidationWithGroup() {
    // We use "IdOrCodeValidationGroup.class" group, thus this is invalid.
    Request request = new Request("ID-001", 111, "Data 1");
    Set<ConstraintViolation<Request>> fails = this.validator.validate(request, IdOrCodeValidationGroup.class);
    Assert.assertFalse(fails.isEmpty());

    // Lets make one of constraint true; In this case, we set code = 0.
    request = new Request("ID-002", 0, "Data 2");
    fails = this.validator.validate(request, IdOrCodeValidationGroup.class);
    // Passed!
    Assert.assertFalse(fails.isEmpty()); 
}

下面是功能完整的示例代码。(别忘了结帐“so-36365734”分行)。这是关于Bean验证组的官方文档。

HTH。