news 2026/4/17 4:12:30

MyBatis体系结构与工作原理 下篇

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MyBatis体系结构与工作原理 下篇

能力有限,只能粗看

核心处理层

ORM

反射模块

如何看

public class Reflector {}
每一个Reflector对应一个java类

简化反射操作

ReflectorFactory-创建Reflector

Reflector 缓存

测试

反射的invoker

metaclass

争对复杂表达式操作

metaObject

争对对象表达式解析操作

从mybatis角度看看

Configuration
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();

反射selectlist返回值处理

不能细看,细看太复杂

​ private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { // 创建延迟加载器,用于懒加载关联属性 final ResultLoaderMap lazyLoader = new ResultLoaderMap(); ​ // 根据 ResultMap 创建当前行对应的对象 Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); ​ // 如果对象不为空且没有类型处理器 if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { ​ // 创建 MetaObject,用于操作对象属性 反射内的包 final MetaObject metaObject = configuration.newMetaObject(rowValue); ​ // 初始化标记,记录是否通过构造函数填充了值 boolean foundValues = this.useConstructorMappings; ​ // 判断是否需要自动映射未在 ResultMap 中指定的列 if (shouldApplyAutomaticMappings(resultMap, false)) { ​ // 自动映射,映射ResultMAP 的列 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } ​ // 按 ResultMap 映射列到对象属性 foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; ​ // 如果有延迟加载属性,也认为找到了值 foundValues = lazyLoader.size() > 0 || foundValues; ​ // 如果没有值且配置不返回空对象,则置为 null rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } ​ // 返回当前行映射得到的对象 return rowValue; } ​

类型转化模块

BaseType 空处理,具体还是给子类

​ public class ZdyTypeHandler extends BaseTypeHandler<String> { /// 插入回调 @Override public void setNonNullParameter(PreparedStatement ps, int i, String s, JdbcType jdbcType) throws SQLException { ps.setString(i, "666" + s); // 写入数据库时加前缀 } ​ @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { String value = rs.getString(columnName); return value != null ? "666" + value : null; // 读出时加前缀 } ​ @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String value = rs.getString(columnIndex); return value != null ? "666" + value : null; } ​ @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String value = cs.getString(columnIndex); return value != null ? "666" + value : null; } } ​

调试

public BaseBuilder(Configuration configuration) { ​ // 接收 MyBatis 全局配置对象 Configuration this.configuration = configuration; // 从 Configuration 中获取类型别名注册器 TypeAliasRegistry // 用于管理 <typeAliases> 中配置的别名,例如 User -> com.xxx.User this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); // 从 Configuration 中获取类型处理器注册器 TypeHandlerRegistry // 用于管理 Java 类型与 JDBC 类型之间的转换处理器,例如 StringTypeHandler this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); } ​

执行过程

当 JDBC 查询返回ResultSet后,MyBatis 会逐行处理:

  • 每一行对应一个 Java 对象

  • 这个对象的生成与赋值由getRowValue完成

这一阶段做三件事:

① 创建结果对象

MyBatis根据ResultMap描述的信息:

  • 确定要创建的实体类型(比如 User)

  • 通过构造器或反射实例化对象

结果:得到rowValue

② 判断是否需要进一步属性填充

如果返回类型是简单类型(如 Integer/String):

  • TypeHandler 直接取值即可,不需要属性映射

如果是普通实体对象:

  • 继续执行字段映射流程

③ MetaObject包装(反射入口)

MyBatis 会创建 MetaObject:

  • 统一封装对象属性访问

  • 后续所有 setter/getter 都通过它完成

  • 底层依赖反射调用 setter

getRowValue

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { // 获取当前 ResultMap 中已经映射到的数据库列名集合 //USER_ID //USER_NAME //REAL_NAME //PASSWORD //AGE //D_ID final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix); boolean foundValues = false; // 获取 ResultMap 中所有属性映射配置(<result>、<association>、<collection>) // final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); //org.apache.ibatis.session.Configuration@10425785,userId,user_Id,class java.lang.Integer,INTEGER,class java.lang.Integer //org.apache.ibatis.session.Configuration@10425785,userName,user_name,class java.lang.String,null,class java.lang.String //org.apache.ibatis.session.Configuration@10425785,realName,real_name,class java.lang.String,VARCHAR,class java.lang.String //org.apache.ibatis.session.Configuration@10425785,password,password,class java.lang.String,VARCHAR,class java.lang.String //org.apache.ibatis.session.Configuration@10425785,age,age,class java.lang.Integer,INTEGER,class java.lang.Integer //org.apache.ibatis.session.Configuration@10425785,dId,d_id,class java.lang.Integer,INTEGER,class java.lang.Integer for (ResultMapping propertyMapping : propertyMappings) { //propertyMapping = {ResultMapping@3586} "ResultMapping{property='password', column='password', javaType=class java.lang.String, jdbcType=VARCHAR, nestedResultMapId='null', nestedQueryId='null', notNullColumns=[], columnPrefix='null', flags=[], composites=[], resultSet='null', foreignColumn='null', lazy=true}" // configuration = {Configuration@3509} // property = "password" // column = "password" // javaType = {Class@683} "class java.lang.String" // jdbcType = {JdbcType@3774} "VARCHAR" 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; } //从 ResultSet 中取出某个属性(字段)的值,并支持嵌套查询、嵌套结果集、普通列映射三种情况。 private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { if (propertyMapping.getNestedQueryId() != null) { return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix); } else if (propertyMapping.getResultSet() != null) { addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK? return DEFERRED; } else { // 获取该属性对应的 TypeHandler,用于从 ResultSet 中取值并转换类型 final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler(); // 使用 TypeHandler 从 ResultSet 中读取该列的值,并返回 final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); return typeHandler.getResult(rs, column); } }

class java.lang.String

哪个适合注册的

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }

注册了类型处理器

日志模块

适配器模式

四个日志级别

slf4j指定

private static void tryImplementation(Runnable runnable) { if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } }

配置

如何关联

config

jdbc

c

操作日志

返回日志代理对象

事务

/** * Copyright 2009-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.transaction; ​ import java.sql.Connection; import java.sql.SQLException; /** * MyBatis Transaction 事务接口 * * 版权声明:Apache License 2.0 * * Transaction 接口是 MyBatis 对数据库事务的抽象定义。 * * 它的核心作用是: * 1)封装 JDBC Connection * 2)统一管理事务生命周期 * * 生命周期包括: * - 创建连接 * - 准备连接(设置自动提交等) * - 提交事务 commit * - 回滚事务 rollback * - 关闭连接 close * * MyBatis 的事务控制最终都会通过该接口实现。 * * @author Clinton Begin */ public interface Transaction { ​ /** * 获取当前事务内部持有的数据库连接 Connection。 * * MyBatis 执行 SQL 时最终都是通过 Connection 创建 Statement 来完成操作。 * * @return 数据库连接对象 Connection * @throws SQLException 如果获取连接失败则抛出异常 */ Connection getConnection() throws SQLException; ​ /** * 提交事务。 * * 当 SQL 执行成功并且需要持久化修改时, * MyBatis 会调用该方法触发 JDBC Connection.commit()。 * * @throws SQLException 提交失败时抛出异常 */ void commit() throws SQLException; ​ /** * 回滚事务。 * * 当 SQL 执行过程中出现异常, * 或用户主动要求撤销操作时, * MyBatis 会调用该方法触发 JDBC Connection.rollback()。 * * @throws SQLException 回滚失败时抛出异常 */ void rollback() throws SQLException; ​ /** * 关闭事务。 * * 事务结束后必须释放数据库连接资源, * MyBatis 会调用该方法关闭 Connection。 * * @throws SQLException 关闭连接失败时抛出异常 */ void close() throws SQLException; ​ /** * 获取事务超时时间(如果配置了)。 * * MyBatis 支持在某些事务管理器中设置超时限制, * 超时后事务可能会自动回滚。 * * @return 超时时间(秒),如果未设置则返回 null * @throws SQLException 获取失败时抛出异常 */ Integer getTimeout() throws SQLException; ​ } ​

MyBatis 默认用 JDBC 原生事务:

  • connection.commit()

  • connection.rollback()

即:

MyBatis 的事务本质就是 JDBC Connection 的事务

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 1:31:37

外包干了17天,技术倒退明显

先说情况&#xff0c;大专毕业&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01; 而我已经在一个企业干了四年的功能…

作者头像 李华
网站建设 2026/4/15 5:05:49

以下7种硬件测试的种类,不知道的赶紧收藏了!

硬件产品测试的 7 大种类 信号质量测试 时序测试 功能测试 性能测试 容错测试 长时间测试 一致性测试 信号质量测试 1.信号质量测试&#xff1a; 信号质量测试&#xff0c;是很底层与直接的一项测试&#xff0c;需要对硬件板卡本身有相当的了解才能进行。另外&#xff0c;板…

作者头像 李华
网站建设 2026/3/28 0:21:57

常用API

成员变量与局部变量 包 String集合

作者头像 李华
网站建设 2026/4/11 12:56:27

Python 初级入门教程:从零开始掌握编程基础

目录 引言一、Python 简介与特点二、环境搭建与准备工作三、Python 基础语法详解3.1 变量与数据类型3.2 控制结构3.3 函数定义与使用 四、常用数据结构与操作4.1 列表操作4.2 字典操作 五、文件操作与异常处理5.1 文件操作5.2 异常处理 六、实用案例&#xff1a;学生成绩管理系…

作者头像 李华
网站建设 2026/4/15 17:05:22

AI产品经理转型指南:从零入行大模型产品经理的核心能力与实战路径_0经验如何快速转型AI产品经理?

文章分析了AI产品经理岗位的热门现状及转型难点&#xff0c;指出经验与岗位要求不匹配是主要问题。介绍了专业型和应用层两类AI产品经理能力模型&#xff0c;强调了产品建设能力、行业理解、技术理解和AI落地经验四大核心能力。最后推荐了产品经理私教陪跑实战营&#xff0c;提…

作者头像 李华