摘要

本篇文章只是个人阅读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框架分析的更多相关文章

  1. MyBatis框架及原理分析

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架,其主要就完成2件事情: 封装JDBC操作 利用反射打通Java类与SQL语句之间的相互转换 MyBatis的主要设计目的就 ...

  2. MyBatis框架的使用及源码分析(十一) StatementHandler

    我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...

  3. MyBatis框架的使用及源码分析(九) Executor

    从<MyBatis框架的使用及源码分析(八) MapperMethod>文中我们知道执行Mapper的每一个接口方法,最后调用的是MapperMethod.execute方法.而当执行Ma ...

  4. MyBatis框架的使用及源码分析(八) MapperMethod

    从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...

  5. MyBatis框架的使用及源码分析(七) MapperProxy,MapperProxyFactory

    从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过Ma ...

  6. MyBatis框架的使用及源码分析(六) MapperRegistry

    我们先Mapper接口的调用方式,见<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用>的示例: public void findUserById() { Sql ...

  7. MyBatis框架的使用及源码分析(五) DefaultSqlSessionFactory和DefaultSqlSession

    我们回顾<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 一文的示例 private static SqlSessionFactory getSessionF ...

  8. MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件

    在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybat ...

  9. MyBatis框架的使用及源码分析(三) 配置篇 Configuration

    从上文<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 我们知道XMLConf ...

随机推荐

  1. 性能测试常用sql技巧_Oracle

    做了一段时间的性能测试,把自己在性能测试过程中,使用到的Oracle中用到的sql语句整理一番,做个备忘: (1)多个字段以某种格式拼接 "||"字符串拼接符; 示例:将" ...

  2. ionic 的缓存 和局部刷新

    最近两天在做项目时,发现ionic的缓存功能非常方便好用,提高了再低端手机特别是android比较低版本上的流畅性!可是,后来发现,整体的缓存整个页面并不是一个一劳永逸的办法,结合局部刷新功能,感觉就 ...

  3. printf和scanf整理(后续填补)

    scanf和printf头文件:<stdio.h> 1.%d.%3d.%03d.%-3d区分 %d:以十进制形式输出整数(int) %3d:指定宽度为3,不足的左边补空格 %03d:一种左 ...

  4. 学容器必须懂 bridge 网络 - 每天5分钟玩转 Docker 容器技术(32)

    上一节我们讨论了 none 和 host 类型的容器网络,本节学习应用最广泛也是默认的 bridge 网络. Docker 安装时会创建一个 命名为 docker0 的 linux bridge.如果 ...

  5. windows下python连接oracle数据库

    1.首先安装cx_Oracle包2.解压instantclient-basic-windows.x64-11.2.0.4.0.zip到c:\oracle3.拷贝instantclient_11_2下所 ...

  6. zookeeer client 通信协议

    这里主要记录zookeeper client通信协议的.在官方的文档里没找到协议相关部分.这里是记录的协议是通过分析客户端代码得来的. 一.通信流程 客户端发起连接,发送握手包进行timeout协商, ...

  7. 8.javaweb之session

    session是客户端和服务端的一次会话 web的session是指用户在浏览某个网站时,从进入网站到关闭浏览器的这段时间,uyejiushi用户浏览这个网站所花费的时间. session是一个时间的 ...

  8. Java内部类与final关键字详解

    一.内部类的几种创建方法: 1.成员内部类 class Outer{ private int i = 1; class Inner{ public void fun() {System.out.pri ...

  9. 搭建nexus私服(maven)

    这里提供nexus的直接下载页面的链接: https://www.sonatype.com/download-oss-sonatype maven获取依赖jar包是从中央仓库获取,但很莫名的出现jar ...

  10. 定制Android开发者专属T恤

    之前在T社上买了一件定制的T恤,感觉质量挺不错的,那是段子张发起的众筹.正面有hello google这几个字母. 我自己本身是一个Android粉,从nexus手机到pixel手机,坚持买原生的操作 ...