Spring Data MongoDB中LocalDateTime闭区间日期查询报错问题
我之前在项目里也碰到过一模一样的问题!用Between方法查出来的结果是开区间,换成GreaterThanEqual+LessThanEqual组合又报序列化错误,核心原因就是Spring Data MongoDB默认没做好Java 8新日期类型(java.time.LocalDateTime)和MongoDB原生DateTime类型的转换适配。
问题根源
MongoDB的Java驱动默认只支持序列化java.util.Date这类旧日期类型,而LocalDateTime是Java 8引入的无时区日期类型,直接传入会导致序列化失败,这就是你看到json can't serialize type : class java.time.LocalDateTime错误的原因。
解决方案:自定义Mongo类型转换器
我们需要给Spring Data MongoDB注册自定义的转换器,让它能正确处理LocalDateTime和MongoDBorg.bson.types.DateTime之间的互转,同时建议统一用UTC时区避免时区差异带来的查询误差。
1. 创建Mongo配置类
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; @Configuration public class MongoDateTimeConfig { @Bean public MongoCustomConversions mongoCustomConversions() { List<Converter<?, ?>> converters = new ArrayList<>(); converters.add(new LocalDateTimeToMongoDateTimeConverter()); converters.add(new MongoDateTimeToLocalDateTimeConverter()); return new MongoCustomConversions(converters); } // LocalDateTime转MongoDB DateTime static class LocalDateTimeToMongoDateTimeConverter implements Converter<LocalDateTime, org.bson.types.DateTime> { @Override public org.bson.types.DateTime convert(LocalDateTime source) { if (source == null) { return null; } // 统一转成UTC时区存储 return new org.bson.types.DateTime( java.util.Date.from(source.atZone(ZoneOffset.UTC).toInstant()) ); } } // MongoDB DateTime转LocalDateTime static class MongoDateTimeToLocalDateTimeConverter implements Converter<org.bson.types.DateTime, LocalDateTime> { @Override public LocalDateTime convert(org.bson.types.DateTime source) { if (source == null) { return null; } // 从UTC时区转回LocalDateTime return source.toInstant().atZone(ZoneOffset.UTC).toLocalDateTime(); } } }
2. 重新使用你的查询方法
配置完转换器后,之前报错的方法就能正常工作了:
List<YourEntity> findAllByMetadataStartTimeGreaterThanEqualAndMetadataStartTimeLessThanEqual(LocalDateTime start, LocalDateTime end);
备选方案:自定义@Query查询
如果你不想用方法名解析,也可以直接写MongoDB查询语句,效果是一样的:
@Query("{'metadata.startTime': {$gte: ?0, $lte: ?1}}") List<YourEntity> findByMetadataTimeRange(LocalDateTime start, LocalDateTime end);
关于Between方法的小补充
你提到findAllByMetadataStartTimeBetween(start, end)是开区间,大概率是时区差异导致的!因为LocalDateTime不带时区,存储到MongoDB时会转成UTC,而你查询用的是本地时区的时间,导致边界值被过滤掉。用上面的UTC统一配置后,Between方法其实应该是闭区间查询,你可以再测试验证一下。
内容的提问来源于stack exchange,提问作者pennstatephil

