【问题标题】:AnyOf-Predicate in ArchUnitArchUnit 中的 AnyOf-Predicate
【发布时间】:2021-06-09 06:05:10
【问题描述】:

我想测试所有 Hibernate 关联注解(@ManyToOne、@OneToMany、@OneToOne、@ManyToMany)都在使用fetch = FetchType.LAZY。这是有效的:

private static final Set<Class<? extends Annotation>> associations =
         Set.of(ManyToOne.class, OneToMany.class, OneToOne.class, ManyToMany.class);

@ArchTest
public static final ArchRule allEntityRelationsShouldBeLazy = fields().that()
      .areDeclaredInClassesThat().areAnnotatedWith(Entity.class)
      .and()
      .areAnnotatedWith(ManyToOne.class)
      .or().areAnnotatedWith(OneToMany.class)
      .or().areAnnotatedWith(OneToOne.class)
      .or().areAnnotatedWith(ManyToMany.class)
      // what I'd like: .areAnnotatedWith(Any.of(associations))
      .should()
      .notBeAnnotatedWith(new DescribedPredicate<>("FetchType.EAGER or undefined FetchType") {
         @Override
         public boolean apply(JavaAnnotation<?> input) {
            JavaClass rawType = input.getRawType();
            if (!rawType.isEquivalentTo(OneToOne.class)
            // what I'd like: if (!Any.of(associations).apply(input)) {
                  && !rawType.isEquivalentTo(OneToMany.class)
                  && !rawType.isEquivalentTo(ManyToOne.class)
                  && !rawType.isEquivalentTo(ManyToMany.class)) {
               // Filter again, because a field can contain multiple annotations.
               return false;
            }
            return input.get("fetch")
                  .transform(JavaEnumConstant.class::cast)
                  .transform(fetchType -> !FetchType.LAZY.name().equals(fetchType.name()))
                  .or(true);
         }
      });

我必须过滤两次,因为一个字段可以有多个注释,如下所示:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "otherEntity", referencedColumnName = "id")
private OtherEntity otherEntity;

我不喜欢的是我必须写两次注释(ManyToOne…)。为什么 ArchUnit 中没有“anyOf”谓词?或者有没有其他方法可以避免重复?

【问题讨论】:

    标签: archunit


    【解决方案1】:

    你想要的可以用Java流操作来构建:

    import static com.tngtech.archunit.base.DescribedPredicate.describe;
    import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
    import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
    
    
    private static final DescribedPredicate<CanBeAnnotated> ALWAYS_FALSE = new DescribedPredicate<CanBeAnnotated>("always false", new Object[0]) {
            @Override
            public boolean apply(CanBeAnnotated input) {
                return false;
            }
        };
    
    @ArchTest
    ArchRule allEntityRelationsShouldBeLazy = fields().that()
        .areDeclaredInClassesThat().areAnnotatedWith(Entity.class)
        .and(are(annotatedWithAnyOf(associations)))
        .should().notBeAnnotatedWith(describe("FetchType.EAGER or undefined FetchType",
            input -> associations.stream().anyMatch(input.getRawType()::isEquivalentTo)
                     && input.get("fetch")
                         .transform(JavaEnumConstant.class::cast)
                         .transform(enumConst -> !FetchType.LAZY.name().equals(enumConst.name()))
                         .get()  // associations have a default fetch type
        ));
        
    DescribedPredicate<CanBeAnnotated> annotatedWithAnyOf(Collection<Class<? extends Annotation>> annotations) {
        return annotations.stream()
                .map(CanBeAnnotated.Predicates::annotatedWith)
                .reduce(DescribedPredicate::or)
                .orElse(ALWAYS_FALSE);  // annotations must not be empty
    }
    

    【讨论】:

      【解决方案2】:

      对实体字段使用自定义条件,可以直接对associations的相关注解进行操作,完全不用重复:

      @ArchTest
      ArchRule allEntityRelationsShouldBeLazy = fields()
          .that().areDeclaredInClassesThat().areAnnotatedWith(Entity.class)
          .should(new ArchCondition<JavaField>("be annotated with FetchType.LAZY if an association") {
              @Override
              public void check(JavaField field, ConditionEvents events) {
                  field.getAnnotations().stream()
                      .filter(annotation -> associations.stream().anyMatch(annotation.getRawType()::isEquivalentTo))
                      .forEach(association -> {
                          JavaEnumConstant fetchType = (JavaEnumConstant) association.get("fetch").get();
                          if (!FetchType.LAZY.name().equals(fetchType.name())) {
                              String message = field.getDescription() + " is not LAZY in " + field.getSourceCodeLocation();
                              events.add(SimpleConditionEvent.violated(field, message));
                          }
                      });
              }
          });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-02
        • 2021-04-04
        • 2021-10-26
        • 1970-01-01
        • 1970-01-01
        • 2022-09-24
        • 2019-03-23
        相关资源
        最近更新 更多