spring boot mybatis TypeHandler 看源码如何初始化及调用
目录
- 概述
- 使用
- TypeHandler
- 使用方式
- 在 select | update | insert 中加入
- 配置文件中指定
- 源码分析
- 配置文件指定
- Mapper
- 执行query
- 如何转换
- 结束
概述
阅读此文 可以达到
spring boot mybatis TypeHandler源码如何初始化及如何调用的。
spring boot 版本为 2.7.17,mybatis(spring boot) 2.3.2
TypeHandler 是 mybatis 中 java 对象和数据库 jdbc 之间进行类型转换的桥梁。
调用层级深,比较复杂,关键源码点,给出了详细的位置,打上断点,降低难度。
接口源码如下:
public interface TypeHandler<T> {// 从java对象到数据库类型的转换void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;// 下面三个是从数据库到java对象的转换T getResult(ResultSet rs, String columnName) throws SQLException;T getResult(ResultSet rs, int columnIndex) throws SQLException;T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
使用
TypeHandler
实现 TypeHandler 接口,也可以实现抽象类 BaseTypeHandler
代码如下:
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(Reason.class)
public class ResonJsonTypeHandler extends BaseTypeHandler<Reason> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Reason parameter, JdbcType jdbcType) throws SQLException {// 将java对象转换成jdbc中的类型ps.setString(i, JSON.toJSONString(parameter));}@Overridepublic Reason getNullableResult(ResultSet rs, String columnName) throws SQLException {return JSON.parseObject(rs.getString(columnName), Reason.class);}@Overridepublic Reason getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return JSON.parseObject(rs.getString(columnIndex), Reason.class);}@Overridepublic Reason getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return JSON.parseObject(cs.getString(columnIndex), Reason.class);}
}
注意:
- @MappedJdbcTypes 代表对应的数据库中字段的类型,json本质上也是字符串
- @MappedTypes 代表要转换的 JavaBean 对象
- java对象与json格式的互相转换需要 JSON 来完成
使用方式
在 select | update | insert 中加入
写在 xml 只有对应的 sql 会被转换

org.apache.ibatis.mapping.ResultMapping.Builder#resolveTypeHandler
配置文件中指定
如下配置,任何使用 Reason 的地方都会进行转换。
mybatis:type-handlers-package: com.fun.demo.mybatis.type.handler
由于 xml 与 mapper 都是会生成覆盖,所以选择配置文件中指定。
源码分析
配置文件指定
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {final Configuration targetConfiguration;XMLConfigBuilder xmlConfigBuilder = null;
....if (hasLength(this.typeHandlersPackage)) {scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers())).forEach(targetConfiguration.getTypeHandlerRegistry()::register);}if (!isEmpty(this.typeHandlers)) {Stream.of(this.typeHandlers).forEach(typeHandler -> {targetConfiguration.getTypeHandlerRegistry().register(typeHandler);LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");});}....
scanClasses 方法进行加载自定义的 typeHandler
org.mybatis.spring.SqlSessionFactoryBean#scanClasses

org.apache.ibatis.mapping.ResultMapping.Builder#resolveTypeHandler

Mapper
在 Mapper 中自定义时,依然也是在 SqlSessionFactoryBean 的 buildSqlSessionFactory 方法。
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration#sqlSessionFactory
org.mybatis.spring.SqlSessionFactoryBean#afterPropertiesSet
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement
org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElements
org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode)
org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode, java.util.List<org.apache.ibatis.mapping.ResultMapping>, java.lang.Class<?>)
org.apache.ibatis.builder.MapperBuilderAssistant#buildResultMapping(java.lang.Class<?>, java.lang.String, java.lang.String, java.lang.Class<?>, org.apache.ibatis.type.JdbcType, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Class<? extends org.apache.ibatis.type.TypeHandler<?>>, java.util.List<org.apache.ibatis.mapping.ResultFlag>, java.lang.String, java.lang.String, boolean)
org.apache.ibatis.builder.BaseBuilder#resolveTypeHandler(java.lang.Class<?>, java.lang.Class<? extends org.apache.ibatis.type.TypeHandler<?>>)
public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();
}
具体的解析 /mapper 元素
private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.isEmpty()) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);}
}
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings,Class<?> enclosingType) {
.......List<XNode> resultChildren = resultMapNode.getChildren();for (XNode resultChild : resultChildren) {if ("constructor".equals(resultChild.getName())) {processConstructorElement(resultChild, typeClass, resultMappings);} else if ("discriminator".equals(resultChild.getName())) {discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);} else {List<ResultFlag> flags = new ArrayList<>();if ("id".equals(resultChild.getName())) {flags.add(ResultFlag.ID);}resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));}}
.....}

