跟踪发现 scale 为2时会有问题,如果为4就会保留小数位

为什么会丢精度(35.35 → 35)
-
SQLServer money 本质
money= 固定 DECIMAL(19,4)(小数点后强制 4 位)- 但它是数据库专有类型,不是标准
DECIMAL/NUMERIC - 当驱动 / MyBatis 没正确指定精度 /scale 时,会被当成整数截断
-
MyBatis 默认映射问题
- 默认
BigDecimalTypeHandler对应NUMERIC/DECIMALMyBatis - 不认识
money,没传 scale 信息 → SQLServer 按 默认 scale=0 处理 - 批量插入时更严重:按最小精度统一截断
- 默认
-
驱动(mssql-jdbc)行为
- 低版本驱动:
setBigDecimal给money时 丢失 scale - 高版本需要加连接参数:
calcBigDecimalPrecision=true
- 低版本驱动:
结果:BigDecimal(35.35, scale=2) → 被当成 35 插入
自定义 修改拦截器(一劳永逸)
写一个专门给 Bigdecimal插入、修改拦截器在即将插入前将精度提高解决:
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
@Component
@Intercepts({
// 拦截 增/删/改 最顶层执行器
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class GlobalBigDecimalInterceptor implements Interceptor {
private static final int TARGET_SCALE = 4;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object parameter = invocation.getArgs()[1]; // 这是 MyBatis 真正的参数!
if (parameter != null) {
// 【全局处理所有 BigDecimal】打断点这里 100% 进来!
processAllBigDecimal(parameter);
}
return invocation.proceed(); // 处理完再执行 SQL
}
// 递归处理一切对象:实体、Map、List、嵌套对象
private void processAllBigDecimal(Object obj) {
if (obj == null) return;
// 1. 处理 List / 批量插入
if (obj instanceof List<?>) {
((List<?>) obj).forEach(this::processAllBigDecimal);
return;
}
// 2. 处理 MyBatis 包装参数 ParamMap
if (obj instanceof MapperMethod.ParamMap<?>) {
MapperMethod.ParamMap<?> map = (MapperMethod.ParamMap<?>) obj;
for (Object key : map.keySet()) {
processAllBigDecimal(map.get(key));
}
return;
}
// 3. 处理 MyBatis 内部 Map
if (obj instanceof DefaultSqlSession.StrictMap<?>) {
DefaultSqlSession.StrictMap<?> map = (DefaultSqlSession.StrictMap<?>) obj;
for (Object key : map.keySet()) {
processAllBigDecimal(map.get(key));
}
return;
}
// 4. 处理普通 Map
if (obj instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) obj;
for (Object key : map.keySet()) {
Object val = map.get(key);
if (val instanceof BigDecimal) {
((Map) map).put(key, fixScale((BigDecimal) val));
} else {
processAllBigDecimal(val);
}
}
return;
}
// 5. 处理实体类(你的对象一定走这里)
Class<?> clazz = obj.getClass();
if (clazz.getName().startsWith("java.")) return;
// 遍历当前类 + 父类所有字段
while (clazz != null && !clazz.getName().startsWith("java.lang")) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getType() == BigDecimal.class) {
try {
field.setAccessible(true);
BigDecimal value = (BigDecimal) field.get(obj);
if (value != null) {
// 🔥 这里你打断点,能亲眼看到 35.35 → 35.3500
field.set(obj, fixScale(value));
}
} catch (Exception ignored) {}
}
}
clazz = clazz.getSuperclass();
}
}
// 精度统一处理
private BigDecimal fixScale(BigDecimal decimal) {
if (decimal.scale() == TARGET_SCALE) return decimal;
return decimal.setScale(TARGET_SCALE, RoundingMode.HALF_UP);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {}
}
解决方法2 cast函数直接强转即可 或调整csale精度大点。

2473

被折叠的 条评论
为什么被折叠?



