MyBatis 3源码解析(一)

Mybatis是支持定制化SQL、存储过程和高级映射的持久层框架。主要完成两件事:

  • 封装JDBC的操作
  • 利用反射完成Java类和SQL之间的转换

mybatis的主要目的就是管理执行SQL是参数的输入和输出,编写SQL和结果集的映射是mybatis的主要优点

mybatis中主要类和接口

  • Configuration:将mybatis配置文件中的信息保存到该类中
  • SqlSessionFactory:解析Configuration类中的配置信息,获取SqlSession
  • SqlSession:负责和数据库交互,完成增删改查
  • Executor:mybatis的调度核心,负责SQL的生成
  • StatementHandler:封装了JDBC的statement操作
  • ParameterHandler:负责完成JavaType到jdbcType的转换
  • ResultSetHandler:负责完成结果集到Java Bean的转换
  • MappedStatement:代表一个select|update|insert|delete元素
  • SqlSource:根据传入的ParamterObject生成SQL
  • BoundSql:包含SQL和参数信息

SqlSessionFactory和SqlSession源码

SqlSessionFactory的创建是mybatis的第一步,SqlSession完成数据库增删改查。我们先来看看二者的创建

首先创建SqlSessionFactoryBudiler对象,在调用builder方法读取mybatis配置文件,并创建SqlSessionFactory:

public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

不管是调用SqlSessionFactoryBuilder哪个build重载方法,最后调用的都是上面的两种,这两种的区别只是采用不同的流读取配置文件,最后都会调用build(Configuration config)创建SqlSessionFactory接口的实现类对象,返回的是DefaultSqlSessionFactory 对象。

当创建SqlSessionFactory完成后下一步就是创建SqlSession:

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //这一步可以发现每个SqlSession会配一个Executor
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

接下来我们看看SqlSession的源码:

public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  @Override
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

SqlSession的所有增删改查操作,最后都会落到上面三个方法,而最后的SQL执行都是使用Executor执行。

接下来我们在看看Executor中的query()和update方法,Executor接口有一个抽象实现类BaseExecutor,我们调用的query()和update()方法实际属于该类,而该类的query()和update()最后都落到三个子类SimpleExecutor、ReuseExecutor、BatchExecutor中。这里我们选SimpleExecutor看看

public class SimpleExecutor extends BaseExecutor {

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //把锅甩给StatementHandler
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //把锅甩给StatementHandler
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

}

上面prepareStatement()方法内部调用StatementHandler的prepare()方法,这一般是我们定制插件的拦截方法。

可以发现最后都交给StatementHandler处理。StatementHandler有三个实现类:PreparedStatementHandler、CallableStatementHandler、RoutingStatementHandler,我们选择PreparedStatementHandler看一下:


public class PreparedStatementHandler extends BaseStatementHandler {

  @Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

}

好了,这里就是我们熟悉的JDBC操作了。

原文地址:https://www.cnblogs.com/xpz-python/p/10742323.html

时间: 2024-10-20 20:13:25

MyBatis 3源码解析(一)的相关文章

Mybatis SqlSessionTemplate 源码解析

在使用Mybatis与Spring集成的时候我们用到了SqlSessionTemplate 这个类. <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> 通过源码我们何以看到 SqlS

MyBatis 3源码解析(二)

MapperProxy 到了这了大家可能还有一个疑问,我调用的是DAO接口中的方法,和上面这些好像没关系.别急接下来我们就来看看二者是怎么联系起来的 在mybatis和Spring集合使用中,使用DAO时我们一般使用@Autowired注入,但是大家有没有一个疑问,DAO是一个接口,接口是不能创建对象的,这个是怎么完成的呢? Mybatis获取如何获取Mapper? 先来看第一个疑惑:使用mybatis操作数据库时,方法是这样的: SqlSessionFactory sessionFactory

Mybatis源码解析(一)(2015年06月11日)

一.简介 先看看Mybatis的源码结构图,Mybatis3.2.7版本包含的包共计19个,其他版本可能会少. 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得,而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例.

mybatis源码-解析配置文件(三)之配置文件Configuration解析(超详细, 值得收藏)

1. 简介 1.1 系列内容 本系列文章讲解的是mybatis解析配置文件内部的逻辑, 即 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 其背后的逻辑. 1.2 适合对象 了解如何使用 mybatis 来访问数据库.可参看<

Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao明明是我们定义的一个接口类,根本没有定义实现类,那这个userMapper是什么?是mybatis自动为我们生成的实现类吗? 为了更好的解释着两个问题,我们需要重新认识Configuration这个类. 但是在这之前,你需要了解一个概念(设计模式):JAVA设计模式-动态代理(Proxy)示例及说明

mybatis源码-解析配置文件(四)之配置文件Mapper解析

在 mybatis源码-解析配置文件(三)之配置文件Configuration解析 中, 讲解了 Configuration 是如何解析的. 其中, mappers作为configuration节点的一部分配置, 在本文章中, 我们讲解解析mappers节点, 即 xxxMapper.xml 文件的解析. 1 解析入口 在解析 mybatis-config.xml 时, 会进行解析 xxxMapper.xml 的文件. 在图示流程的 XMLConfigBuilder.parse() 函数中, 该

mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

相关文章推荐 mybatis 缓存的使用, 看这篇就够了 mybatis源码-解析配置文件(四)之配置文件Mapper解析 1. 简介 本文章主要讲解的是, xxxMapper.xml 文件中, cache 节点的源码. 2. 解析 XMLMapperBuilder.cacheElement() 方法主要负责解析 <cache> private void cacheElement(XNode context) throws Exception { if (context != null) {

Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? ??如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一样,本篇文章最最核心的要点就是 SqlSession实现数据库操作的源码解析.但按照惯例,我这边依然列出如下的问题: 1. SqlSession 是如何被创建的? 每次的数据库操作都会创建一个新的SqlSession么?(也许有很多同学会说SqlSession是通过 SqlSessionFactor

Spring IoC源码解析——Bean的创建和初始化

Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,MyBatis框架等组合使用. IoC介绍 IoC是什么 Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控