【问题标题】:Java 303 / 349 start date before end date validation结束日期验证之前的 Java 303 / 349 开始日期
【发布时间】:2015-11-23 22:29:10
【问题描述】:

有没有更好的方法来编写 Java 验证器,以确保开始日期早于结束日期,而不是按以下方式编写类级别的 ConstraintValidator:

// VALIDATOR IMPLEMENTATION

    public class StartBeforeEndDateValidator implements ConstraintValidator<StartBeforeEndDateValid, Object> {

        // cannot use LocalDate here...
        private String start;
        private String end;

        @Override
        public void initialize(final StartBeforeEndDateValid annotation) {
            start = annotation.start();
            end = annotation.end();

        }

        @Override
        public boolean isValid(final Object bean, final ConstraintValidatorContext context) {
            try {
                final String startDateStr = BeanUtils.getProperty(bean, start);
                final String endDateStr = BeanUtils.getProperty(bean, end);

                final LocalDate startDate = new LocalDate(startDateStr);
                final LocalDate endDate = new LocalDate(endDateStr);

                return !startDate.isAfter(endDate);
            } catch (final Exception e) {
                return false;
            }
        }
    }


// USAGE
    @StartBeforeEndDateValid(start = "startDate", end = "endDate")
    @Entity
    public class MyBean {

        @NotNull
        @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
        private LocalDate startDate;

        @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
        private LocalDate endDate;

        ... 
    }

我不太喜欢我必须使用反射从 bean 中提取 2 个日期对象的事实。不幸的是,验证规范没有指定一种方法来仅设置您想要从 bean 验证的值。

【问题讨论】:

    标签: java validation bean-validation


    【解决方案1】:

    一种方法是将接口添加到MyBean

    public interface StartEndDateable {
       public LocalDate getStartDate();
       public LocalDate getEndDate();
    }
    
    public class MyBean implements StartEndDatable {
    ...
    

    然后您可以将ConstraintValidator 上的泛型类型设置为新接口而不是Object。

    public class StartBeforeEndDateValidator implements ConstraintValidator<StartBeforeEndDateValid, StartEndDatable> {
    
        @Override
        public void initialize(StartBeforeEndDateValid annotation) {
        }
    
        @Override
        public boolean isValid(StartEndDatable bean, ConstraintValidatorContext context) {
            final LocalDate startDate = bean.getStartDate();
            final LocalDate endDate = bean.getEndDate();
    
            return !startDate.isAfter(endDate);
        }
    }
    

    显然,任何你想用开始和结束日期验证的类都必须实现StartEndDateable(不是我知道的最好的名字,我相信你可以想出更好的名字)并定义getStartDategetEndDate 方法。

    【讨论】:

    • 嗨乔治,我曾考虑添加一个 StartEndValidatable 接口,但它似乎不是最干净的选择 - 如果我有很多交叉参数验证要做,我最终会实现数十种不同的仅用于验证的接口,它们会污染我的 bean 实际尝试做的事情的本质。我更希望能够将 2 个日期传递给 isValid 方法,但鉴于当前的规范,我认为这是不可能的。如果我没有收到任何类似的内容,我会接受您的回答作为最佳选择。谢谢。
    • 另一种选择是使用创建 MyBeanStartEndValidatable 的工厂,该工厂环绕 MyBean,这样您就不会触及 MyBean 的实现。这确实意味着每个不同的接口都会有很多单独的类,但我认为这是一个很小的代价。就我个人而言,我会使用显式类型而不是使用反射,因为以后的任何重构都会有点麻烦,会破坏你的代码,并且很难找到并修复它。
    • 您始终可以为 MyBean 编写特定的 ConstraintValidator。如果您只需要少量对象的 StartBeforeEndDateValid,这可能是最简单的并且可以避免反射。如果您使用通用解决方案,则需要使用反射。另一种选择可能是特定于 Hibernate Validator 的 @ScriptAssert。
    • 嗨,Hardy,StartBeforeEndDate 验证器需要是通用的,因为我们有许多需要这样做的实体。我真的不想使用 @ScriptAssert 标签进入 JavaScript,因为我已经有了这个,我更喜欢基于每个 bean:@AssertTrue(message = "start must be before end") public boolean isDatesValid() { return !startDate.isAfter(endDate); } 我将接受 George 关于接口的回答,因为我认为它是目前最好的解决方案。谢谢你们。
    猜你喜欢
    • 2022-01-06
    • 2018-12-31
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    • 2018-01-31
    • 2012-08-31
    相关资源
    最近更新 更多