一个年代久远的项目:将商品库的数据清洗扩展,发送至消息中心,再写入ElasticSearch。某一次新需求开发。看到项目中的POM.xml文件有告警信息:
- <parent>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-parentartifactId>
- <version>1.5.2.RELEASEversion>
- parent>
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- <version>5.0.7version>
- dependency>
Overriding managed version 5.1.41 for mysql-connector-java
嗯,这个mysql 的JDBC驱动版本为啥用这么低的版本,不科学!去了,让它和SpringBoot保持一致。于是,我去除了version指定,愉快地发版了。结果半夜被同事叫醒,说我负责的ElasticSearch数据有问题。经过调查,发现有问题的地方与这次新需求的改动毫无关系。我坚决及肯定地认为不是我的问题(当然我私下又把自己的代码审视了一番之后保证的)。但问题总是要解决,只好十分不情愿地回退版本。结果神奇地发现,数据在渐渐变得正确。
嗯,看来,确实是这次的改动造成了这次“动荡”。
第二天,再次仔细对比了版本,发现除了新需求改动,我还动了上面的POM文件。经过两次打包文件的对比,发现新版本的依赖包:mysql-connector-java已经是5.1.41。
升级JDBC驱动包会影响业务逻辑?!这很不科学!
经过很长时间的本地DEBUG,发现有一段类似下面的代码:
- import java.sql.ResultSet;
- import java.sql.ResultSetMetaData;
- import java.sql.SQLException;
- import java.text.SimpleDateFormat;
- import java.util.HashMap;
- import java.util.Map;
-
- import org.apache.commons.lang.exception.ExceptionUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.jdbc.core.RowMapper;
-
- public class DefaultRowMapper implements RowMapper
-
- private static final Logger logger = LoggerFactory.getLogger(DefaultRowMapper.class);
-
- private String[] columnNames = null;
- private int[] columnTypes = null;
- private final SimpleDateFormat dateFromat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- private final SimpleDateFormat dateFromat2 = new SimpleDateFormat("yyyy-MM-dd");
-
- public Map
mapRow(ResultSet rs, int rowNum) throws SQLException { - int size = 0;
- if(columnNames == null){
- ResultSetMetaData metaData = rs.getMetaData();
- size = metaData.getColumnCount();
- columnNames = new String[size];
- columnTypes = new int[size];
- for(int i=0; i
- columnNames[i] = metaData.getColumnName(i+1);
- columnTypes[i] = metaData.getColumnType(i+1);
- }
- }
- Map
map = new HashMap(); - size = columnNames.length;
- for(int i=0; i
- map.put(columnNames[i], getValue(rs.getObject(i+1), columnTypes[i]));
- }
- return map;
- }
-
- private String getValue(Object fieldValue, int sqlType) {
- if(fieldValue == null){
- return null;
- }
- if(fieldValue instanceof String){
- return (String)fieldValue;
- }
- switch(sqlType){
- case java.sql.Types.TIMESTAMP:
- try {
- return dateFromat1.format(dateFromat1.parse(fieldValue.toString()));
- } catch (Exception e) {
- logger.error("dateFromat1 error"+ExceptionUtils.getFullStackTrace(e));
- }
- case java.sql.Types.DATE:
- try {
- return dateFromat2.format(dateFromat2.parse(fieldValue.toString()));
- } catch (Exception e) {
- logger.error("dateFromat2 error"+ExceptionUtils.getFullStackTrace(e));
- }
- case java.sql.Types.BOOLEAN:
- case java.sql.Types.BIT:
- if(Boolean.TRUE.equals(fieldValue)){
- return "1";
- }else{
- return "0";
- }
- default:
- return fieldValue.toString();
- }
- }
-
- }
(大家看到这样的代码先不要笑!毕竟是十几年前的人写的,可能那时没有ORM,也可能那时作者不知道ORM)
如业务有类似下面的SQL:
SELECT PROVINCE_ID provinceId FROM PRODUCT_RANGE WHERE LIST_ID=?
在驱动版本:5.0.7时,metaData.getColumnName得到的是"provinceId",而当驱动版本升级为5.1.14时,得到的却是"PROVINCE_ID"。这就导致新版本业务数据全部不正常。
按照原编码的意图,应该使用metaData.getColumnLabel取得列名。但为什么原编码不使用metaData.getColumnLabel呢?我将本地环境改为了5.0.7后,发现无论是getColumnName()还是getColumnLabel()方法均返回"provinceId"。这就导致原作者认为无论使用哪个方法都是对的。
结论
经过不断调查,终于在官网上找到比较满意的回答。
MySQL :: MySQL Connector/J 8.0 Developer Guide :: 6.3.17 JDBC compliancehttps://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-jdbc-compliance.html为了防止官网改版移动位置,我在此快照下来:
useColumnNamesInFindColumn
在 JDBC-4.0 之前,JDBC 规范有一个错误,该错误与可以作为列名提供给结果集方法(如“findColumn()”)或采用 String 属性的 getter 相关。JDBC-4.0 将“列名”澄清为表示标签,如“AS”子句中给出并由“ResultSetMetaData.getColumnLabel()”返回,如果未指定“AS”子句,则为列名。将此属性设置为“true”将导致与 JDBC-3.0 和早期版本的 JDBC 规范一致的行为,但可能会产生意外结果。此属性优于“useOldAliasMetadataBehavior”
默认值 false 自版本 5.1.7
useOldAliasMetadataBehavior
驱动程序是否应该对列和表上的“AS”子句使用旧行为,并且只返回“ResultSetMetaData.getColumnName()”或“ResultSetMetaData.getTableName()”的别名(如果有)而不是原始列/表名?
默认值 false 自版本 5.0.4
简单地说,5.1.7版本时就已经使用JDBC4.0规范作为默认实现了。但为了兼容前版本的调用者,驱动有个开关。
说到底,原编码在使用5.0.7版本时,如果仔细看一下API,就应该使用getColumnLabel()方法来取得结果集列名。而我这个后来者,一不小心就踩到这个坑里去了。悲呼!
-
相关阅读:
【SpringBoot集成Redis + Session持久化存储到Redis】
实战计算机网络02——物理层
Kubernetes(K8S)命令指南
一. HTML基础开发标签
OBS-Studio-27.2.4-Full-Installer-x64.exe 下载
2022 Java生态系统报告:Java 11超Java 8、Oracle在缩水、Amazon在崛起
C语言哈希表的线性探测法
如何使用Vue原生组件编译应用程序主题?这个工具不要错过
Ansible--playbook剧本
【C++】二叉搜索树
-
原文地址:https://blog.csdn.net/wzg725/article/details/126183289