mybatis 之 执行流程

Posted better_hui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis 之 执行流程相关的知识,希望对你有一定的参考价值。

目录

一、使用

二、实现原理

原生的jdbc执行步骤

组件

Configuration

SqlSessionFactory

SqlSession

Executor

StatementHandler

ParameterHandler

ResultSetHandler

TypeHandler

MappedStatement

SqlSource

BoundSql

三、具体流程

1、生成sqlSessionFactory

1.1、解析配置文件

2、打开一个sqlSession

2.1、生成执行器

3、获取一个mapper

4、执行 , 我们以query为例

4.1、mapperMethod.execute

4.2、SqlSession.selectList()


一、使用

public static void main(String[] args) throws Exception{
    Reader reader = Resources.getResourceAsReader("Configuration.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    GoodsBiLogMapper mapper = sqlSession.getMapper(GoodsBiLogMapper.class);
    GoodsBiLog goodsBiLog = mapper.selectOne(1);
    goodsBiLog.setBatchNo("123123");
    mapper.update(goodsBiLog);
    System.out.println(goodsBiLog.toString());
}

总结步骤如下:

1、读取配置文件

2、根据配置文件生成SqlSessionFactory

3、生成sqlSession

4、获取mapper

5、执行sql语句

二、实现原理

img

mybatis底层使用的还是原生的jdbc,只是通过各种组件封装了jdbc的执行过程。

原生的jdbc执行步骤

public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        //注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        //获取连接
        Connection conn = DriverManager.getConnection(  "jdbc:mysql://localhost:3306/eesy","root","westos" ); //(url,user,password)
        //获取操作数据库的预处理对象
        PreparedStatement pstem = conn.prepareStatement("select * from account");   
        //执行SQL,得到结果集
        ResultSet rs = pstem.executeQuery();
        //遍历结果集
        while(rs.next()) {
            System.out.println(rs.getString("name"));
        }
        //释放资源
        rs.close();
        pstem.close();
        conn.close();
    }

组件

Configuration

mybatis解析后的配置信息,都保存在这个class里

SqlSessionFactory

mybatis的核心对象,它是单个数据库映射关系经过贬义后的内存镜像,我们可以通过configuration生成sqlSessionFactory对象 。

SqlSession

mybatis底层API , 表示与数据库的一次会话,完成必要数据库增删改查功能

Executor

mybatis的执行器,是mybatis的执行核心,负责sql的执行、缓存的维护

StatementHandler

封装了jdbc statement操作,负责对jdbc statement的操作。

有三个主要实现:

SimpleStatementHandler 简单的通用的

SimpleStatementHandler 预编译的

CallableStatementHandler 存储过程的

ParameterHandler

负责将用户传递的参数转化为jdbc statement所对应的数据类型

默认实现:

DefaultParameterHandler

ResultSetHandler

负责将jdbc返回的resultSet结果集转换为List类型集合

默认实现:

DefaultResultSetHandler

TypeHandler

负责java数据类型和jdbc数据类型之间的映射与转换

默认实现:

BaseTypeHandler

MappedStatement

维护<select|update|delete|insert>节点的封装

SqlSource

负责根据用户传递的parameterObject,动态的生成sql语句,并转换为boundsql

BoundSql

生成的sql语句以及相应的参数信息

img

三、具体流程

1、生成sqlSessionFactory

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
    //  1、new 一个 xmlConfigBuilder
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    //  2、xmlConfigBuilder.parse()  解析配置文件 
    //  3、根据解析的configuration对象 , new DefaultSqlSessionFactory对象  
    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.
    }
  }
}

1.1、解析配置文件

