深入了解MyBatis(一)

995 阅读5分钟

Mybatis是什么

市面上常用的ORM持久层框架,底层是封装了JDBC

什么是ORM

ORM:Object Relational Mapper/对象关系映射

实现面向对象编程语言里不同类型系统的数据之间的转换。(可以理解为Java是中文,SQL是英语,ORM就是中间的翻译)

ORM框架

MyBatis, Hibernate ......(这两个是国内常用的ORM框架)

MyBatis和Hibernate的对比

面试题:MyBatis与Hibernate区别

MyBatis与JDBC的区别

MyBatis相比JDBC的优势

MyBatis实现

MyBatis核心组件

组件名 作用
SqlSession 作为MyBatis工作的主要顶层API,和数据库交互的会话,完成数据库增删改查功能
Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数
ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
MappedStatement 该类维护了Mapper配置文件中的一条SQL语句节点,<select>,<insert>等等
SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql 表示动态生成的SQL语句以及相应的参数信息
Configuration MyBatis所有的配置信息都维持在Configuration对象之中

记住上面这些关键字,面试中问到MyBatis一定用的上!划重点!!

代码案例

public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        try (SqlSession session = sqlSessionFactory.openSession()){
            User user = session.selectOne("com.sunfounder.testapp.UserMapper.getUser",1);
            System.out.println("User:"+user);
        }
    }

我们将会通过上面的代码,了解MyBatis的初始化工作,以及MyBatis是如何执行一条查询语句的

获取数据配置源,创建SqlSession工厂

概述

SqlSessions 是由 SqlSessionFactory 实例创建的。SqlSessionFactory 对象包含创建 SqlSession 实例的所有方法。而 SqlSessionFactory 本身是由 SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或手动配置 Java 代码来创建

底层实现调用链

org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
  >org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream, java.lang.String, java.util.Properties)
        // 加载xml文件 将对应值放入Configration中,并返回
        > public Configuration org.apache.ibatis.builder.xml.XMLConfigBuilder.parse()
             // 读取XML配置文件的标签,并填充到Configuration类中
             >org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode root)
                    // 加载environment节点
                    > org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement(XNode context)
                        // 加载Datasource节点 配置数据库工厂
                        > org.apache.ibatis.builder.xml.XMLConfigBuilder.dataSourceElement(XNode context)
	            // 获取对应的MySQL连接用户信息(root账户账户密码等)
	            > org.apache.ibatis.parsing.XNode.getChildrenAsProperties()
                            > org.apache.ibatis.builder.BaseBuilder.resolveClass(String alias)
                                > org.apache.ibatis.builder.BaseBuilder.resolveAlias
                                    // 注册类型
                                    > org.apache.ibatis.type.TypeAliasRegistry.resolveAlias
                        // 通过工厂(DataSourceFactory)获取数据库源DataSource 
                        >  org.apache.ibatis.datasource.DataSourceFactory.getDataSource
	        // 配置/设置Configuration对象对应的全局变量Environment参数
	        > org.apache.ibatis.session.Configuration.setEnvironment(Environment environment)
		> org.apache.ibatis.mapping.Environment.Builder.build
	// 读取Configuration类,创建SqlSessionFactory
        > org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)

  1. 开发者调用SqlSessionFactoryBuilderbuild() 并传入全局配置文件
  2. 在创建SqlSessionFactory之前调用XMLConfigBuilder的parse方法,该方法返回已经通过读取全局配置文件配置好的 Configuration 实例(具体读取的处理在 parseConfiguration() 中实现)
  3. 调用SqlSessionFactoryBuilder中需要Configuration类参数的 build() 创建 SqlSessionFactory

图解

获取SqlSession实例

底层实现调用链

