mybatis 一次select操作执行流程分析

Mybatis源码解析之执行流程解析 Mybatis源码解析之核心类分析 Mybatis源码解析之初始化分析 本篇文章将在前两篇文章的基础上解析Mybatis执行sql的流程。 一、 映射集mappers解析 对于在原生mybatis中的sql执行,需要在配置文件中通过标签引入mapper.xml文件。前文已经提到,对于xml配置文件,mybatis在XMLConfigBuilder#parseConfiguration(Xnode)... 阅读详情

1.测试代码

package com.testmybatis;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

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 org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.testmybatis.dao.TestMapper;
import com.testmybatis.model.Test;

public class testlanjie {
	
	private static Logger log=Logger.getLogger(testlanjie.class);
	
	public static void main(String args[]){
		List<Test> tests=null;
		try {
			String resource = "com/testmybatis/mybatis-config.xml";
			InputStream inputStream = Resources.getResourceAsStream(resource);
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			SqlSession session = sqlSessionFactory.openSession();
			try {
				TestMapper mapper=session.getMapper(TestMapper.class);
				tests=mapper.test();
				session.commit();
			} finally {
				session.close();
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(tests!=null)
			log.info(JSON.toJSONString(tests));
	}
	
}

2.流程分析

第一步构造SqlSessionFactory

String resource = "com/testmybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactoryBuilder下的build函数

public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
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);
  }

先用配置文件的文件流对象构造一个XMLConfigBuilder对象,在调用parse函数得到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 settings = settingsAsPropertiess(root.evalNode("settings"));
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      loadCustomVfs(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);
    }

其中mybatis主要的配置都在这里完成,存放在configuration中,本次分析会用到的两个函数是environmentsElement和mapperElement,一个是构造datasource,一个是构造存放所有MapperProxyFactory的MapperRegistry,相应的源代码如下

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

其中MapperRegistry对象中存放所有MapperProxyFactory对象的容器是一个hashmap

 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

在构造factory的最后返回了一个DefaultSqlSessionFactory对象,并将创建好的Configuration对象当作参数传给了该对象,成为了他的成员变量。

第二步得到SqlSession对象

SqlSession session = sqlSessionFactory.openSession();

跟踪获取session的过程

 @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
 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);
      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();
    }
  }

最终得到的DefaultSqlSession中包含两个重要对象,一个是从configuration里来的Enviroment,另外一个是包含TransactionFactroy对象的Executor对象。

其中获得Executor对象的函数如下

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

这其中应用的包装模式(将SimpleExecutor对象包装成CachingExecutor)和责任链模式(给CachingExecutro对象装配上plugin)

第三步 根据传入的接口信息生成动态代理

TestMapper mapper=session.getMapper(TestMapper.class);

继续跟踪代码

 @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
@SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
 public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
@SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

大致流程就是先从configuration中找mapperRegistry,再从mapperRegistry中找当初存放mapperfactory的hashmap,再从这个hashmap中找到相应的mapperfactory,最后mapperfactory构造了一个mapperproxy对象,并调用了java的Proxy类,构造了一个动态代理对象的实例。

看一下MapperProxy的代码

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.binding;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

该class实现了InvocationHandler接口,我们知道java中的动态代理模式需要代理class来实现InvocationHandler接口,

而最后动态得到的实例调用函数实际上调用的是代理对象的invoke的函数,所以我们第四步的入口就在这个class里

第四步 调用接口函数test,执行sql语句

由代理对象中的inove函数知道,函数最后执行的其实是MapperMethod类的execute函数。

一路跟踪代码到SimpleExecutor下的doQuery函数中

 @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());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

此处生成了一个StatementHandler对象和一个Statement对象

先看看Statement的生成过程

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

先生成Connection对象 ,用的也是代理模式

 protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
    InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
    ClassLoader cl = Connection.class.getClassLoader();
    return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
  }

