Mybatis框架及原理实例分析

发布时间 - 2026-01-11 02:38:42    点击率:

摘要

本篇文章只是个人阅读mybatis源码总结的经验或者个人理解mybatis的基本轮廓,作为抛砖引玉的功能,希望对你有帮助,如果需要深入了解细节还需亲自去阅读源码。

mybatis基本架构

mybatis的源码应该算是比较容易阅读的,首先mybatis核心功能就是执行Sql语句,但在其基础上又有许多增强的地方(动态Sql,ORM等)。看一个框架的时候,第一步是对整个框架有一个大体的了解。例如mybatis,我们可以从初始化到完成一个sql请求为主线,看一下涉及了哪些类。我个人总结了一下,mybatis的框架主要的核心类有4个

Configuration

Configuration就是用于解析、保存、处理Mybatis的配置内容,包括了

  • mybatis基本配置,例如支持数据库中的字段支持下标转驼峰mapUnderscoreToCamelCase=true等等,参看Mybatis配置说明
  • SqlMapper管理,也就是通过xml或者注解写的一些sql映射。相关的类可以查看源码中MappedStatement类。
  • 创建类,Configuration还有一些创建类的功能,例如Executor、StatementHandler。这个2个类后面还会说到

小节Configuration

总结Configuration的功能,当然,如何读取和解析相关文件是Configuration中大部分代码做的事。这些都是为了准备后面mybatis运行的基本条件。Configuration中创建类是因为创建的这些类都依赖于Configuration(但这样做数据和逻辑没有做到分离)。

SqlSession

SqlSession可能是mybatis中我们最常用的类,其实他是一个门面类,直接对外提供服务

public interface SqlSession extends Closeable {
 <T> T selectOne(String statement);
 <E> List<E> selectList(String statement, Object parameter);
 int delete(String statement);
 void rollback();
 void commit();
 ...

}

这些方法都是直接提供给外部调用的。看到这些方法是不是很亲切。(我个人在看源码的时候看到一些自己用过的一些类或方法的时候都有种莫名的亲近感。感觉终于和我的认知世界有交集了)

SqlSession的创建

SqlSessionFactor是用于创建SqlSession建造者,提供给外部快速创建一个SqlSession。是一个工厂类,而SqlSessionFactor的创建则是由SqlSessionFactorBuilder。

Executor

前面说了SqlSession只是一个门面类,Executor才是负责Sql语句执行的。因此Executor才是整个mybatis核心。Executor的实现类有

  • BaseExecutor:看名字知道是最基础Executor,其他的Executor都和这个类有一定的关系
  • CachingExecutor:每次查询的时候会先从缓存中获取,每次有增删改的时候会让缓存失效。CachingExecutor其实是一个代理内,内部代理了BaseExecutor(或其子类)。在BaseExecutor基础上增加了缓存操作。

相关类

我们看一个Executor参数最多的一个方法

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

这些类都对执行Sql有一定关系

MappedStatement

具体点来理解就是我们定义的Sql映射语句,例如我们xml定义的:

<select id="selectCountByPath" parameterType="java.lang.String" resultType="java.lang.Long">
 select count(1) FROM config
 WHERE path = #{path}
</select>

paramter

这个就是传递给sql映射的参数,用于生成和填充动态sql语句

RowBound

限定一次查询数据量,类很简单,看代码就明白,不多说

public class RowBounds {
 public static final int NO_ROW_OFFSET = 0;
 public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
 public static final RowBounds DEFAULT = new RowBounds();
 private int offset;
 private int limit;
 public RowBounds() {
 this.offset = NO_ROW_OFFSET;
 this.limit = NO_ROW_LIMIT;
 }
 public RowBounds(int offset, int limit) {
 this.offset = offset;
 this.limit = limit;
 }
}

ResultHandler

这个和本地缓存有关,用于保存一个查询语句的缓存对象,下次有相同的查询语句的时候就会先尝试从本地缓存中获取。 注意:

,mybatis有2级缓存,第一级是CachingExecutor,第二级缓存就是mybatis的本地缓存,也就是和ResultHandler

缓存失效策略是和一级缓存一样,任何增删改都会清空本地缓存

CacheKey

一个查询语句的在本地缓存中的key,根据sql语句,参数等等组成

BoundSql

这个对象就是本次实际需要执行的sql语句有关的信息,

public class BoundSql {
 private String sql;
 private List<ParameterMapping> parameterMappings;
 private Object parameterObject;
 private Map<String, Object> additionalParameters;
 private MetaObject metaParameters;
 ...

如果说parameter参数是实际传入的参数,那么BoundSql就是根据传入参数进行相关解析后的结果。他的创建在MappedStatement中,根据parameter和当前执行MappedStatement生成

public BoundSql getBoundSql(Object parameterObject) {
 BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 if (parameterMappings == null || parameterMappings.isEmpty()) {
  boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
 }
 // check for nested result maps in parameter mappings (issue #30)
 for (ParameterMapping pm : boundSql.getParameterMappings()) {
  String rmId = pm.getResultMapId();
  if (rmId != null) {
  ResultMap rm = configuration.getResultMap(rmId);
  if (rm != null) {
   hasNestedResultMaps |= rm.hasNestedResultMaps();
  }
  }
 }
 return boundSql;
}

Interceptor

Mybatis提供了Interceptor用于在执行Executor之前进行一些操作,mybatis是怎么使用Interceptor。其实就是在创建Executor时候,会

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
 executorType = executorType == null ? defaultExecutorType : executorType;
 executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
 Executor executor;
 if (ExecutorType.BATCH == executorType) {
  executor = new BatchExecutor(this, transaction);
 } else if (ExecutorType.REUSE == executorType) {
  executor = new ReuseExecutor(this, transaction);
 } else {
  executor = new SimpleExecutor(this, transaction);
 }
 if (cacheEnabled) {
  executor = new CachingExecutor(executor);
 }
 //看这里!!!
 executor = (Executor) interceptorChain.pluginAll(executor);
 return executor;
 }