// SqlSessionFactory获取SqlSession实例方法
org.apache.ibatis.session.SqlSessionFactory.openSession()
    >org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession()
        // 该方法返回 DefaultSqlSession
        >SqlSession org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
	  // 获取全局配置参数
	  > org.apache.ibatis.session.Configuration.getEnvironment()
	  // 创建执行器(默认是SIMPLE), 返回 SimpleExecutor , 一共有三种执行器
	  > org.apache.ibatis.session.Configuration.newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)
	      // 实例化
	      > org.apache.ibatis.executor.SimpleExecutor
	  // 有参构造函数创建 DefaultSqlSession对象,openSession方法要返回的就是这个对象
	  > org.apache.ibatis.session.defaults.DefaultSqlSession()
  1. 开发者调用openSession() 创建SqlSession会话
  2. 方法内部调用 DefaultSqlSessionFactoryopenSessionFromDataSource()
  3. 通过读取 Configuration 初始化 Executor/执行器 ,并返回DefaultSqlSessionFactory对象。应用程序中,SqlSession的初始化一般只执行一次

图解

selectOne()语句的实现

实现调用链

// 入口,顶层API
> 1.org.apache.ibatis.session.SqlSession.selectOne(java.lang.String, java.lang.Object)
       // selectone的本质其实就是selectList后处理,返回数组中的第一个
      > 2.org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(java.lang.String, java.lang.Object)
	// 跳转到该重载方法
	> 3.org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
	     // 得到SQL语句维护对象
	     > 4.org.apache.ibatis.session.Configuration.getMappedStatement(java.lang.String)
	        // 通过传入的 StatementId找到对应的MapperStatement对象
	        > 5.java.util.Map.get()
	     // 执行查询
	     > 6.org.apache.ibatis.executor.Executor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler()
		 // 为当前的查询创建一个缓存Key
		> 7.org.apache.ibatis.executor.BaseExecutor.createCacheKey
		/* 判断是否走缓存还是从DB中查询 */
		> 8.org.apache.ibatis.executor.BaseExecutor.query(此处省略过长的形参)
		    // 判断list是否为空,不为空则从缓存中查找
		    // 走数据库查询	
		    > 9.org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
			    // 执行查询,返回List 结果,将查询的结果放入缓存之中
			    > 10.org.apache.ibatis.executor.BaseExecutor.doQuery()
			        // 创建StatementHandler对象来执行查询操作
			        > 11.org.apache.ibatis.session.Configuration.newStatementHandler()
			        // 创建java.Sql.Statement对象,该类属于JDBC接口,能够发送 SQL 命令或 PL/SQL 命令到数据库,并从你的数据库接收数据。
			        > 12.org.apache.ibatis.executor.BaseExecutor.prepareStatement()
			        // 调用StatementHandler.query()方法
			        // 内部实现是封装了JDBC查询方法返回List结果集
			        > 13.org.apache.ibatis.executor.statement.StatementHandler.query(Statement statement, ResultHandler resultHandler)
			        // 返回结果集
			        > 14.org.apache.ibatis.executor.resultset.ResultSetHandler.handleResultSets()
  1. 开发者通过SqlSession对象调用顶层API:selectOne()
  2. 顶层方法的内部其实是调用selectList,内部进行了处理返回结果集的第一个
  3. 获取MapperStatement对象,该对象保存在Map中,通过StatementId查找到MapperStatement,该对象保存一条SQL语句的相关信息
  4. 调用执行器的query方法
  5. 创建对应的缓存Key,但并没放入缓存中
  6. 调用执行器中的重载query方法,多了缓存key参数
  7. 该方法会判断是从缓存中获取结果集还是去数据库中查找数据(我们假设没有缓存),那么会执行 queryFromDatabase()
  8. 内部会调用SimpleExecute(默认创建的执行器)的doQuery方法
  9. 内部创建一个StatementHandler对象,然后将必要的参数传递给 StatementHandler,使用StatementHandler的 query() 来完成对数据库的查询(底层通过statement类实现查询),最终返回List结果集。

图解


解释下,如果使用<>会出现Bug,使用《》代替,谅解

参考文献

MyBatis官网

《深入理解mybatis原理》 MyBatis的架构设计以及实例分析