使用RSQL查询枚举类型触发InvalidDataAccessApiUsageException问题
解决RSQL查询枚举类型属性时的类型不匹配异常
这个问题我之前也碰到过——本质是RSQL解析出来的字符串参数直接传给JPA后,JPA没办法自动把字符串转换成你的SiteType枚举类型,所以才会抛出InvalidDataAccessApiUsageException。
问题原因
你的Site实体中siteType字段是SiteType枚举,虽然用@Enumerated(EnumType.STRING)指定了数据库存储为字符串,但RSQL解析查询语句时,会把LOCAL当成普通字符串参数传递,而JPA的Specification在构建查询条件时,无法自动完成字符串到枚举的类型转换,导致类型不匹配。
解决方案:修改CustomRsqlVisitor,添加枚举类型转换逻辑
你需要在自定义的CustomRsqlVisitor中,处理每个比较节点(比如EQ、IN)时,判断目标属性是否为枚举类型,如果是,就把字符串参数转换成对应的枚举实例。
1. 处理EqualsNode(==操作)
修改visit(EqualsNode)方法,添加枚举转换逻辑:
@Override public Specification<Site> visit(EqualsNode node) { String property = node.getSelector(); List<String> arguments = node.getArguments(); return (root, query, cb) -> { // 获取当前属性的Java类型 Class<?> propertyType = root.get(property).getJavaType(); Object value = arguments.get(0); // 如果是枚举类型,将字符串转为枚举实例 if (Enum.class.isAssignableFrom(propertyType)) { // 可选:统一大小写,避免因大小写不一致报错 String enumValue = arguments.get(0).toUpperCase(); value = Enum.valueOf((Class<? extends Enum>) propertyType, enumValue); } return cb.equal(root.get(property), value); }; }
2. 处理InNode(in操作)
如果你的查询用到了in操作(比如siteType=in=(LOCAL,ONDEMAND)),同样需要修改visit(InNode)方法:
@Override public Specification<Site> visit(InNode node) { String property = node.getSelector(); List<String> arguments = node.getArguments(); return (root, query, cb) -> { Class<?> propertyType = root.get(property).getJavaType(); List<Object> convertedValues = new ArrayList<>(); if (Enum.class.isAssignableFrom(propertyType)) { Class<? extends Enum> enumType = (Class<? extends Enum>) propertyType; for (String arg : arguments) { // 统一大小写后转换为枚举 convertedValues.add(Enum.valueOf(enumType, arg.toUpperCase())); } } else { convertedValues.addAll(arguments); } return root.get(property).in(convertedValues); }; }
3. 其他比较节点的处理
类似的,对于NotEqualsNode(!=)、GreaterThanNode等其他节点,只要涉及到枚举属性的参数传递,都需要做同样的类型转换处理,确保字符串参数被正确转为枚举实例。
额外注意事项
- 枚举值大小写:如果你的RSQL查询可能传入小写或混合大小写的枚举值,一定要加上
toUpperCase()(或toLowerCase(),根据你的枚举定义调整),避免Enum.valueOf抛出IllegalArgumentException。 - 枚举合法性:可以额外添加校验逻辑,判断传入的字符串是否是有效的枚举值,比如用
Arrays.stream(enumType.getValues()).anyMatch(e -> e.name().equalsIgnoreCase(arg)),如果无效可以提前抛出友好的异常。
内容的提问来源于stack exchange,提问作者Rajesh Jaiswal
相关产品推荐
相关产品推荐

