Mybatis(调用简图和框架设计思路)+手写模拟mybatis源码分享

近期深入学习我们工作中基本都会使用到的Mybatis框架,从mybatis的框架设计上收获良多,下面用一篇简单易懂的文章分享一下学习心得,文末会结合阅读的源码结构进行模拟手写一个简易版的mybatis调用代码加深理解。

一.什么是Mybatis?

根据官网定义:mybatis – MyBatis 3 | Introduction

MyBatis 是一款优秀的持久层ORM框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

二.Mybatis作为ORM框架,和传统的JDBC有什么区别?

ORM全称是Object-Relaction-Mapper,顾名思义就是将对象-数据库关系映射,就是通过Java的POJO类(类似Model层的VO)直接与数据库的表/字段建立关联关系,让使用者可以直接通过Java去直接直观地操作数据库表/字段。

ORM框架是为了解决JDBC一系列存在问题而诞生,那么传统JDBC存在哪些弊端呢?

(1)JDBC底层没有使用连接池,操作数据库需要不断创建连接,关闭连接,会带来比较大的性能消耗;

(2)JDBC的sql在Java原生方式编写,如果改动需要重新编译不利于系统维护;

(3)JDBC使用PrepareStatement预编译方式,对变量赋值时需要维护索引号,需要使用setInt、setString等硬编码写法,不利于拓展;

(4)JDBC返回的结果集ResultSet解析结果时,也不得不进行硬编码。

而Mybatis针对这些问题都封装了一系列工具类来解决,使得使用者不需要过多关注JDBC层的连接和硬编码问题。

三.快速开始

下面以我自己使用的配置,对mybatis的简单使用进行介绍,更多使用可以查阅官网。

Maven配置添加mybatis依赖:


  org.mybatis
  mybatis
  3.4.1

resources目录下mybatis-config.xml全局文件:




  
    
      
      
        
        
        
        
      
    
  
  
    
  

resources目录下config.properties文件:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mysql
username=root
password=root

resources/com/test/mapper目录下的BlogMaper.xml文件:





  

  

model目录下Blog.java:

package com.test.model;
public class Blog {
  private Integer id;
  private String name;
  private Integer amount;
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Integer getAmount() {
    return amount;
  }
  public void setAmount(Integer amount) {
    this.amount = amount;
  }
  @Override
  public String toString() {
    return "Blog{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", amount=" + amount +
      '}';
  }
}

Main方法:

public class TestMybatis {
  public static void main(String[] args) throws IOException {

    //解析配置文件
    InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    Properties properties = new Properties();
    properties.load(Resources.getResourceAsStream("config.properties"));

    //得到SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, properties);

    //得到SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //通过namesace获取
    Blog blog = (Blog) sqlSession.selectOne("com.test.model.BlogMapper.selectTestById", 2);
    System.out.println(blog);

}

通过上面简单的配置,就可以通过mybaits对数据库进行查询等操作了。从上述配置可以看到,mybaits对于XML配置模式的整个调用过程,可以理解为以SqlSession作为中转,向上解析配置,向下连接JDBC进行数据库连接调用。

四.Mybatis调用简图和简要分析

Mybatis(调用简图和框架设计思路)+手写模拟mybatis源码分享_第1张图片

Mybatis(调用简图和框架设计思路)+手写模拟mybatis源码分享_第2张图片

Mybatis相当于在Java和Mysql等数据库之间作为桥梁,Mybatis内部会提前解析一系列配置信息。Java通过简单的配置请求到Mybatis,Mybatis内部通过对请求的解析,最终还是会使用原生JDBC对数据库进行操作。

那么我们就可以对Mybatis内部设计做一个简单的分层猜想,SqlSession作为中间层分别向上和向下连接配置Configuration及JDBC执行器Executor,负责根据不同请求情况在Configuration中找到合适的执行语句,交给Executor去连接操作JDBC,最终返回结果。

(1)首先,Mybatis对全局配置文件的解析,主要包括mybatis-config.xml,Mapper.xml等配置文件的解析。

核心入口在:

 //得到SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, properties);

这个入口会使用xpath对mybatis-config.xml和mapper.xml等配置文件进行的解析,将每个节点的配置封装为一个个的Configuration内部属性当中。

org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)

-->org.apache.ibatis.builder.xml.XMLConfigBuilder.parser

----->org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration

------->org.apache.ibatis.builder.xml.XMLConfigBuilder.propertiesElement

------->org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement

------->org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement

