RSQL-Parser和布尔查询值导致IllegalArgumentException错误。

huangapple go评论85阅读模式
英文:

RSQL-Parser and boolean query values failing with IllegalArgumentException

问题

我有一个 REST API,我使用 RSQL 解析器实现了查询功能。它对一切都很有效,但布尔值 "true" 和 "false" 除外。

以下是我 RSQL 规范,根据 Baeldung 的示例稍作修改以处理 API 中的枚举值:

public class GenericRsqlSpecification<T> implements Specification<T> {

  private String property;
  private ComparisonOperator operator;
  private List<String> arguments;

  public GenericRsqlSpecification(String property, ComparisonOperator operator, List<String> arguments) {
    this.property = property;
    this.operator = operator;
    this.arguments = arguments;
  }

  @Override
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
    // ... (代码在此省略)
  }

  // ... (代码在此省略)
}

关于 Car 模型的部分如下:

public class CarDO {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(nullable = false)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
  private ZonedDateTime dateCreated = ZonedDateTime.now();
  @Column(nullable = false)
  private String licensePlate;
  @Column(nullable = false)
  private int seatCount;
  @Column(nullable = false)
  private boolean convertible;
  @Column(nullable = false)
  private int rating;
  @Column(nullable = false)
  @Enumerated(EnumType.STRING)
  private EngineType engineType;
  @Column(nullable = false)
  private String manufacturer;
  @Column(nullable = false)
  private boolean selected;
  @OneToOne(fetch = FetchType.EAGER)
  @JoinTable(name = "driver_with_car",
    joinColumns = {@JoinColumn(name = "car_id")},
    inverseJoinColumns = {@JoinColumn(name = "driver_id")})
  private DriverDO driver;
  //Constructors, Getters and Setters not included.
}

目前,对这个问题不太确定如何解决。目前,搜索只会引发以下异常:

java.lang.IllegalArgumentException: Parameter value [true] did not match expected type [java.lang.Boolean (n/a)]

有关其余代码的 Github 存储库链接请参见 car-booking-system

英文:

I have a rest API which I have implemented a query feature using RSQL-Parser. It works great for everything but boolean values "true" & "false".
This is my Rsql Specification following examples from Baeldung with some minor alterations to deal with Enum values have in the API.

public class GenericRsqlSpecification&lt;T&gt; implements Specification&lt;T&gt; {

  private String property;
  private ComparisonOperator operator;
  private List&lt;String&gt; arguments;

  public GenericRsqlSpecification(String property, ComparisonOperator operator, List&lt;String&gt; arguments) {
    this.property = property;
    this.operator = operator;
    this.arguments = arguments;
  }

