mybatis报错Error attempting to get column ‘id‘ from result set. Cause: org.postgresql.util.PSQLExcept

2023-05-16

mybatis报错Error attempting to get column ‘id’ from result set. Cause: org.postgresql.util.PSQLException: Bad value for type int : 493987884173376\n;

1、事件起因:公司变更原本的自增id转成snowid,但是在测试过程中出现了一个select语句报错了,代码无变更,报错的内容大致是接收的对象不应该使用id来接收,因为数据库变更后是个bigint,正常得拿long来接收,但是问题是sql查询的确实有id,可是接收的对象类中是没有id字段的,正常不会接收这个id参数才对。对象类和sql贴在下方。在这里插入图片描述


2、sql语句比较简单,仅仅是一个查询语句,附上sql和表结构

在这里插入图片描述

    <select id="selectAutoPullGroupConfig"
            resultType="com.hujing.hujing_sc_bussiness.pojo.vo.channel2.AutoPullGroupConfigVo">
        select * from qw_pull_group_config
        where corpid = #{corpid}
          and state = #{state}
          and deleted = 0
    </select>

3、对象的接收类如下,其中可以看到报错的id字段在数据库中是存在的,但是对象类中并没有id字段:


@Data
public class AutoPullGroupConfigVo {
    private Integer lastGroupSurplus;
    private Integer pullGroupDays;
    @NotNull
    private Integer qrcodeType;
    private String userConfig;
    private Integer usingQrcodeOrder;
    private Long createrId;
    private String createrName;
    private Long createrDeleted;
}

4、后来根据报错显示:

Error attempting to get column ‘id’ from result set.

也就是说在resultset中的id字段是存在问题的

我们找到了mybatis对接数据库,进行查询接收数据的流程如下,从建立连接到取回结果集,最终会拿到一个resultset

在这里插入图片描述

5、当我们拿到了resultset,接下来我们要做的肯定是和接收结果的对象类来做映射,问题呢就出现在这个地方,我们正好回顾一下取结果的过程。