buildResultMappingFromContext
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) {String property;if (flags.contains(ResultFlag.CONSTRUCTOR)) {property = context.getStringAttribute("name");} else {property = context.getStringAttribute("property");}......String nestedResultMap = context.getStringAttribute("resultMap",() -> processNestedResultMappings(context, Collections.emptyList(), resultType));....String typeHandler = context.getStringAttribute("typeHandler");String resultSet = context.getStringAttribute("resultSet");String foreignColumn = context.getStringAttribute("foreignColumn");.....}
String typeHandler = context.getStringAttribute(“typeHandler”);
执行query
如何转换
org.apache.ibatis.executor.SimpleExecutor#doQuery

org.apache.ibatis.session.Configuration#newStatementHandler
org.apache.ibatis.executor.statement.RoutingStatementHandler#RoutingStatementHandler
org.apache.ibatis.executor.statement.PreparedStatementHandler
org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler
org.apache.ibatis.executor.statement.RoutingStatementHandler#query
org.apache.ibatis.logging.jdbc.PreparedStatementLogger#invoke
org.apache.ibatis.executor.resultset.ResultSetHandler#handleResultSets
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSet
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValues
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyPropertyMappings
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getPropertyMappingValue
com.fun.demo.mybatis.type.handler.ResonJsonTypeHandler#getNullableResult(java.sql.ResultSet, java.lang.String)

执行结果

