mybatis执行流程源码分析

本文通过对mybatis执行流程的源码分析,介绍了从项目搭建到SqlSessionFactory创建的步骤。首先,按照官方教程配置环境,然后详细解析了SqlSessionFactoryBuilder如何从配置文件中构建SqlSessionFactory。在SqlSession打开后,逐步剖析了mybatis如何进行初始化和操作数据库的准备工作,但尚未涉及具体的JDBC内容。

mybatis执行流程源码分析

mybatis官网 https://mybatis.org/mybatis-3/

需要准备的环境:mysql、maven、idea

根据官网的入门教程进行项目搭建

项目结构图

在这里插入图片描述
pom文件配置
我这项目存在父工程,可以把parent标签删除

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>parent-mybatis</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mybatis-demo1</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    </dependencies>

</project>

mybatis configuration 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis-test"/>
                <property name="username" value="xxx"/>
                <property name="password" value="xxx"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--        配置资源文件-->
        <mapper resource="DeptMapper.xml"/>
    </mappers>
</configuration>

mybatis 资源文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        
        <!-- namespace 代表了一个唯一的类,如果唯一可以去掉包名如:
        <mapper namespace="Dept"> 或者通过配置文件可以进行别名配置(自行百度。)-->
        
<mapper namespace="com.lhl.demo1.Dept">
    <insert id="insertDept" >
        insert into dept(name,loc) values(#{name},#{loc})
    </insert>
</mapper>

实体类自行加getset或者用lombok

public class Dept {

    private int id;

    private String name;

    private String loc;
}

测试类

package com.lhl.demo1;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;


public class TestMain {

    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Dept dept =new Dept();
        dept.setLoc("湖北");
        dept.setName("金融事业单位");
        int insertDept = sqlSession.insert("insertDept", dept);
        sqlSession.commit();
        System.out.println(insertDept);
    }


}

源码解析
在这里插入图片描述

1、首先在第22行打下断点,14-22行就是一个读取mybatis配置文件的输入流通过debug的变量可以看到获取到的是一个缓冲输入流 BufferedInputStream对象
2、在第22行可以发现调用了SqlSessionFactoryBuilder的builder方法获取到了一个SqlSessionFactory对象。首先进入SqlSessionFactoryBuilder类下发现使用默认空参构造方法无任何代码存在。

在这里插入图片描述

builder 方法是一个重载方法,最终调用方法提供三个参数的builder方法

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    	// 第一步调用 XMLConfigBuilder 三个参数的构造方法
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 调用parse的parse方法返回Configuration,包含了配置文件中的所有属性值,调用build方法传递Configuration到DefaultSqlSessionFactory构造方法生成DefaultSqlSessionFactory实例
      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);
  }


// XMLConfigBuilder类
 public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  // 根据mybatis配置文件dtd约束文档的标签进行解析xml文件封装成configuration对象的属性
private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      //第一个举例说明
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

 private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      //解析出来的值最终封装成立了configuration的属性
      configuration.setVariables(defaults);
    }
  }

第23行 SqlSession sqlSession = sqlSessionFactory.openSession();

// DefaultSqlSessionFactory类的实现
 @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  // 打开session从数据源,三个参数值如下图
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
    // 从configuration获取各种配置信息,
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //生成事务实例tx
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据配置信息生成新的执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 最终创建DefaultSqlSession对象
      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();
    }
  }

在这里插入图片描述

//Configuration类
// mybatis实现了多个执行器
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;
  }

在这里插入图片描述

到这步也未看到跟jdbc有关的内容,都是基于配置文件mybatis-config.xml做的初始化工作,继续进行下步工作,初始化并赋值dept对象,然后调用sqlSession对象的insert方法

// DefaultSqlSession
 @Override
  public int insert(String statement, Object parameter) {
  // 调用update方法
    return update(statement, parameter);
  }

  @Override
  public int update(String statement) {
    return update(statement, null);
  }

  @Override
  public int update(String statement, Object parameter) {
    try {
    // DefaultSqlSession存在一个属性dirty ,赋值为true,初始值看到是false,先不管。。
      dirty = true;
      // 调用configuration获取MappedStatement 对象,跟源码发现是在初始化时创建了一个
      //protected final Map<String, MappedStatement> mappedStatements = new //StrictMap<MappedStatement>("Mapped Statements collection")
   //   .conflictMessageProducer((savedValue, targetValue) ->
    //      ". please check " + savedValue.getResource() + " and " + //targetValue.getResource()); 来进行内存存储的。从下面debug内容可以看到,他是根据statement来进行查找到已经配置好的内容,如类型信息,namespace,sql语句等。。
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 调用executor的update方法
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 private Object wrapCollection(final Object object) {
    return ParamNameResolver.wrapToMapIfCollection(object, null);
  }
// 包装对象,将Collection、array类型进行包装
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


 @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 根据ms创建StatementHandler 对象
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      // 根据StatementHandler 对象创建Statement 对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行最终的stmt对象
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }
 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //jdbc操作 获取数据库连接
    Connection connection = getConnection(statementLog);
    // 创建prepareStatement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    //处理prepareStatement对象
    handler.parameterize(stmt);
    return stmt;
  }

  protected Connection getConnection(Log statementLog) throws SQLException {
  // 从transaction对象的datasource连接池里拿出来一个连接
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

// RoutingStatementHandler
 @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  // 委托
    return delegate.prepare(connection, transactionTimeout);
  }

// BaseStatementHandler 
@Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
    // 实例化
      statement = instantiateStatement(connection);
     // 设置属性如超时时间等
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }


@Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
   // 获取sql语句
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

//对sql语句的参数进行赋值
 @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

@Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //jdbc
    ps.execute();
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    //返回影响的行数
    return rows;
  }

 @Override
  public void commit() {
    commit(false);
  }

@Override
  public void commit(boolean force) {
    try {
    // force初始为false
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 private boolean isCommitOrRollbackRequired(boolean force) {
 	//dirty 用来进行是否提交的
    return (!autoCommit && dirty) || force;
  }

// 只有进行了
 @Override
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    clearLocalCache();
    flushStatements();
    // 如果为false,不进行事务提交
    if (required) {
      transaction.commit();
    }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值