开篇略谈

  谈到Mybatis,对于我们猿们来说是熟悉不过了。但是有没有兴趣去探一下其实现原理呢?是的,请往下看 ↓  come on...

Mybatis综述

  Mybatis一个数据持久层轻量级框架,回顾我们原始的开发即没有持久层框架的年代。话不多说上代码 ↓

Connection con = DriverManager.getConnection(url, "...", "..."); // 首先我们得获得一个数据库连接
Statement stmt = con.createStatement(); // 不管你是获得statement还是preparedStatement,总之在项目越来越大得时候这些代码会有点累赘// PreparedStatement prestmt = con.prepareStatement(...);

  而我们得Mybatis则将其封装了起来,构成持久层框架,我们只要按照它的规定去配置就可以了,而无需在关注上面得代码,只需关注sql就行了,接下来入正题

Mybatis源码解析

  提到Mybatis我们总会想起那个耳熟能详的东西sqlSession,是的,这是Mybatis的核心所在,sqlSession是由单例sqlSessionFactory创建而来的,而sqlSessionFactory又是由sqlSessionFactoryBuilder创建而来的,因此sqlSession得老祖宗就是它。在这里不管它的父辈,我们就来研究一下sqlSession的默认defaultSqlSession,多说无益看代码

// 可以看到 DefaultSqlSession 实现了SqlSession,这很容易让我们想起模板方法模式,请往下看
public class DefaultSqlSession implements SqlSession {// 下面是一些变量private Configuration configuration; // 这个东西是核心关注点也是此次重点讲解点,它包含了Mybatis的所有配置信息,我们此次所要研究的就是configuration是如何将sql获取到的private Executor executor;private boolean autoCommit;private boolean dirty;private List<Cursor<?>> cursorList;// 以下是对SqlSession方法的实现
  @Overridepublic <T> T selectOne(String statement) {return this.<T>selectOne(statement, null);}@Overridepublic <T> T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.List<T> list = this.<T>selectList(statement, parameter);if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}}// 这个是获取configuration的mapper的,也就是获取sql语句的,这是重点讲解的
  @Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}

public <T> void addMapper(Class<T> type) {    mapperRegistry.addMapper(type);   }  
// 获取数据库连接  @Override public Connection getConnection() { try { return executor.getTransaction().getConnection(); } catch (SQLException e) { throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e); } } } // 而SqlSession又继承了Closeable,很明显Closeable就是关闭connection的,这里就不详细说明 public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); .......此处省略,详情可自己看源码 } 

以上是DefaultSqlSession的方法介绍,此次要从addMapper方法入手弄明白Mybatis它是如何获取我们写的SQL语句的,come on

// 以下是mapperRegistry
public class MapperRegistry {public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse(); // 这是使用了MapperAnnotationBuilder的parse方法进行了解析,那么它是如何解析的呢,解析的又是什么?loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}..... 其他代码就省略了
}

接下来我们看它是如何解析的

public class MapperAnnotationBuilder {public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) { // 首先会判断这个接口即Mapper是否加载过了loadXmlResource(); // 这是对mapper的xml文件进行解析configuration.addLoadedResource(resource); // 加载进去
      assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods(); // 获得当前Mapper接口的所有方法for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method); // 对方法进行解析
          }} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}
}

  // 这个方法也是MapperAnotationBuilder的