然后生成preparestatement

 @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 {
    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() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

最后调用了connec tion的prepareStatement函数,而因为connection其实是一个代理对象,他实际调用的函数是

@Override
  public Object invoke(Object proxy, Method method, Object[] params)
      throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }    
      if ("prepareStatement".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }        
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        //实际调用创建prepareedstatement对象的地方
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }        
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("createStatement".equals(method.getName())) {
        Statement stmt = (Statement) method.invoke(connection, params);
        stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else {
        return method.invoke(connection, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) {
    InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack);
    ClassLoader cl = PreparedStatement.class.getClassLoader();
    return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
  }

发现原来这个pareparedStatement对象还是个代理对象

继续跟踪代码

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

调用了之前生成的preparedStatement对象的execute函数,其实也就是代理对象的inovke函数

@Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }          
      if (EXECUTE_METHODS.contains(method.getName())) {
        if (isDebugEnabled()) {
          debug("Parameters: " + getParameterValueString(), true);
        }
        clearColumnInfo();
        if ("executeQuery".equals(method.getName())) {
          ResultSet rs = (ResultSet) method.invoke(statement, params);
          return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
        } else {
          return method.invoke(statement, params);
        }
      } else if (SET_METHODS.contains(method.getName())) {
        if ("setNull".equals(method.getName())) {
          setColumn(params[0], null);
        } else {
          setColumn(params[0], params[1]);
        }
        return method.invoke(statement, params);
      } else if ("getResultSet".equals(method.getName())) {
        ResultSet rs = (ResultSet) method.invoke(statement, params);
        return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
      } else if ("getUpdateCount".equals(method.getName())) {
        int updateCount = (Integer) method.invoke(statement, params);
        if (updateCount != -1) {
          debug("   Updates: " + updateCount, false);
        }
        return updateCount;
      } else {
        return method.invoke(statement, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

此时sql语句执行完成,之后是获取结果集

第五步 获取结果集

@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

此处调用stmt.getResultSet(),然后他又进了代理对象的invoke函数

@Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }          
      if (EXECUTE_METHODS.contains(method.getName())) {
        if (isDebugEnabled()) {
          debug("Parameters: " + getParameterValueString(), true);
        }
        clearColumnInfo();
        if ("executeQuery".equals(method.getName())) {
          ResultSet rs = (ResultSet) method.invoke(statement, params);
          return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
        } else {
          return method.invoke(statement, params);
        }
      } else if (SET_METHODS.contains(method.getName())) {
        if ("setNull".equals(method.getName())) {
          setColumn(params[0], null);
        } else {
          setColumn(params[0], params[1]);
        }
        return method.invoke(statement, params);
      } else if ("getResultSet".equals(method.getName())) {
        ResultSet rs = (ResultSet) method.invoke(statement, params);
        return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
      } else if ("getUpdateCount".equals(method.getName())) {
        int updateCount = (Integer) method.invoke(statement, params);
        if (updateCount != -1) {
          debug("   Updates: " + updateCount, false);
        }
        return updateCount;
      } else {
        return method.invoke(statement, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
 public static ResultSet newInstance(ResultSet rs, Log statementLog, int queryStack) {
    InvocationHandler handler = new ResultSetLogger(rs, statementLog, queryStack);
    ClassLoader cl = ResultSet.class.getClassLoader();
    return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
  }

可以看到这还是一个代理模式,创建了一个用来获取结果集的对象ResultSetLogger,看一下他的invoke函数

 @Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }    
      Object o = method.invoke(rs, params);
      if ("next".equals(method.getName())) {
        if (((Boolean) o)) {
          rows++;
          if (isTraceEnabled()) {
            ResultSetMetaData rsmd = rs.getMetaData();
            final int columnCount = rsmd.getColumnCount();
            if (first) {
              first = false;
              printColumnHeaders(rsmd, columnCount);
            }
            printColumnValues(columnCount);
          }
        } else {
          debug("     Total: " + rows, false);
        }
      }
      clearColumnInfo();
      return o;
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

从ResultSetLogger对象遍历出结果集还要经过一步处理得到最终需要返回类型的结果集

第六步 封装结果集

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        if (resultHandler == null) {
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }
 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(resultObject);
      boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      resultObject = foundValues ? resultObject : null;
      return resultObject;
    }
    return resultObject;
  }

这一步最核心的代码就是上面这个函数,可以看到他是由configuration生成一个MetaObject,在通过applyAutomaticMappings函数,将数据库结果中相应字段的值填充到MetaObject中,代码如下

 private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (autoMapping.size() > 0) {
      for (UnMappedColumnAutoMapping mapping : autoMapping) {
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
          // gcode issue #377, call setter on nulls (value is not 'found')
          metaObject.setValue(mapping.property, value);
        }
      }
    }
    return foundValues;
  }

