QueryDSL NumberPath<BigDecimal> gt查询MongoDB集合结果异常求助
你遇到的问题核心原因非常明确:MongoDB中amountDue.amount存储的是字符串类型,而你在用QueryDSL做gt比较时,实际触发的是MongoDB的字符串字典序比较,而非数值比较。
举个直观的例子:字符串"106.00"和"20.00"做字典序比较时,会从第一个字符开始对比——'1'的ASCII码小于'2',所以MongoDB会判定"106.00" < "20.00"。这就是为什么你查询amountDue.amount > 20.00时,本该返回的106.00对象被排除;而当你把106改成小于100的数值(比如99.00),字符串"99.00"的第一个字符'9'大于'2',字典序比较结果就和数值比较一致,测试也就正常了。
针对这个问题,有两种可行的解决思路,优先推荐第一种(从根源修复):
1. 修复MongoDB数据类型(推荐)
金额这类数值型数据本来就不该用字符串存储,最彻底的解决方式是把amountDue.amount的类型从String改成MongoDB的Decimal128类型(对应Java的BigDecimal):
步骤1:批量更新现有数据
在MongoDB Shell中执行以下脚本,把所有文档的amountDue.amount从字符串转成Decimal128:
db.Payments.updateMany( {}, // 匹配所有文档 [ { $set: { "amountDue.amount": { $toDecimal: "$amountDue.amount" } } } ] )
步骤2:确保Java实体类映射正确
修改你的Payment实体类,确保amountDue.amount字段映射为BigDecimal(MongoDB的Decimal128可以直接和Java的BigDecimal兼容),对应的QueryDSL自动生成的路径也会是NumberPath<BigDecimal>,此时你原来的绑定代码就能正常工作,无需修改。
2. 临时方案:查询时强制数值转换
如果暂时无法修改数据库数据类型,可以在QueryDSL中使用MongoDB的聚合表达式,先把字符串转成数值再做比较:
修改你的绑定方法,用Expressions.booleanTemplate生成MongoDB的原生查询逻辑:
@Override public Predicate bind(NumberPath<BigDecimal> bigDecimalNumberPath, Collection<? extends BigDecimal> value) { ArrayList<? extends BigDecimal> amounts = new ArrayList<>(value); // 利用MongoDB的$toDecimal函数将字符串转为数值后再比较 String mongoTemplate = "{ $gt: [ { $toDecimal: {0} }, {1} ] }"; return Expressions.booleanTemplate(mongoTemplate, bigDecimalNumberPath, amounts.get(0)); }
这段代码会让MongoDB先把amountDue.amount的字符串值转成Decimal类型,再和传入的BigDecimal值做数值比较,这样就能得到符合预期的结果。
内容的提问来源于stack exchange,提问作者William

