当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

开心一刻

  中午和哥们一起喝茶

  哥们说道:晚上喝酒去啊

  我:不去,我女朋友过生日

  哥们瞪大眼睛看着我:你有病吧,充气的过什么生日

  我生气到:有特么生产日期的好吧

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

需求背景

  系统对接了外部系统,调用外部系统的接口需要付费,一个接口一次调用付费 0.03 元

  同一个月内,同一个接口最高付费 25 元

  统计每个月的付费情况

  需求清楚了不?不清楚? 给大家举个案例

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  这下明白了吧

  明白了需求,相信大家都会觉得很简单,不就是一个分组汇总吗?

  客官说的对,但生活总会给我们一点 surprise 

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  我们慢慢往下看

环境准备

   SQL Server 版本: SQL Server 2017 

   MySQL 版本: 8.0.27 

  引入 MySQL ,是为了跟 SQL Server 做对比

   SQL Server 建表并初始化数据

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

CREATE TABLE tbl_interface_call_times (
    id BIGINT PRIMARY KEY IDENTITY(1,1),
    call_month INT NOT NULL,
        interface varchar(50) NOT NULL ,
        times INT NOT NULL
);
INSERT INTO tbl_interface_call_times(call_month, interface, times) VALUES
(202301, 'interface1', 800),
(202301, 'interface2', 1000),
(202301, 'interface3', 100),
(202302, 'interface1', 833),
(202302, 'interface2', 834),
(202302, 'interface3', 134),
(202302, 'interface4', 243),
(202302, 'interface5', 2143);

View Code

   MySQL 建表并初始化数据

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

CREATE TABLE tbl_interface_call_times (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    call_month INT NOT NULL COMMENT '月份',
        interface varchar(50) NOT NULL COMMENT '接口',
        times INT NOT NULL COMMENT '调用次数',
    PRIMARY KEY(id)
) COMMENT '接口调用次数';
INSERT INTO tbl_interface_call_times(call_month, interface, times) VALUES
(202301, 'interface1', 800),
(202301, 'interface2', 1000),
(202301, 'interface3', 100),
(202302, 'interface1', 833),
(202302, 'interface2', 834),
(202302, 'interface3', 134),
(202302, 'interface4', 243),
(202302, 'interface5', 2143);

View Code

  汇总每个月的付费, SQL 该如何写?

  很简单的啦,如下所示

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

SELECT call_month, 
    SUM(
        CASE WHEN times * 0.03 > 25 THEN 25
        ELSE times * 0.03
        END
    ) monthFee
FROM tbl_interface_call_times
GROUP BY call_month

View Code

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  通用写法, SQL Server 和 MySQL 都支持

  我们看下查询结果

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  一切都很正常,觉得世界真美好!

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

问题复现

  我们不能光玩数据库吧?

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  不得像这样雨露均沾?

  必须把 spring-boot 、 MyBatis-Plus 安排上

   mysql-jdbc 版本: 8.0.21 , mssql-jdbc 版本: 6.2.1.jre8 

  完整代码:mybatis-plus-dynamic-datasource

  访问: http://localhost:8081/interface/summary?startMonth=202301&endMonth=202302 

  你会发现,你心心念念的 surprise 终于出现了!

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  正确应该是 86.3.3 哪去了?

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  直查数据库是没问题的呀

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  莫非 MyBatis-Plus 有问题?

  我们切到 MySQL 试试;将 InterfaceCallTimesServiceImpl 上的数据源改成 mysql_db 

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  然后重启,我们再访问: http://localhost:8081/interface/summary?startMonth=202301&endMonth=202302 

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  这说明应该不是 MyBatis 的问题,那不完犊子了?

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

问题解决

  是不是束手无策了? 也不是,我们可以 Bing 一下的嘛

  你会发现说的都是批量 insert 的时候, BigDecimal 有精度丢失

  单条插入的时候,是没有精度丢失的

  然后了,大家试出了一条件论: 批量插入数据时,如果插入的数据精度不统一,最终入库的数据精度统一按最低的精度入库 

  虽说我们只是查询,莫非也需要 精度统一 ?

  精度统一

  试试呗,反正又不要钱

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  重启,神奇的事情发生了

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  .3 它回来了! 相信此刻的你肯定有一种与知己久别重逢的激动

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  问题貌似解决了,但说实话,这种处理方式你用的放心吗?

  升级 mssql-jdbc 版本

  我们好好捋一下,程序从 SQL Server 获取数据,经历了哪些环节?

  只有三个: MyBatis-Plus  ->  mssql-jdbc ->  SQL Server 

  前面我们已经排除了 SQL Server 和 MyBatis-Plus 

  那问题肯定就出在 mssql-jdbc 身上了

  问题又来了,该如何从 mssql-jdbc 上找问题了?

  开源的东西从它的官方找相关的 issue ,肯定不止我们遇到这样的问题,那么肯定有人会给官方提了 issue 

   issue 地址: https://github.com/microsoft/mssql-jdbc/issues 

  直接搜索 BigDecimal ,像这样

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  回车之后,你会发现,原来你不是一个人在战斗

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  那就去里面找呗,发现 #1489 跟我们的问题有点像,仔细去读,发现关联了 #1912

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  读到 1912 的末尾,你会发现又关联了 #2051,我们去看看 2051

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  那就是在这里修复了呀,那它关联的版本是哪个了?

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  然后我们在回到我们搜索 BigDecimal 相关 issue 的时候,你会发现

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

   12.2.0 已经发布了

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  如果觉得看英文的费劲,那就看中文的:Microsoft JDBC Driver for SQL Server 发行说明

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  这总看得懂了吧

  那就将 mssql-jdbc 升级到 12.2.0 试试

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

  入参不用统一精度,结果也正确了!

  但是,又开始转折了,你以为 12.2.0 就高枕无忧了?

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

   BigDecimal 的问题都延续到 12.3.0 了

  此刻大家的心情是怎样的,请评论区留言