结束
spring boot mybatis TypeHandler 如何初始化及如何调用,就解释的非常明白了,如有疑问,欢迎评论区留言。
相关文章:
spring boot mybatis TypeHandler 看源码如何初始化及调用
目录 概述使用TypeHandler使用方式在 select | update | insert 中加入 配置文件中指定 源码分析配置文件指定Mapper 执行query如何转换 结束 概述 阅读此文 可以达到 spring boot mybatis TypeHandler 源码如何初始化及如何调用的。 spring boot 版本为 2.7.17,my…...
数据结构基础(带头节点的双向循环链表)
完整代码 DLinkList.hDLinkList.ctest.c DLinkList.h #pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h>typedef int ElemType;// SList - 单链表 // DList - 双链表 // 带头节点的双向循环链表 - 最优链表结构,任意位置…...
STM32CubeMx+MATLAB Simulink点灯程序
STM32CubeMxMATLAB点灯程序 ✨要想实现在MATLAB Simulink环境下使用STM32,前提是已经搭建好MATLAB环境并且安装了必要的Simulink插件,以及对应的STM32支持包。 🌿需要准备一块所安装支持包支持的STM32开发板. 🔖具体支持包详情页…...
【深度学习】gan网络原理生成对抗网络
【深度学习】gan网络原理生成对抗网络 GAN的基本思想源自博弈论你的二人零和博弈,由一个生成器和一个判别器构成,通过对抗学习的方式训练,目的是估测数据样本的潜在分布并生成新的数据样本。 1.下载数据并对数据进行规范 transform tran…...
springboot参数汇总
multipart multipart.enabled 开启上传支持(默认:true) multipart.file-size-threshold: 大于该值的文件会被写到磁盘上 multipart.location 上传文件存放位置 multipart.max-file-size最大文件大小 multipart.max-request-size 最大请求…...
【算法刷题】Day9
文章目录 611. 有效三角形的个数题干:题解:代码: LCR 179. 查找总价格为目标值的两个商品题干:题解:代码: 1137. 第 N 个泰波那契数题干:原理:1、状态表示(dp表里面的值所…...
LangChain的函数,工具和代理(三):LangChain中轻松实现OpenAI函数调用
在我之前写的两篇博客中:OpenAI的函数调用,LangChain的表达式语言(LCEL)中介绍了如何利用openai的api来实现函数调用功能,以及在langchain中如何实现openai的函数调用功能,在这两篇博客中,我们都需要手动去创建一个结构比较复杂的函数描述变量…...
WiFi概念介绍
WiFi概念介绍 1. 什么是WLAN2. 什么是Wi-Fi3. Wi-Fi联盟4. WLAN定义范围5. WiFi协议体系6. 协议架构7. WiFi技术的发展7.1 IEEE802.117.2 802.11标准和补充 8. 术语 1. 什么是WLAN Wireless Local Area Network,采用802.11无线技术进行互连的一组计算机和相关设备。…...
如何优雅的进行业务分层
1.什么是应用分层 说起应用分层,大部分人都会认为这个不是很简单嘛 就controller,service, mapper三层。 看起来简单,很多人其实并没有把他们职责划分开,在很多代码中,controller做的逻辑比service还多,service往往当…...
C++的std命名空间
总以为自己懂了,可是仔细想想,多问自己几个问题,发现好像又不是很清楚 命名空间(Namespace)是C中一种用于解决命名冲突问题的机制,它能够将全局作用域划分为若干个不同的区域,每个区域内可以有…...
unity学习笔记
一、射线检测 如何让鼠标点击某个位置,游戏角色就能移动到该位置? 实现的原理分析:我们能看见游戏的东西就是摄像机拍摄到的东西,所以摄像机的镜平面就是当前能看到的了。 那接下来我们可以让摄像机发射一条射线,鼠标…...
使用SpringBoot和ZXing实现二维码生成与解析
一、ZXing简介 ZXing是一个开源的,用Java实现的多种格式的1D/2D条码图像处理库。它包含了用于解析多种格式的1D/2D条形码的工具类,目标是能够对QR编码,Data Matrix, UPC的1D条形码进行解码。在二维码编制上,ZXing巧妙地利用构成计…...
C++模板—函数模板、类模板
目录 一、函数模板 1、概念 2、格式 3、实例化 4、模板参数的匹配 二、类模板 1、定义格式 2、实例化 交换两个变量的值,针对不同类型,我们可以使用函数重载实现。 void Swap(double& left, double& right) {double tmp left;left ri…...
Monkey
一、Monkey的概念 “猴子测试”是指没有测试经验的人甚至对计算机根本不了解的人(就像猴子一样)不需要知道程序的任何用户交互方面的知识,如果给他一个程序,他就会针对他看到的界面进行操作,其操作是无目的的、乱点乱按…...
SQL中left join、right join、inner join等的区别
一张图可以简洁明了的理解出left join、right join、join、inner join的区别: 1、left join 就是“左连接”,表1左连接表2,以左为主,表示以表1为主,关联上表2的数据,查出来的结果显示左边的所有数据&#…...
算法学习—排序
排序算法 一、选择排序 1.算法简介 选择排序是一个简单直观的排序方法,它的工作原理很简单,首先从未排序序列中找到最大的元素,放到已排序序列的末尾,重复上述步骤,直到所有元素排序完毕。 2.算法描述 1ÿ…...
在Pycharm中创建项目新环境,安装Pytorch
在python项目中,很多项目使用的各类包的版本是不一致的。所以我们可以对每个项目有专属于它的环境。所以这个文章就是教你如何创建新环境。 一、创建新环境 首先我们需要去官网下载conda。然后在Pycharm下面添加conda的可执行文件。 用conda创建新环境。 二、…...
linux里source、sh、bash、./有什么区别
1、source source a.sh 在当前shell内去读取、执行a.sh,而a.sh不需要有"执行权限" source命令可以简写为"." . a.sh 注意:中间是有空格的。 2、sh/bash sh a.sh bash a.sh 都是打开一个subshell去读取、执行a.sh,而a.…...
IDEA编译器技巧-提示词忽略大小写
IDEA编译器技巧-提示词忽略大小写 写代码时,每次创建对象都要按住 Shift 字母 做大写开头, 废手, 下面通过编译器配置解放Shift 键 setting -> Editor -> General -> Code Completion -> Match case 把这个√去掉, 创建对象就不需要再按住 Shift 键 示例: 1.…...
【MySQL】MySQL安装 环境初始化
MySQL安装 MYSQL官网 安装完成后,傻瓜下一步即可 配置一下环境变量即可 (1) 初始化MySQL, 管理员身份运行 mysqld --initialize-insecure(2) 注册 mysqld mysqld -install# 如果记录以前的版本执行下面指令 mysqld -remove(3) 启动MySQL服务 // 启动mysql服务 net start …...
机器标识重置技术实现的Cursor Pro功能解锁解决方案
机器标识重置技术实现的Cursor Pro功能解锁解决方案 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your trial request li…...
3个核心模块揭秘:Python量化投资如何免费获取通达信专业数据
3个核心模块揭秘:Python量化投资如何免费获取通达信专业数据 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx 你是否在量化投资中为数据获取而烦恼?商业接口太贵,…...
ClickHouse数据报表实战:如何把分组后的明细‘压缩’成一行摘要(附完整SQL)
ClickHouse数据报表实战:高效聚合多行文本的工程化解决方案 在数据分析与报表生成的实际业务场景中,我们经常遇到这样的需求:需要将同一维度下的多条文本明细(如用户行为日志、错误信息、月份列表等)合并成一条简洁的摘…...
如何快速配置TranslucentTB:Windows任务栏美化终极教程
如何快速配置TranslucentTB:Windows任务栏美化终极教程 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 想要让Windows任务栏变…...
ContextMenuManager:让Windows交互回归高效本质
ContextMenuManager:让Windows交互回归高效本质 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 当你在Windows系统中右键点击文件时,是否…...
PyTorch 2.8镜像法律科技:庭审音视频→AI摘要+关键帧提取+证据链可视化
PyTorch 2.8镜像法律科技:庭审音视频→AI摘要关键帧提取证据链可视化 1. 镜像概述与法律科技应用场景 在司法数字化进程中,庭审音视频处理正面临三大挑战:海量数据难以快速消化、关键信息提取效率低下、证据呈现方式单一。PyTorch 2.8深度学…...
PyTorch 2.8镜像部署教程:RTX 4090D配置htop实时监控GPU/CPU/内存使用
PyTorch 2.8镜像部署教程:RTX 4090D配置htop实时监控GPU/CPU/内存使用 1. 环境准备与快速部署 在开始之前,请确保您的硬件配置满足以下要求: 显卡:RTX 4090D 24GB显存内存:120GB及以上存储:系统盘50GB …...
告别手动重复!用Python+ArcPy实现多要素批量裁剪年度影像的保姆级教程
PythonArcPy自动化遥感影像裁剪:从原理到实战的完整解决方案 遥感影像处理是GIS工程师的日常必修课。每当拿到新一年的土地利用数据或行政区划影像时,最头疼的莫过于要为每个行政单元单独裁剪每年的数据。我曾花费整整一周时间手动处理30个乡镇5年的NDVI…...
深入理解 MySQL 事务:从基础到实战,一篇吃透
在开发和运维 MySQL 数据库的过程中,事务(Transaction) 是绕不开的核心知识点,它是保证数据库数据安全、一致、可靠的基石。无论是电商下单、银行转账、支付结算,还是日常的业务数据操作,都离不开事务的支撑…...
SiameseUIE多任务统一Schema设计:一套定义覆盖NER/关系/事件/情感
SiameseUIE多任务统一Schema设计:一套定义覆盖NER/关系/事件/情感 1. 引言:信息抽取的“瑞士军刀” 想象一下,你手头有一堆杂乱无章的中文文档——可能是新闻稿、用户评论、技术报告或者客服对话。老板让你快速从中找出所有提到的人名、公司…...
