当前位置: 首页 > news >正文

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 的事务

http://www.jsqmd.com/news/344150/

相关文章:

  • 2026年银烧结设备服务商综合实力与选型指南 - 2026年企业推荐榜
  • 2026湖南炒货配送服务商综合评测与选型指南 - 2026年企业推荐榜
  • 2026年热门的三维动画设计成熟方案汇总 - 行业平台推荐
  • 2026年山东洗涤软件服务商综合实力深度测评与选型指南 - 2026年企业推荐榜
  • python接口自动化测试 | yaml数据驱动参数化,看完这一篇就够了
  • 中阶FPGA效能红线重新划定! AMD第2代Kintex UltraScale+登场,记忆体频宽跃升5倍
  • 谣言只是表象,投资者耐心正在耗尽!
  • 软件测试找bug小技巧总结,从初级跨入中级测试......
  • Python自动化测试 APP自动化测试最全教程,玩转自动化测试
  • 2026年评价高的球磨铁机床铸件厂家怎么选 - 行业平台推荐
  • 软件测试之——性能测试,Web性能测试用例(详全)
  • 一款既能去除Gemini生成图片水印,又能压缩图片的工具火了(免费安全)。。。
  • 基于物理交互的具身智能决策框架设计一、 为什么物理交互是决策的关键?二、 决策框架的核心设计原则#物理交互#具身智能决策框架#决策闭环#物理常识#力控制#AI#LLM#Transform
  • 【测试面试】2026年软件测试面试题大全(精品带回答的)
  • 软件测试实例:登录功能怎么设计测试用例
  • 2026年比较好的三维动画设计/医疗三维动画设计服务质量推荐 - 行业平台推荐
  • 什么是交互测试?
  • Fiddler抓取HTTPS最全(强)攻略
  • 从零搭建完整python自动化测试框架logging日志模块详解
  • 自动化测试 | 多个自动化测试用例?多个自动化测试数据怎么管理?资深测试总结
  • 2026年安徽国考备考,如何选择一家靠谱的培训机构? - 2026年企业推荐榜
  • 最新出炉 -Web自动化测试之playwright:概述
  • 外包干了17天,技术倒退明显
  • 2026年安徽编制考试备考:如何选择诚信可靠的教育平台 - 2026年企业推荐榜
  • 带你用python做自动化测试,这五大自动化测试框架读这一篇就够了......
  • 2026年别墅石材选型指南:四大服务商综合实力深度解析 - 2026年企业推荐榜
  • 软件测试项目实战,Web测试常用测试点,即拿即用宝典
  • 2026年初,合肥天猫代运营服务团队选择指南 - 2026年企业推荐榜
  • 2026年值得关注的五家通宵自助棋牌室企业 - 2026年企业推荐榜
  • 2026长沙SPA足浴口碑榜发布:综合评测与深度选型指南 - 2026年企业推荐榜