→数据库在执行query方法的过程中,最后会去拿handleResultSets的结果,执行的过程我标红处理

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      // 这里处理多结果集的嵌套映射,不分析
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        // 如果用户没有指定对结果的处理器ResultHandler,那么默认会使用DefaultResultHandler
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        // 对结果集进行转换
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        // 将解析后的结果添加到multiResults中
        // 如果没有指定ResultHandler,那么默认会将解析之后的结果添加到multipleResults中
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        // 用户定义了对结果集的处理方法,即ResultHandler
        // 那么使用ResultSetHandler处理之后,会将结果再交给ResultHandler进行处理
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    // issue #228 (close resultsets)
    closeResultSet(rsw.getResultSet());
  }
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  // 判断是否有嵌套的映射
  if (resultMap.hasNestedResultMaps()) {
    ensureNoRowBounds();
    checkResultHandler();
    // 处理嵌套映射(rsw是取回的结果集,resultmap是接收的对象类)
    handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {
    // 处理简单映射
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}


private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
  // ResultContext用来存放结果对象
  DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
  // 获取jdbc查询结果集
  ResultSet resultSet = rsw.getResultSet();
  // 取出rowbounds中的offset,跳过结果集中的前面offset行
  skipRows(resultSet, rowBounds);
  // 判断是否需要接着处理,shouldProcessMoreRows判断当前处理的行数是否已经超过了rowbounds中limit指定的行数
  while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
    Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
    storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  // 通过构造方法来创建结果对象,可能会用到构造方法参数映射
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  // 判断结果值是否为空,并且没有对应当前结果java类型的typehandler
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    // 判断是否使用了我们定义的映射
    boolean foundValues = this.useConstructorMappings;
    // 判断是否需要使用自动映射,来设置没有映射对应的字段
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 对其他resultMapping处理
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  // 用来判断是否使用到了构造方法参数映射
  this.useConstructorMappings = false; // reset previous mapping result
  final List<Class<?>> constructorArgTypes = new ArrayList<>();
  final List<Object> constructorArgs = new ArrayList<>();
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  // 当前结果不为空,并且不存在可以直接将ResultSet转换为指定java类型的typeHandler
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      // issue gcode #109 && issue #149
      // 懒加载,单独一篇文章来分析
      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
        resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        break;
      }
    }
  }
  // 如果结果对象不为空,并且构造方法使用到了构造参数映射,那么将useConstructorMappings设置为true
  this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
  return resultObject;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
      throws SQLException {
  final Class<?> resultType = resultMap.getType();
  final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
  // 取出构造函数参数的映射,就是FLAG为CONSTRUCTOR的映射
  final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
  if (hasTypeHandlerForResultObject(rsw, resultType)) {
    // 如果符合当前java结果类型的TypeHandler,那么会使用typehandler来对结果集进行处理
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {
    // 如果指定了构造参数映射,使用构造参数映射来进行构造
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
    // 如果是结果或者有默认构造函数,那么直接通过ObjectFactory来创建
    return objectFactory.create(resultType);
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {
    // 判断是否开启了自动映射
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

6、问题主要出现在这里!!

我们可以看到最后返回接收对象的结果类,是使用了构造函数来进行构造的,可是按照我们的对象类正常是无参构造才对,因为我们只用了@Data注解,正常情况是没有构造函数的,但是这里却走进了默认的构造函数。问题出现如下:

    private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
        //获取所有声明的构造函数
        Constructor<?>[] constructors = resultType.getDeclaredConstructors();
        //查找默认的构造函数
        Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
         //如果找到了默认的构造函数,则调用该构造函数进行对象的创建
        if (defaultConstructor != null) {
            return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
        } else {
        //如果没有找到默认的构造函数,则需要根据结果集中jdbcTypes集合的内容判断是否可以使用构造函数创建对象
            Constructor[] var7 = constructors;
            int var8 = constructors.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Constructor<?> constructor = var7[var9];
                if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
                    return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
                }
            }

            throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
        }
    }

在这里插入图片描述

我们发现拿回来的构造函数是带有一个integer类型的,而mybatis对于构造函数的结果处理如下,其中会拿出构造函数的属性,那么拿到的会是一个integer,而根据取回的rsw所拿到的字段,第一个拿到的colunname是id,所以我们到现在明白了第一个问题 —> mybatis会根据构造函数进行一一对应,这个id对应的integer类型是在这里拿到的,那么问题回到了这个构造函数为什么会带一个integer呢???

在这里插入图片描述

我们发现这个integer是通过上面resultType.getDeclaredConstructors();拿到的,那么我们做了一个测试,就是脱离mybatis,单从jdk的角度确认他反射出来的对象的构造方法是什么样子的。

    @Test
    public void text() {
        Class a = AutoPullGroupConfigVo.class;
        Constructor<?>[] constructors = a.getDeclaredConstructors();
    }

我们拿到的这个构造方法如下,发现这个构造方法也是带有Integer的,也就是说明他和mybatis无关,我们注意到这个对象类有一个特别之处在于使用了@NotNull这个注解,我们测试了一下去掉NotNull注解

在这里插入图片描述

下面是去掉NotNull注解的情况,发现变成了无参构造,那么问题就出现在了NotNull注解上

在这里插入图片描述

由此我们知道了,在mybatis结果集映射中,因为受到构造函数的影响,造成了值的对应错误,同时notNull注解会更改构造函数的结构,处理方法我们可以增加@NoArgsConstructor 和@AllArgsConstructor变为有参和无参构造,但是请注意,这样会使notNull的单参数构造失效,以上就是本次错误的排查总结过程。

附:我又测试了单全参注解,和单无参注解,得到的结果是:只有全参构造也报错,因为需要字段与类参数一一对应,无参构造则不走这个对应规则,只有无参不报错,这个以后继续来看mybatis的映射规则吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mybatis报错Error attempting to get column ‘id‘ from result set. Cause: org.postgresql.util.PSQLExcept 的相关文章

随机推荐

  • 小记:Xshell 简单STP上传下载文件

    小记 xff1a Xshell 简单STP上传下载文件 目录 xff1a 1 Xshell连接远程系统 2 STP连接 3 put get上传下载文件 工具说明 xff1a 本地Windows 10系统 远程Linux系统 xff1a Ce
  • info函数与describe函数

    info 函数与describe 函数 一 xff0c info 函数 功能 xff1a 给出样本数据的相关信息概览 xff1a 行数 xff0c 列数 xff0c 列索引 xff0c 列非空值个数 xff0c 列类型 xff0c 内存占用
  • 快速排序(java代码)

    文章目录 一 快排核心思想二 实例三 代码实现 xff08 java xff09 一 快排核心思想 快排核心思想就是 xff1a 首先在待排序数组中随便选择一个数作为节点 xff08 pivot xff09 xff0c 然后从最后面 xff
  • C# List集合查找删除指定数据

    C List集合查找删除指定数据 文章目录1 实体类2 操作第一个负荷条件数据3 操作所有符合条件数据4 优质源码 文章目录 1 实体类 public class FaultLevelModel public string LBWJ get
  • java.lang.IllegalArgumentException: Unable to instantiate factory class

    前提 xff1a 这两天在搭springboot后台框架的时候 xff08 用的JDK1 8和eclipse xff09 xff0c 折腾了半天 xff0c 在搞泛型的时候会有一些报错 xff0c 原来IDEA上的代码 xff0c 在ecl
  • 发声单元的原理/扬声器的工作原理

    目前市面上常见的耳塞以及耳机的发声单元主要有三种 xff0c 分别是动圈单元 动铁单元和静电单元 静电单元由于工艺比较复杂 成本高 xff0c 单元本身比较脆弱 xff0c 所以市面上并不多见 所以下面着重介绍动圈单元和动铁单元的发声原理
  • 猿创征文|Hexo+Github搭建完全免费个人博客详细教程

    前言 完全免费的搭建个人博客 xff0c 没有任何收费 xff0c 零基础也能上手 xff0c 不需要编程基础 xff0c 跟着操作来即可 首先 xff1a 要了解一下我们搭建博客要用到的框架 xff1a Hexo是高效的静态站点生成框架
  • 3维向量的点乘叉乘运算

    目录 三维向量的点乘三维向量的叉乘点到直线的距离点到平面的距离 三维向量的点乘 点乘得到的是对应元素乘积的和 xff0c 是一个标量 xff0c 没有方向 V1 x1 y1 z1 V2 x2 y2 z2 61 x1x2 43 y1y2 43
  • Windows Server 2016修改Administrator的密码

    1 xff09 在仪表盘上 xff0c 点击 工具 菜单 xff0c 选择 计算机管理 子菜单 2 xff09 本地用户和组 gt 用户 选中Administrator xff0c 点击右键 xff0c 弹出的菜单中选择 设置密码 的子菜单
  • Ubuntu安装Tomcat 执行[./startup.sh]命令显示“ Permission denied “问题解决

    报错贴图 xff1a 根据错误提示 xff0c 为权限问题 然后我们检查权限情况 xff1a 方框中为管理员权限 xff0c 显示为rw r代表read xff08 读 xff09 xff0c w代表write xff08 写 xff09
  • 2021年7月1日:AndroidStudio集成opencv指南。

    版本声明 xff1a 1 AndroidStudio 4 2 1 2 opencv版本4 5 2 3 androidStudio创建项目选择项目类型Native C 43 43 4 下载opencv android版本解压后 xff0c 找
  • Redis(十) 布隆过滤器

    速记 为什么使用布隆过滤器 xff1f 1 为了省内存 xff0c 提高速率 2 因为1所以布隆过滤器不需要百分百正确 3 说存在不一定存在 xff0c 说不存在一定不存在 4 在解决缓存穿透的问题时 xff0c 拦截了大部分的请求 xff
  • Docker学习笔记(九)---DockerFile

    DockerFile 文章目录 DockerFileDockerFile介绍DockerFile构建过程基础知识 DockerFile的指令实战测试CMD 和 ENTRYPOINT 区别 Docker其他学习笔记 DockerFile介绍
  • 【使用multipart/form-data方式传递MultipartFile参数,实现服务间文件的传递】

    目录 一 代码实现二 MultipartFile工具类三 HttpClient使用四 参考链接 一 代码实现 1 A服务接收前端上传文件并发送至B服务 引入依赖 lt dependency gt lt groupId gt org apac
  • JSP中JavaBean的应用:计算三角形或者梯形的面积

    计算三角形或者梯形的面积 1 jsp页面的编写 页面部分包含一个表单 xff0c 这个表单有一个提供选择的图形的下拉列表 xff0c 三个输入框分别输入三角形的三条边或者是梯形的上底 下底和高 xff0c 一个计算的提交按钮 在页面中还应该
  • ubuntu22虚拟机设置中文(亲测有效)

    1进入 虚拟机 找到设置 2 进入设置找到区域和语言并切换为Chinese xff0c 再点击select 3 再点击管理已安装语言 xff0c 弹框再点击添加或删除语言 xff08 记得点击应用到整个系统 xff09 4 在里面找到中文语
  • JDK环境配置

    JDK环境配置 xff08 注 xff1a 安装jdk和jre的时候把两个文件夹都放在同一目录下 xff0c 如 xff1a 放在 D Program Files Java xff09 1 右键点击此电脑 xff0c 选择属性 xff0c
  • 复合语句和流程控制

    复合语句 在MariaDB 10 1 1 43 版本中 xff0c 我们可以在存储过程以外来使用复合语句了 xff0c 顾名思义 xff0c 复合语句就是将多条语句作为一个整体来执行 xff0c 可以在其中使用一些逻辑判断 xff0c 循环
  • C++函数篇之求某个范围中素数的和

    1 案例 输入两个正整数m和n xff0c 求m到n之间 xff08 包括m和n xff09 所有素数的和 要求定义并调用 函数isprime xff08 x xff09 来判断x是否为素数 2 思路 主函数编写 函数的编写 xff1a 素
  • mybatis报错Error attempting to get column ‘id‘ from result set. Cause: org.postgresql.util.PSQLExcept

    mybatis报错Error attempting to get column id from result set Cause org postgresql util PSQLException Bad value for type in