【问题定位】通过看Mybatis源码解决系统问题

news2024/9/20 18:41:57

开发需求好好的,运维同事突然发现了一个问题,某个任务的详情页面加载不出来。看日志,系统在进行查询操作的时候抛出空指针异常。感觉是Mybatis内部异常,所以就跟踪源码看下Mybatis运行到哪一步报错的。

DefaultSqlSession#selectOne(java.lang.String, java.lang.Object),空指针异常的地方是从DefaultSqlSession报出来的,由于查询结果返回null,导致调用size方法报错。

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

BaseExecutor#query()BaseExecutor的query方法是Mybatis执行查询sql的必经之路,这边有一级缓存和二级缓存的知识点。

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

SimpleExecutor#doQuery,这个方法有两个重要的方法调用,prepareStatement()主要是给预编译的sql设置参数,handler.query(stmt, resultHandler);是获取数据,并进行对象封装。

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

DefaultResultSetHandler#handleResultSets,将查询出来的数据集合挨个解析。

  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

DefaultResultSetHandler#applyPropertyMappings,该方法是将挨个数据库中的列数据映射到属性中。当系统遍历到日期字段,end的时候,该方法报错,跳出。

  private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.getNestedResultMapId() != null) {
        // the user added a column attribute to a nested result map, ignore it
        column = null;
      }
      if (propertyMapping.isCompositeResult()
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
          || propertyMapping.getResultSet() != null) {
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue #541 make property optional
        final String property = propertyMapping.getProperty();
        if (property == null) {
          continue;
        } else if (value == DEFERRED) {
          foundValues = true;
          continue;
        }
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
          // gcode issue #377, call setter on nulls (value is not 'found')
          metaObject.setValue(property, value);
        }
      }
    }
    return foundValues;
  }

因此发现该行数据的end字段有问题,用Datagrip查询,该字段的数据是2023-11-31 23:59:59,而2023年11月份是没有31号的,所以mybatis赋值会报错。mysql允许非法的日期格式插入,但是mybatis对于这非法的数据处理是处理不了的。

总结:

  1. mybatis查询的核心流程就是解析sql,执行sql,封装对象。空指针异常不是对象为空,可能是mybatis在数据处理的过程中有异常。
  2. 注意数据插入的合法性。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1238499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二、Gitee使用方法

目录 &#xff08;1&#xff09;首先可以注册一个 gitee 账号&#xff0c;注册很方便&#xff0c;自行注册 &#xff08;2&#xff09;登陆后进入你的主页 &#xff08;3&#xff09;创建仓库 &#xff08;3&#xff09;克隆 &#xff08;4&#xff09;代码提交 &#xf…

SQLite3 数据库学习(四):Qt 数据库基础操作

参考引用 SQLite 权威指南&#xff08;第二版&#xff09;SQLite3 入门 1. 创建连接执行 sql 语句 在 Qt 中使用数据库要在工程文件中添加QT sql1.1 main.cpp #include "createsqlapp.h" #include <QApplication> #include <QSqlDatabase> #include &l…

vite构建项目不能使用require解决方案