最后将得到的封装好的对象放入一个list中返回结果,关闭数据库连接,一次完整的sql语句执行流程至此完结 。

使用VMware Converter将物理机转换为虚拟机时,目标虚拟机的converter-helper不启动的问题解决方法 摘要:在使用VMware Converter将Linux物理机转换为虚拟机时,遇到转换进度卡在1%且无法连接helper server的问题。主要原因是未给helper server配置与源主机互通的IP地址,以及目标虚拟机未正确加载helper server镜像文件或未设置光驱为第一启动项。解决方法包括:1)在转换配置中手动设置helper server的IP地址;2)检查并手动上传helper server镜像文件到目标虚拟机;3)确保虚拟机的光驱设置为第一启动项。正确配置后,转换进程即可正常进行。(1 阅读详情

相关推荐

高通camera open代码流程(hal到kernel)

内容:简单介绍Camera open代码调用流程,从HAl到kernel。(api1+hal1) 前言 Android camera api和hal对应关系。安卓在版本更新过程中,camera相关代码也经历了几个版本的更新,表现为camera HAL版本更新(HAL1->HAL2->HAL3),camera API版本更新(camera API1->camera API2),接下...

oxoo123的专栏 4276

17. Mybatis 查询操作-select

1. select 标签 select 标签是用于定义插入语句的, select 标签是Mybatis 中最复杂的标签了. 1.1 常用属性 select 的属性和子标签比较多, 常用属性有: id: sql 片段在命名空间内的唯一标识. 和mapper 中方法名保持一致 useGeneratedKeys: 是否回填自动生成的主键 keyProperty: 主键回填到哪个属性 keyColumn: 主键回填的字段名, 可省略 parameterType: 参数类型, 通常都可以省略. flushCac.

u011250186的博客 8378

银河麒麟arm64 V10 SP1桌面系统高效部署onnxruntime推理环境实战

本文详细介绍了在银河麒麟arm64 V10 SP1桌面系统上高效部署onnxruntime推理环境的完整实战流程。从系统环境准备、关键依赖安装,到onnxruntime的安装验证,并以YOLOv5模型部署为例进行性能对比分析,提供了在国产化飞腾D2000平台上进行AI边缘推理的实用指南和避坑技巧。

ss78901的博客 636

Mybatis如何执行Select语句

撸起袖子就是干 二话不说,先来一张流程图,Mybatis六剑客,如下: 上图中的这六剑客在前面两篇文章中已经介绍的非常清楚了,此处略过。为什么源码解析的每一篇文章中都要放一张这个流程图呢?因为Mybatis底层就是围绕着这六剑客展开的,我们需要从全局掌握Mybatis的源码究竟如何执行的。 测试环境搭建 举个栗子:根据用户id查询用户信息,Mapper定义如下: List<UserInfo>selectList(@Param("userIds")List..

Pastxu的小屋 829

WhatsApp注册流程逆向分析:从协议解析到风控对抗实战

移动应用安全分析的核心在于理解客户端与服务端的交互协议与验证机制。通过逆向工程,可以深入剖析应用的数据加密、传输与身份验证原理,这对于构建安全模型、进行自动化测试和风控策略研究具有重要技术价值。在即时通讯、金融支付等高安全要求场景中,掌握应用注册流程的底层实现是评估其安全性的关键。本文聚焦于使用动态分析工具如Frida进行Hook和流量抓包,结合设备指纹识别与模拟器检测技术,详细解析了大型应用的注册风控体系,为安全研究人员和开发者提供了从环境搭建到实战对抗的完整方法。

weixin_33935636的博客 315

一文读懂OpenClaw:核心组件与工作原理深度解析

Agent核心:负责理解意图、规划任务、协调执行消息系统:统一的消息格式,实现组件间解耦工具注册表:可扩展的工具系统,支持自定义工具模型适配层:多模型支持,统一API接口安全机制:沙箱隔离、权限确认、操作审计性能优化:并行执行、智能缓存、增量更新理解这些核心组件和工作原理,将帮助你更好地使用OpenClaw,也为后续开发自定义扩展打下坚实基础。在下一篇文章中,我们将详细介绍OpenClaw的界面操作,包括命令行与交互模式的各种技巧。

gaoxiang_120的专栏 833

MyBatis执行流程

通过DefaultSqlSession执行查询的时候例如调用org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds) 方法。1、首选是org.apache.ibatis.session.SqlSessionFactory调用。然后调用SimpleExecutor中的doQuery方法。

daitianze的博客 305

iStoreOS等软路由插件卸载的残留?分享一款软路由高级卸载工具,自定义卸载,轻松解决卸载残留问题

iStoreOS用户看过来!👀 还在被插件包残留困扰吗?本文分享一款“高级卸载”的插件,助你深度清理iStoreOS系统,彻底解决软件卸载不净的痛点,告别频繁恢复出厂设置的烦恼!🚀

XiaoqiangClub的博客 1541

Mybatis-plus的使用(一)

前言: Mybatis在持久层框架中还是比较流行的,现在一般项目都是基于SSM或者SpringBoot+Mybatis。虽然Mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但如果都要通过SQL语句进行,就必须写大量的xml文件。比如一般的单表操作,CRUD等简单操作,这些如果每一个都写一段SQL语句,就显得非常麻烦和不必要。Mybatis-plus就很好的解决了这个问题。 一、Mybatis-plus简介: Mybatis-Plus 是一个 Mybatis框架的增强工具,在 Mybati

qq_42386143的博客 586

8.26华为OD机试真题 新系统 - 字符串回文判 (Java/Py/C/C++/Js/Go)

8.26华为OD机试真题 新系统 - 字符串回文判 (Java/Py/C/C++/Js/Go)

算法大师! 1234

Python从0到100(二):Python语言介绍及第一个Pyhon程序

Python从0到100(二):Python语言介绍及第一个Pyhon程序。与C和Java比,Python的学习成本和难度曲线不是低一点,更适合新手入门,自底向上的技术攀爬路线。

拥有百余位万粉技术博主、200余位可合作代发博主,私域技术社群规模达千人,商务合作私信 5440

MyBatis 中的 select 用法

使用过 MyBatis 开发过应用程序你,应该都使用过 select 元素吧,select 元素是我们最常用也是功能最强大的 SQL 语句。在使用原生 JDBC 时,需要我们手写查询语句,并且对结果集进行手动处理,将结果映射到对象的属性中。而我们在使用 MyBatis 框架时,只需要在 XML 中添加一个 select 元素和写一条 SQL 语句,同时还需做一些简单配置,就可以将查询的结果直接映射到对象中。在执行 select 语句之前,还需要定义参数,这些参数可以是简单的参数类型,也可是是复杂的参数类型。

qq_38991247的博客 5158

ABAP Module Pool 编程入门:把 Table Control 用透,在 Dynpro 里做出可编辑的列表界面

摘要:Table Control是SAP GUI中用于对话式事务的多行数据交互控件,具备滚动、选择、修改等功能。它通过CXTAB_CONTROL/CXTAB_COLUMN结构实现动态控制,需在PBO/PAI中配合LOOP循环处理数据流。开发时需注意LINES、TOP_LINE等关键字段设置,并采用并行循环简化编码。典型应用包括订单行维护、主数据管理等场景,支持多选删除、列排序等业务需求。(150字)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 343

深入理解MyBatis(四)—MyBatis的Select操作执行流程

深入理解MyBatis(四)—MyBatis的Select操作执行流程 MyBatis的操作主要分为update类和select类;insert操作,delete操作,update操作都是通过update操作实现; 个人主页:tuzhenyu’s page 原文地址:深入理解MyBatis(四)—MyBatis的Select操作执行流程 Select操作流程 (1

tuzhenyu的博客 3557

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

MyBatis的执行流程

特性一级缓存二级缓存作用范围SqlSession级别Mapper级别默认状态默认开启需要手动开启生命周期SqlSession关闭后失效跨SqlSession共享返回对象同一个对象引用对象拷贝。

动动发财的小手点点赞 1665

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Mybatis执行SQL流程(六)之Executor执行器

定义了执行器的通用骨架(如查询/更新流程、事务提交/回滚、本地缓存管理),将具体操作延迟到子类实现(SimpleExecutor、ReuseExecutor、BatchExecutor)中执行对方法进行了区分,Sqlsession调用 Executor 对应的方法执行sql。对于新增来说,涉及到主键自动生成keyGenerator、一级缓存清除、参数处理、typeHandler。对于查询来说,主要涉及到二级缓存、一级缓存、参数处理、结果集处理。真正执行sql是交给jdbc执行的。

qq_38077040的博客 815

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【Mytais系列】Select语句执行流程

和缓存更新机制,有助于优化数据访问层设计,平衡性能与数据一致性。的优先级策略,最大化利用缓存提升性能。理解其内部组件协作(如。用户操作 MyBatis 的入口,封装了会话状态和数据库操作。执行器,负责缓存查询和 SQL 执行(分为。详细描述的 MyBatis 执行一条。处理结果集(映射为 Java 对象)。装饰器模式实现,添加二级缓存逻辑。处理 SQL 语句(创建。绑定 SQL 参数。实际执行数据库操作。

技术改变世界 605

mybatis源码分析执行流程

前言 在上一篇,我们了解了mybatis的整体执行流程,以及内部的各个组件在执行过程中的功能,下面我们来通过源码的方式对其中比较重要的几个组件在执行过程的使用进行简单的分析与说明 环境准备 基于第一篇搭建好的源码过程和demo,我们通过一个简单的查询进行分析,完整的代码如下: userDao public interface UserDao { User findUserById(int id); } MyUserMapper.xml <mapper namespace="com.congge.

congge 1102

Mybatis执行select流程

AccountMapper 从图中可知,AccountMapper的代理就是1个MapperProxy 主线流程 MapperProxy public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSes..

kongkong的专栏 667

源码分析mybatis-select执行流程

从Opensession开始: private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final Tra..

weixin_53847158的博客 198

Mybatis如何执行Select语句,你真的知道吗?

前言 本篇文章是Myabtis源码分析的第三篇,前两篇分别介绍了Mybatis的重要组件和围绕着Mybatis中的重要组件教大家如何阅读源码的一些方法。 今天接上一篇,围绕Mybatis中的selectList()来看一看Mybatis底层到底做了什么,有什么高级的地方。 环境准备 本篇文章讲的一切内容都是基于Mybatis3.5和SpringBoot-2.3.3.RELEASE。 由于此篇文章是基于前两篇文章的基础之上,因此重复的内容不再详细赘述了。 撸起袖子就是干

java_xiaoo的博客 727

mybatis一个方法执行多条sql_MyBatis一次执行多条SQL语句的操作

有个常见的场景:删除用户的时候需要先删除用户的外键关联数据,否则会触发规则报错。解决办法不外乎有三个:1、多条sql分批执行;2、存储过程或函数调用;3、sql批量执行。今天我要说的是MyBatis中如何一次执行多条语句(使用mysql数据库)。1、修改数据库连接参数加上allowMultiQueries=true,如:hikariConfig.security.jdbcUrl=jdbc:mysq...

weixin_42365141的博客 2512

MyBatis Select操作深度解析

在当今的软件开发领域,数据库操作是任何应用程序不可或缺的一部分。尤其是在处理大量数据时,如何高效、安全地从数据库中检索信息成为了一个关键问题。想象一下,一个电商网站在高峰时段,用户对商品信息的查询需求激增,如果数据库查询效率低下,不仅会影响用户体验,还可能造成服务器资源的浪费。为了解决这一问题,MyBatis框架中的select操作应运而生。MyBatis的select操作是框架的核心功能之一,它允许开发者以声明式的方式从数据库中查询数据。

AIHacksCash的博客 873

MyBatis 源码分析之 Select 语句执行(上)

内容来自【自学星球】欢迎大家来了解我的星球,和星主(也就是我)一起学习 Java ,深入 Java 体系中的所有技术。我给自己定的时间是一年,无论结果如何,必定能给星球中的各位带来点东西。

技术分享、唠嗑水文、如有不周、还望海涵 1320

手写MyBatis第36弹:MyBatis执行流程中SQL命令类型解析

本文深入解析MyBatis框架中SQL命令类型的解析机制,揭示其如何通过XML配置和注解方式确定SQL操作类型(SELECT/INSERT/UPDATE/DELETE),并详细阐述该类型在MappedStatement创建及Executor执行路由中的关键作用。文章从解析原理、源码实现到实际应用,系统性地展示了SQL命令类型如何影响MyBatis的执行决策路径、缓存行为及事务管理,帮助开发者深入理解框架底层工作原理。通过对比XML节点解析与注解解析的异同,结合典型配置示例,为优化MyBatis使用和问题排查

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 1629
上一篇: java cglib
下一篇: ReentrantLock 原理分析
caideb
博客等级 码龄15年 33粉丝 176原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值