这里主要是通过jdk动态代理实现的

public class Plugin implements InvocationHandler {
 ...
 public static Object wrap(Object target, Interceptor interceptor) {
 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
 Class<?> type = target.getClass();
 Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
 if (interfaces.length > 0) {
  return Proxy.newProxyInstance(
   type.getClassLoader(),
   interfaces,
   new Plugin(target, interceptor, signatureMap));
 }
 return target;
 }
 
 ...
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 try {
  Set<Method> methods = signatureMap.get(method.getDeclaringClass());
  if (methods != null && methods.contains(method)) {
  return interceptor.intercept(new Invocation(target, method, args));
  }
  return method.invoke(target, args);
 } catch (Exception e) {
  throw ExceptionUtil.unwrapThrowable(e);
 }
 }

这样在调用Executor的时候就会先判断是否满足Interceptor的执行条件,满足则会先执行Intercepter#intercept()方法

最底层的Handler

要说直接和Jdbc打交道的就是各种Handler类,例如

  • StatementHandler: 处理java.sql.Statement
  • ParameterHandler: 向PreparedStatement中设置参数
  • ResultSetHandler:处理sql执行结果,并转换成指定的类对象 上面的这些其实都不复杂,所以代码还是比较好理解的

Transaction

每个Executor生成的时候都会把Transaction传入,在BaseExecutor中Transaction是其成员变量,那Transaction的作用是什么呢?

public interface Transaction {
 Connection getConnection() throws SQLException;
 void commit() throws SQLException;
 void rollback() throws SQLException;
 void close() throws SQLException;
 Integer getTimeout() throws SQLException;
}

其实之前一直都没提到过Connect谁来管理,这里可以看出来,Transaction负责了Connection的获取,以及对这次Connect的提交和回滚等操作。这个类也是比较好理解的。Executor的commit或者rollback最后都是调用Transaction的

总结

可以看出,mybatis的源码是比较容易阅读的(相对于Spring等)。上面介绍了框架中的一些核心类,但是很多细节的地方值得我们去深挖。这个就需要我们能沉下来好好阅读代码。


# mybatis  # 框架分析  # mybatis 连接mysql数据库 tinyint 为boolean类型详解  # 关于mybatis传入参数一直为null的问题  # mybatis中如何用tinyint保存Boolean类型  # 都是  # 是一个  # 会先  # 才是  # 有一定  # 比较好  # 可以看出  # 提供给  # 比较容易  # 是因为  # 都不  # 子类  # 最多  # 说了  # 基础上  # 则是  # 都没  # 还会  # 是怎么  # 但在 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel如何集成Inertia.js与Vue/React?(安装配置)  网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?  html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】  Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  EditPlus中的正则表达式实战(5)  Android 常见的图片加载框架详细介绍  Laravel Session怎么存储_Laravel Session驱动配置详解  Laravel如何自定义错误页面(404, 500)?(代码示例)  Laravel如何实现API速率限制?(Rate Limiting教程)  如何快速生成专业多端适配建站电话?  Swift中switch语句区间和元组模式匹配  如何打造高效商业网站?建站目的决定转化率  教学论文网站制作软件有哪些,写论文用什么软件 ?  Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】  如何用AWS免费套餐快速搭建高效网站?  免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?  Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试  企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?  PythonWeb开发入门教程_Flask快速构建Web应用  香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化  如何快速上传自定义模板至建站之星?  js代码实现下拉菜单【推荐】  悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  如何快速搭建高效香港服务器网站?  打开php文件提示内存不足_怎么调整php内存限制【解决方案】  如何在建站主机中优化服务器配置?  Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】  laravel怎么配置和使用PHP-FPM来优化性能_laravel PHP-FPM配置与性能优化方法  Laravel如何记录自定义日志?(Log频道配置)  焦点电影公司作品,电影焦点结局是什么?  千库网官网入口推荐 千库网设计创意平台入口  惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?  Laravel如何生成API文档?(Swagger/OpenAPI教程)  香港服务器网站生成指南:免费资源整合与高速稳定配置方案  javascript读取文本节点方法小结  小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像  高防服务器租用指南:配置选择与快速部署攻略  googleplay官方入口在哪里_Google Play官方商店快速入口指南  高防服务器租用首荐平台,企业级优惠套餐快速部署  JS经典正则表达式笔试题汇总  php8.4header发送头信息失败怎么办_php8.4header函数问题解决【解答】  开心动漫网站制作软件下载,十分开心动画为何停播?  iOS中将个别页面强制横屏其他页面竖屏  laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法  Laravel如何设置定时任务(Cron Job)_Laravel调度器与任务计划配置  浅谈javascript alert和confirm的美化  Laravel如何使用withoutEvents方法临时禁用模型事件