在utils文件夹下创建一个getImgUrl.ts文件 /** vite的特殊性, 需要处理图片 */ export const require (imgPath: string) > {try {const handlePath imgPath.replace(, ..)console.log(handlePath::, imgPath)return new URL(handlePath, import.meta.url).href} catch (…

微信怎么设置自动回复?

自动回复的用处 微信自动回复可以提高沟通效率。当你无法立即回复消息时&#xff0c;设置自动回复可以让对方知道你的情况&#xff0c;并且不会因为长时间没有回复而产生误解或不满。 微信自动回复可以节省时间和精力。如果你经常收到类似的询问或回复&#xff0c;通过设置自动…

Linux常用操作 Vim一般使用 SSH介绍 SSH密钥登录

目录 1. 常用命令 2. vim一般使用 3. SSH介绍 4. ssh密钥登录 1. 常用命令 1&#xff09;# 与 $ 提示的区别 # 表示用户有root权限&#xff0c;一般的以root用户登录提示符为#&#xff0c; $提示符表示用户为普通用户 2&#xff09;ifconfig 查看ip地址 eno1: 代表由主板…

【C++11并发】future库 笔记

简介 C11之前&#xff0c;主线程要想获取子线程的返回值&#xff0c;一般都是通过全局变量&#xff0c;或者类似机制。C11开始为我们提供了一组方法来获取子线程的返回值&#xff0c;并保证其原子性。 头文件 #include <future>std::promise 在promise中保存了一个值…

【github】初学者使用指南

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于新西兰奥克兰大学攻读IT硕士学位。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。跨领域…

DependencyProperty.Register:wpf 向别的xaml传递参数

一.使用背景&#xff1a;在A.xaml中嵌入B.xaml&#xff0c;并且向B.xaml传递参数。 函数介绍&#xff1a; public static DependencyProperty Register(string name, Type propertyType, Type ownerType );name&#xff08;string&#xff09;&#xff1a; 依赖属性的名称。在…

2023 IDEA大会开幕 共探AI新篇章下的技术创新与创业

11月22日&#xff0c;AI与数字经济领域一年一度的科创盛会&#xff0c;2023 IDEA大会在深圳举行。IDEA研究院创院理事长、美国国家工程院外籍院士沈向洋在会上发表主旨演讲&#xff0c;发布IDEA研究院的重磅研产结晶与市场化成果&#xff1b;在大咖云集的论坛环节&#xff0c;多…

【大数据Hive】hive 优化策略之job任务优化

目录 一、前言 二、hive执行计划 2.1 hive explain简介 2.1.1 语法格式 2.1.2 查询计划阶段说明 2.2 操作演示 2.2.1 不加条件的查询计划分析 2.2.2 带条件的查询计划分析 三、MapReduce属性优化 3.1 本地模式 3.1.1 本地模式参数设置 3.1.2 本地模式操作演示 3.2 …

Oracle:poor sql导致的latch: cache buffers chains案例

巡检时&#xff0c;执行如下sql发现长会话&#xff1a; SELECT SE.SID,SE.SERIAL#,TO_CHAR(LOGON_TIME,YYYY-MM-DD HH24:MI:SS),SE.STATUS,SE.OSUSER,SE.MACHINE,SE.PROGRAM,SE.BLOCKING_SESSION, SE.SQL_ID,SE.PREV_SQL_ID ,SE.EVENT,SE.P1TEXT,SE.P1,SE.P2TEXT,SE.P2,SE.P3…

神经网络中BN层简介及位置分析

1. 简介 Batch Normalization是深度学习中常用的技巧&#xff0c;Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift (Ioffe and Szegedy, 2015) 第一次介绍了这个方法。 这个方法的命名&#xff0c;明明是Standardization, 非…

PowerQuery领域的经典之作“猴子书“中文版来啦!

与数据打交道&#xff0c;还在纠结于Excel、SQL、VBA、Python&#xff1f;数据处理领域经典之作PowerQuery"猴子书"让你用更聪明的方法处理数据。学完这本书&#xff0c;你就掌握了Power Query的一切&#xff0c;想要学Power Query&#xff0c;只需要这一本就够啦&am…

提升企业人效,从精细化考勤管理开始

过去&#xff0c;许多企业提到考勤管理&#xff0c;只能关联到打卡、请假、算薪这些简单的事务性流程。随着越来越多企业希望通过数字化转型来提升运营效率&#xff0c;实现精细化人员管理。考勤数据的作用也不再仅限于算薪&#xff0c;而是成为了企业分析人效的关键因子。因此…

飞瓜数据B站丨B站UP主11月第3周榜单排行榜榜单(B站平台)发布!

飞瓜轻数发布2023年11月13日-11月19日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数、带货数据等维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营…

LangChain: 类似 Flask/FastAPI 之于 Django,LangServe 就是「LangChain 自己的 FastAPI」

原文&#xff1a;LangChain: 类似 Flask/FastAPI 之于 Django&#xff0c;LangServe 就是「LangChain 自己的 FastAPI」 - 知乎 说明&#xff1a;LangServe代替 langchainserver 成为新的langchain 部署工具 官网资料&#xff1a;&#x1f99c;️&#x1f3d3; LangServe | &…

智慧物流仓储仓库温湿度管理采集器钡铼技术远程终端RTU的使用

智慧物流仓储是当今物流行业的一个重要发展方向&#xff0c;它通过应用先进的技术和设备&#xff0c;实现对仓储环境的监控和管理。在智慧物流仓储中&#xff0c;温湿度管理是十分关键的一项工作。为了解决温湿度管理的问题&#xff0c;采集器钡铼技术远程终端RTU被广泛应用于仓…

未来制造业的新引擎:工业机器人控制解决方案

制造业正经历着一场革命性的变革 在这个变革的浪潮中&#xff0c;工业机器人成为推动制造业高效生产的关键力量。然而&#xff0c;要发挥机器人的最大潜力&#xff0c;一个强大而智能的控制系统是必不可少的。在这个领域&#xff0c;新一代的工业机器人控制解决方案正崭露头角&…

Linux:进度条(小程序)以及git三板斧

Linux小程序&#xff1a;进度条 在实现小程序前我们要弄清楚&#xff1a; 1.缓冲区&#xff1b; 2.回车与换行。 缓冲区&#xff1a; 分别用gcc来编译下面两个程序&#xff1a; 程序一&#xff1a; #include <stdio.h> int main() { printf("hello Makefil…