---------->org.apache.ibatis.session.Configuration.setEnvironment

对于mapper.xml扫描在上面org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement步骤中,执行流程如下:

org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement

----->org.apache.ibatis.builder.xml.XMLMapperBuilder.parse

------>org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement

--------->org.apache.ibatis.builder.xml.XMLMapperBuilder.parameterMapElement

--------->org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElements

--------->org.apache.ibatis.builder.xml.XMLMapperBuilder.sqlElement

--------->org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext

------------>org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode

------>org.apache.ibatis.builder.xml.XMLMapperBuilder.bindMapperForNamespace

------>org.apache.ibatis.builder.xml.XMLMapperBuilder.parsePendingResultMaps

------>org.apache.ibatis.builder.xml.XMLMapperBuilder.parsePendingCacheRefs

------>org.apache.ibatis.builder.xml.XMLMapperBuilder.parsePendingStatements

--------->org.apache.ibatis.session.Configuration.addMappedStatement

上述源码的调用过程,核心目的就是解析配置文件,将配置文件的信息封装到Configuration当中。

(2)SqlSession调用Executor

从源码中我们可以看到,sqlSession是一个接口,定义了诸多操作数据库的方法,同时还提供了获取Configuration配置,以及通过@Mapper获取Mapper接口代理对象的方法:

package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;

public interface SqlSession extends Closeable {
   T selectOne(String statement);
   T selectOne(String statement, Object parameter);
   List selectList(String statement);
   List selectList(String statement, Object parameter);
   List selectList(String statement, Object parameter, RowBounds rowBounds);
   Map selectMap(String statement, String mapKey);
   Map selectMap(String statement, Object parameter, String mapKey);
   Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
   Cursor selectCursor(String statement);
   Cursor selectCursor(String statement, Object parameter);
   Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds);
  void select(String statement, Object parameter, ResultHandler handler);
  void select(String statement, ResultHandler handler);
  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
  int insert(String statement);
  int insert(String statement, Object parameter);
  int update(String statement);
  int update(String statement, Object parameter);
  int delete(String statement);
  int delete(String statement, Object parameter);
  void commit();
  void commit(boolean force);
  void rollback();
  void rollback(boolean force);
  List flushStatements();
  void close();
  void clearCache();
  //获取配置和根据Mpper注解获取代理Mapper对象
  Configuration getConfiguration();
   T getMapper(Class type);
  Connection getConnection();
}

 我们进一步查看其实现子类DefaultSqlSession.java:

Mybatis(调用简图和框架设计思路)+手写模拟mybatis源码分享_第3张图片

查看DefaultSqlSession.java中的Executor的属性对应子类实现: 

Mybatis(调用简图和框架设计思路)+手写模拟mybatis源码分享_第4张图片

可以看到sqlSession的实现当中是封装了Configuration和Executor的,而Executor就是对JDBC的执行器。也就是说sqlSession实际上并不直接操作数据库,而是在配置层和JDBC执行层进行了一层包装和转发而已。这也印证了上面的猜测,但是实际上每一层的实现逻辑还是很复杂的,考虑的场景很多,包括缓存,事务,动态sql,以及Spring整合点等等。

五.Mybatis使用了哪些设计模式?

(1)Builder 模 式 : 最常看到的设计模式了,核心思想都是使用内部Builder类对传入的参数构建出一个目标对象返回。例 如 SqlSessionFactoryBuilder 、 XMLConfigBuilder 、 XMLMapperBuilder 、XMLStatementBuilder、CacheBuilder;
(2)工厂模式:例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
(3)单例模式:例如ErrorContext和LogFactory;
(4)代理模式:Mybatis实现的核心,比如MapperProxy、ConnectionLogger用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
(5)组合模式:例如SqlNode和各个子类ChooseSqlNode等;
(6)模 板 方 法 模 式 : 例 如 BaseExecutor 和 SimpleExecutor , 还 有 BaseTypeHandler 和 所 有 的 子 类 例 如IntegerTypeHandler;
(7)适配器模式:例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
(8)装饰者模式:例如Cache包中的cache.decorators子包中等各个装饰者的实现;
(9)迭代器模式:例如迭代器模式PropertyTokenizer;

.......

六.手写模拟实现一个简易Mybatis,支持XML和注解方式查询

文章单独一篇展开,链接为:手写模拟实现一个简易Mybatis,支持XML和注解方式查询_喜欢火影的木易杨的博客-CSDN博客

你可能感兴趣的:(mybatis专栏,xml,java,maven)