如何在Spring Data R2DBC中映射PostgreSQL的'infinity'值到LocalDateTime
解决数据库'infinity'值与Java LocalDateTime的映射问题
你的场景是Java实体里定义了private LocalDateTime expireOn;,但数据库会在API没传值或传null时自动存'infinity',导致Java端映射报错,下面是几个直接可用的解决办法:
方案1:用JPA自定义属性转换器(适合Spring Data JPA场景)
写一个转换器类,自动把数据库的'infinity'转成Java的LocalDateTime最大值,反过来也能把null或最大值转成数据库的'infinity':
import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; import java.time.LocalDateTime; @Converter(autoApply = true) public class LocalDateTimeInfinityConverter implements AttributeConverter<LocalDateTime, String> { private static final String DB_INFINITY = "infinity"; // 用LocalDateTime的最大值代表无限过期时间 private static final LocalDateTime MAX_EXPIRE_TIME = LocalDateTime.MAX; @Override public String convertToDatabaseColumn(LocalDateTime entityValue) { if (entityValue == null || entityValue.equals(MAX_EXPIRE_TIME)) { return DB_INFINITY; } return entityValue.toString(); } @Override public LocalDateTime convertToEntityAttribute(String dbValue) { if (dbValue == null || DB_INFINITY.equalsIgnoreCase(dbValue)) { return MAX_EXPIRE_TIME; } return LocalDateTime.parse(dbValue); } }
- 如果想让这个转换器全局生效,保留
autoApply = true;如果只想给expireOn字段用,去掉autoApply,然后在字段上加@Convert(converter = LocalDateTimeInfinityConverter.class)。
方案2:MyBatis自定义类型处理器(适合MyBatis场景)
如果用MyBatis做持久层,写个类型处理器处理转换逻辑:
import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; @MappedTypes(LocalDateTime.class) public class LocalDateTimeInfinityTypeHandler extends BaseTypeHandler<LocalDateTime> { private static final String DB_INFINITY = "infinity"; private static final LocalDateTime MAX_EXPIRE_TIME = LocalDateTime.MAX; @Override public void setNonNullParameter(PreparedStatement ps, int index, LocalDateTime param, JdbcType jdbcType) throws SQLException { if (param.equals(MAX_EXPIRE_TIME)) { ps.setString(index, DB_INFINITY); } else { ps.setString(index, param.toString()); } } @Override public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException { return parseDbValue(rs.getString(columnName)); } @Override public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parseDbValue(rs.getString(columnIndex)); } @Override public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parseDbValue(cs.getString(columnIndex)); } private LocalDateTime parseDbValue(String dbValue) { if (dbValue == null || DB_INFINITY.equalsIgnoreCase(dbValue)) { return MAX_EXPIRE_TIME; } return LocalDateTime.parse(dbValue); } }
- 写完后在MyBatis配置文件里注册这个处理器,或者在ResultMap的字段上指定
typeHandler属性即可。
方案3:数据库查询层面临时处理(适合快速调试)
如果不想改代码,可以在查询SQL里用CASE语句把'infinity'转成Java能解析的最大日期字符串:
SELECT CASE WHEN expire_on = 'infinity' THEN '9999-12-31T23:59:59' ELSE expire_on END AS expireOn FROM your_table;
不过这个方法需要修改所有涉及该字段的查询,灵活性不如代码层面的转换器。
内容的提问来源于stack exchange,提问作者Thirumal
相关产品推荐
相关产品推荐

