Spring Boot JPA Repository解析MongoDB ISODate字段失败问题
这个问题我之前也踩过坑,根源其实很清楚:你的MongoDB集合里的lastModified字段存储的并不是真正的ISODate类型,而是包裹在字符串里的模拟格式(比如"ISODate(2018-04-19T19:10:39.356574)"),但你的实体类里对应的字段应该是java.util.Date或者Java 8+的时间类型,Spring Data MongoDB没法自动把这种特殊字符串转换成日期对象,所以就抛出了ConversionFailedException。
下面给你两种解决方案,优先选第一种,实在不行再用第二种:
方案一:修改MongoDB中的数据为真正的ISODate类型(推荐)
这是最根本的解决办法,因为用字符串存日期不符合MongoDB的最佳实践,后续做日期范围查询、排序都会有麻烦。你可以用MongoDB Shell执行以下命令批量更新数据:
db.yourCollectionName.updateMany( { lastModified: { $type: "string" } }, // 只更新字符串类型的lastModified字段 [ { $set: { lastModified: { // 提取字符串中ISODate(...)内部的日期部分,转换成Date类型 $dateFromString: { dateString: { $substrCP: ["$lastModified", 9, { $subtract: [{ $strLenCP: "$lastModified" }, 10] }] } } } } } ] )
记得把yourCollectionName换成你实际的集合名称。执行完之后,你的lastModified字段就会变成真正的ISODate类型,Spring Data MongoDB就能自动映射到Java的日期字段了,不需要额外代码。
方案二:自定义转换器处理特殊字符串格式(无法修改数据库时用)
如果因为各种原因不能修改数据库里的数据,那我们可以给Spring Data MongoDB加一个自定义转换器,让它能识别这种"ISODate(...)"格式的字符串并转换成Java日期。
步骤1:编写自定义转换器
如果你用的是java.util.Date,可以写这样的转换器:
import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class IsoDateStringToDateConverter implements Converter<String, Date> { // 匹配你的日期格式,注意毫秒部分是6位 private static final SimpleDateFormat ISO_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); @Override public Date convert(String source) { // 校验输入格式 if (source == null || !source.startsWith("ISODate(") || !source.endsWith(")")) { return null; } // 提取ISODate()内部的日期字符串 String dateStr = source.substring(9, source.length() - 1); try { return ISO_FORMAT.parse(dateStr); } catch (ParseException e) { throw new IllegalArgumentException("Invalid ISODate string format: " + source, e); } } }
如果项目用的是Java 8+的LocalDateTime,可以换成下面的转换器:
import org.springframework.core.convert.converter.Converter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class IsoDateStringToLocalDateTimeConverter implements Converter<String, LocalDateTime> { private static final DateTimeFormatter ISO_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); @Override public LocalDateTime convert(String source) { if (source == null || !source.startsWith("ISODate(") || !source.endsWith(")")) { return null; } String dateStr = source.substring(9, source.length() - 1); return LocalDateTime.parse(dateStr, ISO_FORMAT); } }
步骤2:注册转换器到Spring配置
创建一个MongoDB的配置类,把上面的转换器注册进去:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import java.util.Arrays; @Configuration public class MongoConfig { @Bean public MongoCustomConversions mongoCustomConversions() { // 这里用你上面写的转换器类 return new MongoCustomConversions(Arrays.asList(new IsoDateStringToDateConverter())); // 如果用LocalDateTime的话就换成: // return new MongoCustomConversions(Arrays.asList(new IsoDateStringToLocalDateTimeConverter())); } }
步骤3:确保实体类字段配置正确
你的实体类里的lastModified字段要和转换器的目标类型对应,比如:
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; import java.util.Date; @Document(collection = "yourCollectionName") public class YourEntity { private String name; @Field("lastModified") private Date lastModified; // 省略getter、setter和构造方法 }
如果用LocalDateTime,就把字段类型改成LocalDateTime即可。
做完这些配置后,再调用findByName(String name)方法就不会再抛出转换异常了。
内容的提问来源于stack exchange,提问作者MichaelB

