英文:
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<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) {
Path<String> propertyExpression = parseProperty(root);
List<Object> 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('*', '%'));
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('*', '%'));
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<String> parseProperty(Root<T> root) {
Path<String> path;
if (property.contains(".")) {
// Nested properties
String[] pathSteps = property.split("\\.");
String step = pathSteps[0];
path = root.get(step);
From lastFrom = root;
for (int i = 1; i <= 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<Object> castArguments(Path<?> propertyExpression) {
Class<?> type = propertyExpression.getJavaType();
return arguments.stream().map(arg -> {
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 & 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 = "driver_with_car",
joinColumns = {@JoinColumn(name = "car_id")},
inverseJoinColumns = {@JoinColumn(name = "driver_id")})
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 = "driver_with_car",
joinColumns = {@JoinColumn(name = "car_id")},
inverseJoinColumns = {@JoinColumn(name = "driver_id")})
private DriverDO driver;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论