前言
在使用 MyBatis Plus
操作 PostgreSQL
数据库时,我们可能会遇到一个棘手的问题:BigDecimal
类型的数据在存储或查询时精度丢失。这不仅会影响数据的准确性,还可能导致一些难以察觉的错误。本文将详细介绍这个问题的背景、排查过程以及最终的解决方案。
1.项目场景
在我的项目中,使用了 PostgreSQL
数据库,并且在表中定义了 NUMERIC
类型的字段来存储高精度数值。例如,我们有一个 data_cover
表,其中的 x
和 y
字段被定义为 NUMERIC
类型,用于存储坐标值:
CREATE TABLE data_cover (
id BIGINT NOT NULL,
parking_id BIGINT,
timestamp BIGINT,
x NUMERIC,
y NUMERIC,
floor VARCHAR(255)
);
在 Java
实体类中,我们使用 BigDecimal
来映射这些字段:
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("data_cover")
public class DataCover {
@TableField("id")
private Long id;
@TableField("parking_id")
private Long parkingId;
@TableField("timestamp")
private Long timestamp;
@TableField("x")
private BigDecimal x;
@TableField("y")
private BigDecimal y;
@TableField("floor")
private String floor;
}
然而,在插入或查询数据时,我们发现 x
和 y
字段的精度丢失了,只保留了整数部分。这显然不符合我们的预期。
2.排查过程
- 检查数据库字段定义
没问题
- 检查 SQL 插入语句
没问题
- 检查 MyBatis Plus 配置
在插入数据之前打印了 BigDecimal
的值,结果显示,BigDecimal
的值在插入之前是正确的,但插入数据库后精度丢失。这表明问题可能出现在 MyBatis Plus
的类型处理逻辑中
3.原因分析
最终排查到,MyBatis Plus 默认的类型处理器可能没有正确处理 BigDecimal 的精度。默认情况下,MyBatis Plus 使用 JdbcType.NUMERIC 来处理 BigDecimal,但可能在某些场景下(如批量操作)导致精度丢失。
4.解决方案
创建了一个自定义的 BigDecimalTypeHandler
类,覆盖了 MyBatis
的默认行为:
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@MappedTypes(BigDecimal.class)
@MappedJdbcTypes(JdbcType.NUMERIC)
public class BigDecimalTypeHandler extends BaseTypeHandler<BigDecimal> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, BigDecimal parameter, JdbcType jdbcType) throws SQLException {
ps.setBigDecimal(i, parameter); // 直接设置,避免精度丢失
}
@Override
public BigDecimal getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getBigDecimal(columnName);
}
@Override
public BigDecimal getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getBigDecimal(columnIndex);
}
@Override
public BigDecimal getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getBigDecimal(columnIndex);
}
}
然后,我们在 MyBatis Plus
的配置中注册了这个自定义类型处理器:
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
configuration.getTypeHandlerRegistry().register(BigDecimal.class, BigDecimalTypeHandler.class);
};
}
}
通过自定义类型处理器,成功解决了 BigDecimal 精度丢失的问题。自定义类型处理器确保了在插入和查询时,BigDecimal 的精度不会被截断或修改。
5.总结
在使用 MyBatis Plus 操作 PostgreSQL 数据库时,BigDecimal 精度丢失的问题可能是由于默认的类型处理器没有正确处理高精度数值。通过自定义类型处理器,我们可以覆盖默认行为,确保精度不会丢失。
如果你在项目中遇到类似的问题,可以尝试以下步骤:
1.检查数据库字段定义,确保字段类型和精度设置正确。
2.检查 SQL 插入语句,确保 BigDecimal 的值在插入之前是正确的。
3.检查 MyBatis Plus 的配置,确保没有对 BigDecimal 进行不必要的处理。
4.使用自定义类型处理器来确保精度不会丢失。
希望这篇文章能帮助你解决类似的问题。如果你有任何疑问或建议,欢迎在评论区留言!