  @Override
  public Predicate toPredicate(Root&lt;T&gt; root, CriteriaQuery&lt;?&gt; query,
                               CriteriaBuilder builder) {
    Path&lt;String&gt; propertyExpression = parseProperty(root);
    List&lt;Object&gt; args = castArguments(propertyExpression);
    Object argument = args.get(0);
    switch (RsqlSearchOperation.getSimpleOperator(operator)) {
      case EQUAL:
        if (argument instanceof String)
          return builder.like(propertyExpression,
            argument.toString().replace(&#39;*&#39;, &#39;%&#39;));
        else if (argument == null)
          return builder.isNull(propertyExpression);
        else return builder.equal(propertyExpression, argument);

      case NOT_EQUAL:
        if (argument instanceof String)
          return builder.notLike(propertyExpression,
            argument.toString().replace(&#39;*&#39;, &#39;%&#39;));
        else if (argument == null)
          return builder.isNotNull(propertyExpression);
        else return builder.notEqual(propertyExpression, argument);

      case GREATER_THAN:
        return builder.greaterThan(propertyExpression,
          argument.toString());

      case GREATER_THAN_OR_EQUAL:
        return builder.greaterThanOrEqualTo(propertyExpression,
          argument.toString());

      case LESS_THAN:
        return builder.lessThan(propertyExpression,
          argument.toString());

      case LESS_THAN_OR_EQUAL:
        return builder.lessThanOrEqualTo(propertyExpression,
          argument.toString());
      case IN:
        return propertyExpression.in(args);
      case NOT_IN:
        return builder.not(propertyExpression.in(args));
    }

    return null;
  }

  private Path&lt;String&gt; parseProperty(Root&lt;T&gt; root) {
    Path&lt;String&gt; path;
    if (property.contains(&quot;.&quot;)) {
      // Nested properties
      String[] pathSteps = property.split(&quot;\\.&quot;);
      String step = pathSteps[0];
      path = root.get(step);
      From lastFrom = root;

      for (int i = 1; i &lt;= pathSteps.length - 1; i++) {
        if (path instanceof PluralAttributePath) {
          PluralAttribute attr = ((PluralAttributePath) path).getAttribute();
          Join join = getJoin(attr, lastFrom);
          path = join.get(pathSteps[i]);
          lastFrom = join;
        } else if (path instanceof SingularAttributePath) {
          SingularAttribute attr = ((SingularAttributePath) path).getAttribute();
          if (attr.getPersistentAttributeType() != Attribute.PersistentAttributeType.BASIC) {
            Join join = lastFrom.join(attr, JoinType.LEFT);
            path = join.get(pathSteps[i]);
            lastFrom = join;
          } else {
            path = path.get(pathSteps[i]);
          }
        } else {
          path = path.get(pathSteps[i]);
        }
      }
    } else {
      path = root.get(property);
    }
    return path;
  }

  private Join getJoin(PluralAttribute attr, From from) {
    switch (attr.getCollectionType()) {
      case COLLECTION:
        return from.join((CollectionAttribute) attr);
      case SET:
        return from.join((SetAttribute) attr);
      case LIST:
        return from.join((ListAttribute) attr);
      case MAP:
        return from.join((MapAttribute) attr);
      default:
        return null;
    }
  }

  private List&lt;Object&gt; castArguments(Path&lt;?&gt; propertyExpression) {
    Class&lt;?&gt; type = propertyExpression.getJavaType();

    return arguments.stream().map(arg -&gt; {
      if (type.equals(Integer.class)) return Integer.parseInt(arg);
      else if (type.equals(Long.class)) return Long.parseLong(arg);
      else if (type.equals(Byte.class)) return Byte.parseByte(arg);
      else if (type.equals(Boolean.class)) return Boolean.parseBoolean(arg);
      else if (type.equals(EngineType.class)) return EngineType.valueOf(arg);
      else if (type.equals(OnlineStatus.class)) return OnlineStatus.valueOf(arg);
      else return arg;
    }).collect(Collectors.toList());
  }
//GETTERS &amp; SETTERS left out.
}

Car Model

public class CarDO {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(nullable = false)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
  private ZonedDateTime dateCreated = ZonedDateTime.now();
  @Column(nullable = false)
  private String licensePlate;
  @Column(nullable = false)
  private int seatCount;
  @Column(nullable = false)
  private boolean convertible;
  @Column(nullable = false)
  private int rating;
  @Column(nullable = false)
  @Enumerated(EnumType.STRING)
  private EngineType engineType;
  @Column(nullable = false)
  private String manufacturer;
  @Column(nullable = false)
  private boolean selected;
  @OneToOne(fetch = FetchType.EAGER)
  @JoinTable(name = &quot;driver_with_car&quot;,
    joinColumns = {@JoinColumn(name = &quot;car_id&quot;)},
    inverseJoinColumns = {@JoinColumn(name = &quot;driver_id&quot;)})
  private DriverDO driver;
//Constructors, Getters and Setters not included.
}

Not really sure on how to work around this issue. Currently, the search just fails with this exception

java.lang.IllegalArgumentException: Parameter value [true] did not match expected type [java.lang.Boolean (n/a)]

Github repo for rest of the code
car-booking-system

答案1

得分: 1

在一个“这不可能是这样,但是算了” 的瞬间,我将可转换和选择的数据类型从boolean更改为Boolean,然后一切正常运作。

public class CarDO {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(nullable = false)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
  private ZonedDateTime dateCreated = ZonedDateTime.now();
  @Column(nullable = false)
  private String licensePlate;
  @Column(nullable = false)
  private int seatCount;
  @Column(nullable = false)
  private Boolean convertible;
  @Column(nullable = false)
  private int rating;
  @Column(nullable = false)
  @Enumerated(EnumType.STRING)
  private EngineType engineType;
  @Column(nullable = false)
  private String manufacturer;
  @Column(nullable = false)
  private Boolean selected;
  @OneToOne(fetch = FetchType.EAGER)
  @JoinTable(name = "driver_with_car",
    joinColumns = {@JoinColumn(name = "car_id")},
    inverseJoinColumns = {@JoinColumn(name = "driver_id")})
  private DriverDO driver;
}
英文:

So on a "That can't be it but what the heck" moment I changed the datatype of convertible and selected to Boolean instead of boolean and all worked fine.

public class CarDO {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  @Column(nullable = false)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
  private ZonedDateTime dateCreated = ZonedDateTime.now();
  @Column(nullable = false)
  private String licensePlate;
  @Column(nullable = false)
  private int seatCount;
  @Column(nullable = false)
  private Boolean convertible;
  @Column(nullable = false)
  private int rating;
  @Column(nullable = false)
  @Enumerated(EnumType.STRING)
  private EngineType engineType;
  @Column(nullable = false)
  private String manufacturer;
  @Column(nullable = false)
  private Boolean selected;
  @OneToOne(fetch = FetchType.EAGER)
  @JoinTable(name = &quot;driver_with_car&quot;,
    joinColumns = {@JoinColumn(name = &quot;car_id&quot;)},
    inverseJoinColumns = {@JoinColumn(name = &quot;driver_id&quot;)})
  private DriverDO driver;

huangapple
  • 本文由 发表于 2020年10月8日 22:04:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/64264235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定