解析后的结果是生成configuration对象

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}
private void parseConfiguration(XNode root) {
  try {
      // properties属性 比如
      /**
      <properties resource="org/mybatis/example/config.properties">
          <property name="username" value="dev_user"/>
          <property name="password" value="F2Fa3!33TYyg"/>
      </properties>
      */
    propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      // 类型别名 比如
      /**
      <typeAliases>
            <typeAlias alias="Author" type="domain.blog.Author"/>
      </typeAliases>
      */
    typeAliasesElement(root.evalNode("typeAliases"));
      //插件 也就是拦截器
      /**
      <plugins>
          <plugin interceptor="org.mybatis.example.ExamplePlugin">
            <property name="someProperty" value="100"/>
          </plugin>
      </plugins>
      */
    pluginElement(root.evalNode("plugins"));
      //对象工厂 public class ExampleObjectFactory extends DefaultObjectFactory 
      /**
      <objectFactory type="org.mybatis.example.ExampleObjectFactory">
          <property name="someProperty" value="100"/>
      </objectFactory>
      */
    objectFactoryElement(root.evalNode("objectFactory"));
      //对象加工工厂 对指定对象进行特殊加工
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //各种配置信息
      /**
      <settings>
          <setting name="cacheEnabled" value="true"/>
          <setting name="lazyLoadingEnabled" value="true"/>
          <setting name="multipleResultSetsEnabled" value="true"/>
          <setting name="useColumnLabel" value="true"/>
          <setting name="useGeneratedKeys" value="false"/>
          <setting name="autoMappingBehavior" value="PARTIAL"/>
          <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
          <setting name="defaultExecutorType" value="SIMPLE"/>
          <setting name="defaultStatementTimeout" value="25"/>
          <setting name="defaultFetchSize" value="100"/>
          <setting name="safeRowBoundsEnabled" value="false"/>
          <setting name="mapUnderscoreToCamelCase" value="false"/>
          <setting name="localCacheScope" value="SESSION"/>
          <setting name="jdbcTypeForNull" value="OTHER"/>
          <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
       </settings>
      */
    settingsElement(root.evalNode("settings"));
      //环境及数据源配置
      /**
      <environments default="development">
          <environment id="development">
            <transactionManager type="JDBC">
              <property name="..." value="..."/>
            </transactionManager>
            <dataSource type="POOLED">
              <property name="driver" value="${driver}"/>
              <property name="url" value="${url}"/>
              <property name="username" value="${username}"/>
              <property name="password" value="${password}"/>
            </dataSource>
          </environment>
      </environments>
      */
    environmentsElement(root.evalNode("environments"));
      //数据库标识,根据这个标识mybatis会映射不同的sql
      /**
        <databaseIdProvider type="DB_VENDOR">
          <property name="SQL Server" value="sqlserver"/>
          <property name="DB2" value="db2"/>
          <property name="Oracle" value="oracle" />
        </databaseIdProvider>
      */
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 类型处理器 public class ExampleTypeHandler extends BaseTypeHandler<String> {
      /**
        <typeHandlers>
          <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
        </typeHandlers>
      */
    typeHandlerElement(root.evalNode("typeHandlers"));
      // mapper的解析
      /**
      <mappers>
          <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
          <mapper url="file:///var/mappers/BlogMapper.xml"/>
          <mapper class="org.mybatis.builder.AuthorMapper"/>
          <package name="org.mybatis.builder"/>
      </mappers>
      */
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

2、打开一个sqlSession

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);
      //生成一个执行器
    final Executor executor = configuration.newExecutor(tx, execType);
      //将执行器包装成一个sqlSession
    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();
  }
}

2.1、生成执行器

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;
}

3、获取一个mapper

public class MapperProxyFactory<T> {
    //我们一路点啊点啊的 进入到最终核心的逻辑,看到了一个核心的类 MapperProxy , 这就是jdk代理的handler 
    //我们后续所有的sql执行最终都要在这个方法的invoke方法里
    public T newInstance(SqlSession sqlSession) {
      final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
      return newInstance(mapperProxy);
    }
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {           mapperInterface }, mapperProxy);
  }
}

4、执行 , 我们以query为例

//下面看一下invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
          //如果是object的普通方法 ,则直接执行
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //获取mapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //这里就是拦截到了方法 , 并最终调用
    return mapperMethod.execute(sqlSession, args);
  }

4.1、mapperMethod.execute

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  if (SqlCommandType.INSERT == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.insert(command.getName(), param));
  } else if (SqlCommandType.UPDATE == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.update(command.getName(), param));
  } else if (SqlCommandType.DELETE == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.delete(command.getName(), param));
  } else if (SqlCommandType.SELECT == command.getType()) {
    if (method.returnsVoid() && method.hasResultHandler()) {
      executeWithResultHandler(sqlSession, args);
      result = null;
    } else if (method.returnsMany()) {
      result = executeForMany(sqlSession, args);
    } else if (method.returnsMap()) {
      result = executeForMap(sqlSession, args);
    } else {
        //查询的方法 最终会走到这里
      Object param = method.convertArgsToSqlCommandParam(args);
      result = sqlSession.selectOne(command.getName(), param);
    }
  } else {
    throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName() 
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

4.2、SqlSession.selectList()

public <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;
  }
}

sqlSession.selectList()

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

executor.query()

 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
     //根据传入的参数 获取 绑定的sql
   BoundSql boundSql = ms.getBoundSql(parameter);
     //生成缓存的key
   CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
     //查询
   return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

以上是关于mybatis 之 执行流程的主要内容,如果未能解决你的问题,请参考以下文章

mybatis 之 执行流程

精通Mybatis之动态sql全流程解析

精通Mybatis之动态sql全流程解析

mybatis源码-解析配置文件之解析的流程

mybatis执行流程

mybatis执行流程