总结

  1、当 mssql-jdbc 遇上 BigDecimal ,两种处理方式

    1.1  BigDecimal 类型的入参全部统一成最高精度

    1.2 版本升级到 12.2.0 ,但还是有问题,需要考虑业务是否会触发 12.2.0 的 bug 

  2、  mssql-jdbc 的 BigDecimal 的问题从 2016 年就开始出现了,到了现在( 2023 )还存在问题,我真的想对官方说一句

当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑!

原文链接:https://www.cnblogs.com/youzhibing/p/17353176.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:当 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丢失,真坑! - Python技术站

(0)
上一篇 2023年4月28日
下一篇 2023年4月28日

相关文章

  • java使用反射创建并操作对象的方法

    Java反射可以在运行时获取类的信息以及动态操作对象,使用反射创建并操作对象的方法如下: 1.获取Class对象 使用反射创建对象,首先需要获取Class对象,有如下三种方式:- 调用Class.forName()- 通过类名.class获取- 使用对象.getClass()方法获取Class对象 示例1:调用Class.forName()方法获取Class…

    Java 2023年5月26日
    00
  • 利用数组实现栈(Java实现)

    下面就详细讲解一下“利用数组实现栈(Java实现)”的完整攻略。 一、栈的概念 栈是一种具有特殊性质的线性结构,它只允许在一端进行插入和删除操作,这一端被称为栈顶。具体来说,栈的特点是后进先出(Last In First Out,LIFO)。 二、栈的实现 栈可以使用数组实现,这里我们介绍一种基于数组的简单栈实现方法: public class MyStac…

    Java 2023年5月26日
    00
  • Springboot mybatis常见配置问题解决

    下面是Springboot MyBatis常见配置问题解决的完整攻略。 问题一:MyBatis的Mapper不能正常映射数据库表 原因 由于 Mapper 文件和数据库表的对应关系没有处理好,MyBatis 执行时会找不到对应的表或列,导致不能正常映射。 解决方案 确认数据库配置是否正确,包括数据库名称、端口、用户名、密码等。 确认 Mapper 文件的命名…

    Java 2023年5月20日
    00
  • Java中的File类是什么?

    File类是Java中的一个类,用于操作文件或文件夹。它主要用于获取文件或文件夹的属性、操作(创建、删除、重命名等)文件或文件夹。 File类的基本使用 File类提供了多个构造方法,可以使用文件路径或URI来创建一个文件实例,例如: // 通过文件路径创建File实例 File file = new File("C:/test.txt"…

    Java 2023年4月27日
    00
  • Maven学习教程之搭建多模块企业级项目

    以下是详细讲解Maven学习教程之搭建多模块企业级项目的完整攻略: 1. 准备工作 在开始搭建多模块企业级项目之前,我们需要进行以下准备工作: (1)安装Java和Maven 首先需要安装Java和Maven,Java需要1.7及以上版本,Maven需要3.0及以上版本。 (2)创建项目目录结构 我们需要创建项目目录结构,可以使用以下命令创建: mkdir …

    Java 2023年5月20日
    00
  • java中字符串转整数及MyAtoi方法的实现

    接下来我将会详细讲解Java中字符串转整数及myAtoi方法的实现。 什么是字符串转整数? 字符串转整数是指将一个字符串表示的数字转换为整数类型的过程。在Java中,可以通过Integer.parseInt()方法将字符串转换为整数。但需要注意的是,转换时存在一些限制条件,例如字符串必须全为数字等。 myAtoi方法的实现 myAtoi()方法是LeetCo…

    Java 2023年5月26日
    00
  • (starters)springboot-starter整合阿里云datahub方式

    完整攻略:Spring Boot整合阿里云DataHub 一、前置条件在开始整合之前,需要先确保以下几个条件: 阿里云账号及DataHub服务我们需要一个已开通DataHub服务的阿里云账号,假设我们已有一个名为”test-datahub”的DataHub项目。 工具准备a) Maven及Java IDE(本文以Intellij IDEA为例)b) 阿里云S…

    Java 2023年5月20日
    00
  • 一篇文章带你了解Java 中序列化与反序列化

    一篇文章带你了解Java 中序列化与反序列化 引言 在Java编程中,可能需要将对象存储在文件中或通过网络传输。使用序列化来处理这些任务是很常见的方法。本篇文章将介绍Java中的序列化和反序列化的概念和用法,给你提供一个完整的攻略。 Serializable 接口 Java 中的序列化和反序列化要求被序列化的类必须实现 Serializable 接口。实现 …

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部