void parseStatement(Method method) {Class<?> parameterTypeClass = getParameterType(method); // 获得参数类型LanguageDriver languageDriver = getLanguageDriver(method); SqlSource sqlSource = getSqlSourceFromAnnotations(method,  // 这里是获取sqlSource parameterTypeClass, languageDriver);if (sqlSource != null) {Options options = method.getAnnotation(Options.class);final String mappedStatementId = type.getName() + "." + method.getName();Integer fetchSize = null;Integer timeout = null;StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;SqlCommandType sqlCommandType = getSqlCommandType(method);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;KeyGenerator keyGenerator;String keyProperty = "id";String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// first check for SelectKey annotation - that overrides everything elseSelectKey selectKey = method.getAnnotation(SelectKey.class);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}if (options != null) {if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();resultSetType = options.resultSetType();}String resultMapId = null;ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {String[] resultMaps = resultMapAnnotation.value();StringBuilder sb = new StringBuilder();for (String resultMap : resultMaps) {if (sb.length() > 0) {sb.append(",");}sb.append(resultMap);}resultMapId = sb.toString();} else if (isSelect) {resultMapId = parseResultMap(method);}assistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,// DatabaseIDnull,languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);}}private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {try {Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method); // 这里会判断方法是否使用了select等注解Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method); // 是否使用了provider注解if (sqlAnnotationType != null) {if (sqlProviderAnnotationType != null) {throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());}Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);return buildSqlSourceFromStrings(strings, parameterType, languageDriver);} else if (sqlProviderAnnotationType != null) {Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);}return null;} catch (Exception e) {throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);}}  private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {try {Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);if (sqlAnnotationType != null) {if (sqlProviderAnnotationType != null) {throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());}Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);return buildSqlSourceFromStrings(strings, parameterType, languageDriver);} else if (sqlProviderAnnotationType != null) {Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);}return null;} catch (Exception e) {throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);}}

从上面两个方法我们可以解释为什么我们使用provider和直接在mapper接口方法上加select语句会有效

Mybatis原理初探相关推荐

  1. 2021年大数据Flink(九):Flink原理初探

    Flink原理初探 Flink角色分工 在实际生产中,Flink 都是以集群在运行,在运行的过程中包含了两类进程. JobManager: 它扮演的是集群管理者的角色,负责调度任务.协调 checkp ...

  2. C#之CLR内存原理初探

    C#之CLR内存原理初探 投稿:shichen2014 字体:[增加 减小] 类型:转载 时间:2014-08-04 我要评论 这篇文章主要介绍了C#之CLR内存原理初探,有助于读者进一步理解C#的运 ...

  3. 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现

    本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论. MyBatis将数据缓存设计成两级结构,分为一级缓存 ...

  4. SpringBoot运行原理初探

    运行原理初探 其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件! <parent><groupId>org.springframework.boot</grou ...

  5. 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)

    文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...

  6. mybatis 原理_深入理解MyBatis原理 MyBatis数据源与连接池

    点击上方"程序开发者社区"关注,选择"设为星标" 第一时间送达实用干货 对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文 ...

  7. 5.Flink原理初探\角色分工\执行流程图生成\DataFlow,Operator,Partition,Parallelism,SubTask\OperatorChain和Task\任务槽\槽共享

    本文来自:Flink1.12-2021黑马程序员贺岁视频 的学习笔记 5.Flink原理初探 5.1.角色分工 5.2.执行流程 5.3.DataFlow 5.3.1.DataFlow.Operato ...

  8. java解析sql语句简书,Mybatis原理解析(一)--java.sql数据库操作的基本实现方式

    在研究Mybatis原理之前,先看一下java中是如何实现一次对数据库的访问的: public void testSql() { Connection connection = null; State ...

  9. 【mybatis原理工作原理】

    文章目录 mybatis原理: mybatis缓存机制 mybatis原理: mybatis的工作原理就是:先封装sql,接着调用jdbc操作数据库,最后把数据库返回的表结果封装成java类. 通过代 ...

最新文章

  1. centos6.4安装nagios—4.0.8
  2. 进程间通信的方式(三):消息队列
  3. Docker实战:Docker安装部署RabbitMQ
  4. mysql主从复制周期_Mysql主从复制的实现
  5. ubuntu18安装微信
  6. Redis学习笔记001---Windows下安装Redis
  7. Android 调试技巧之快速重启生效
  8. Oracle Database Documentation
  9. 07、基于ADC0808/ADC0809的多通道电压采集程序设计
  10. 解决 error: Raw kernel process exited code: 3221226505
  11. EXFAT文件系统DBR的完美恢复
  12. MIT 线性代数导论 第十九、二十讲:行列式公式、代数余子式、克拉默法则
  13. java是牌子的眼镜多少钱一副_一副好眼镜的成本都去哪儿了?
  14. linux之ls -l命令详解
  15. 递归概述与递归能解决的问题和规则 [数据结构][Java]
  16. Excel在统计分析中的应用—第二章—描述性统计-分组数据的中位数的求解方法(组离散数据)
  17. 手机图片压缩大小的方法,用什么软件压缩
  18. mysql 汉字字母拼音_mysql 汉字按拼音字母排序、获取拼音首字母、拼音全拼
  19. 对象(构造函数)和类(class)
  20. 【zephyr】apds9660 接近(Proximity)传感器 驱动模型实现方式(一)

热门文章

  1. 合并表格,并实现对datatable的group by 功能
  2. C语言长度为0的数组
  3. Normal-Inverse Gamma Mixture简介
  4. linux 内核 printk 使用
  5. CentOS6安装MySQL 2 - yum makecache成功
  6. 未处理OleDbException - 找不到可安装的ISAM学习总结
  7. Windows编程设备描述表的概念和在客户区绘制、在窗口标题栏绘制、在桌面绘制图解
  8. vue 自定义封装组件 使用 model 选项
  9. Java开发小技巧(五):HttpClient工具类
  10. JavaScript -- 理解对象的属性