Spring Boot Data如何正确处理MongoDB中NumberLong类型的查询?
这个问题我之前也帮人排查过,核心就是大整数(超过Double精度范围)的精度丢失在搞鬼!你存储的是完整的64位整数,但不管是Mongo Shell还是Spring Data里,如果不小心把数值转成了Double类型,就会因为精度丢失匹配不上数据库里的真实值,下面分别给你说解决办法:
一、Mongo Shell里的正确查询姿势
Mongo Shell会自动把超出Double精度范围的整数转成Double类型,而Double只能精确表示2^53以内的整数,你的-3130564683773456202明显超了,直接写数值查肯定找不到。正确的做法是用NumberLong()构造函数,并且要把数值以字符串的形式传进去(避免先转成Double再转Long):
db.yourCollection.find({messageUid: NumberLong("-3130564683773456202")})
这样就能精确匹配到数据库里的Int64类型字段了。
二、Spring Boot Data中的处理方案
Spring Data MongoDB默认对Long类型的支持是没问题的,但有时候可能因为细节处理不到位导致查询空结果,你可以从这几点入手:
- 确保实体类字段类型正确:把
messageUid字段声明为Long(包装类比基本类型long更稳妥),并用@Field注解明确映射数据库字段名,避免名称或大小写不匹配:
import org.springframework.data.mongodb.core.mapping.Field; public class YourEntity { // 其他字段... @Field("messageUid") private Long messageUid; // 别忘了写getter和setter }
- 查询时用精确的Long参数:在Repository接口里定义查询方法时,参数类型一定要是
Long,别用字符串或者其他类型:
import org.springframework.data.mongodb.repository.MongoRepository; public interface YourEntityRepository extends MongoRepository<YourEntity, String> { YourEntity findByMessageUid(Long messageUid); }
调用的时候直接传Long.valueOf("-3130564683773456202")或者对应的Long值,确保参数是纯Long类型,这样Spring Data会自动把它映射成MongoDB的Int64类型去查询。
- 自定义Query查询的注意事项:如果用
MongoTemplate写自定义查询,一定要保证查询条件里的值是Long类型,别让它自动转成Double:
import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; // 注入MongoTemplate @Autowired private MongoTemplate mongoTemplate; public YourEntity getByMessageUid(Long uid) { Query query = new Query(Criteria.where("messageUid").is(uid)); return mongoTemplate.findOne(query, YourEntity.class); }
这里的uid必须是Long类型,这样生成的查询条件才会是精确的Int64匹配。
为啥会出现这个问题?
简单说就是MongoDB的Int64对应Java的Long,而Double类型的精度有限——超过2^53(大概9万亿)的整数,Double就没法精确表示了,你的这个数值绝对值远大于这个数,转成Double后会变成一个近似值,自然和数据库里的精确值匹配不上啦。
内容的提问来源于stack exchange,提问作者disk91

