Mybatis can run source address: github.com/MrZhang-bad… Or gitee.com/ZhangHua-Ba…

The project contains

  • Mybatis source
  • Com. Making. PageHelper source code

You can learn MyBatis source code plug-in part of the time to directly configure PageHelper to deepen the understanding of plug-in development, applied to work projects

A summary.

1. Introduction of Mybatis

MyBatis is an excellent persistence layer framework that supports custom SQL, stored procedures, and advanced mappings. MyBatis eliminates almost all of the JDBC code and the work of setting parameters and getting result sets. MyBatis configures and maps primitive types, interfaces, and Java POJOs to records in the database using simple XML or annotations. — from official documents

2. Architecture design

3. Operating principle

Two. Detailed implementation process

1. Obtain the Session process

(0) Example

public static SqlSession getSqlSession(a) throws IOException { 
    / / read mybatis - config. XML
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    / / SqlSessionFactory is established
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
	/ / get the SqlSession
    SqlSession sqlSession = factory.openSession();                                
	return sqlSession;                                                            
}                                                                                 
Copy the code

(1) Read the configuration file

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
Copy the code
  • To obtainmybatis-config.xmlFile input stream

(2) Generate SqlSessionFactory from the configuration file

The build() method actually calls the following method
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {  
  try {       
    // Encapsulate the XMLConfigBuidler from the inputStream to build the Configuration with the parser. Parase ()
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);             
    // The parse method returns Configuration
    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.                          }}}Copy the code
Let’s look at the parse.parse() method
public Configuration parse(a) {                                                 
  // Check whether it has been translated
  if (parsed) {                                                                
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }                                                                            
  parsed = true;                                                               
  parseConfiguration(parser.evalNode("/configuration"));                       
  return configuration;                                                        
}                                                                              
Copy the code
// The input parameter is the XNode node in the wrapped XMLConfigBuidler
private void parseConfiguration(XNode root) {                                               
  try {                                                                                     
    // Start the Configuration from mybatis-config. XML
    propertiesElement(root.evalNode("properties"));                                         
    Properties settings = settingsAsProperties(root.evalNode("settings"));                  
    loadCustomVfs(settings);                                                                
    loadCustomLogImpl(settings);                                                            
    typeAliasesElement(root.evalNode("typeAliases"));                                       
                                                                                            
    // Register the Plugin with the Configuration
    pluginElement(root.evalNode("plugins"));                                                
    objectFactoryElement(root.evalNode("objectFactory"));                                   
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));                     
    reflectorFactoryElement(root.evalNode("reflectorFactory"));                             
    settingsElement(settings);                                                                                    
    environmentsElement(root.evalNode("environments"));                                     
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));                         
    typeHandlerElement(root.evalNode("typeHandlers"));
      
    
       tags are registered in the MapperRegister in the Configuration
    mapperElement(root.evalNode("mappers"));          
  } catch (Exception e) {         
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: "+ e, e); }}// Let's look at the code that parses the Plugin and Mapper


// Parse the Plugin code
private void pluginElement(XNode parent) throws Exception {                                                            
  if(parent ! =null) {                                                                                                
    // Add all Plugins from the Plugins TAB to the configuration
    for (XNode child : parent.getChildren()) {                                                                         
      String interceptor = child.getStringAttribute("interceptor");                                                    
      Properties properties = child.getChildrenAsProperties();                                                         
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
      interceptorInstance.setProperties(properties);
      // Look at the code hereconfiguration.addInterceptor(interceptorInstance); }}}// In the Configuration class, add an interceptor (plug-in) to the interceptorChain.
public void addInterceptor(Interceptor interceptor) { 
  interceptorChain.addInterceptor(interceptor);       
}

/ / parsing Mapper
// This code shows the four ways to configure mapper
private void mapperElement(XNode parent) throws Exception {                                                                              
  if(parent ! =null) {                                                                                                                  
    for (XNode child : parent.getChildren()) {                                                                                           
      // The four ways to get mapper
      // The first 
      
        tag is configured with 
       
        .
       
      if ("package".equals(child.getName())) {                                                                                           
        String mapperPackage = child.getStringAttribute("name");                                                                         
        configuration.addMappers(mapperPackage);                                                                                         
      } else {                                                                                                                           
        // Obtain the path to the xxxmapper. XML file
        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);                                                                                    
          try(InputStream inputStream = Resources.getResourceAsStream(resource)) {                                                       
            // Read the xxxMapper. XML file and wrap it into XMLMapperBuilder
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); 
            // Start the translation according to the xxxmapper. XML file
            // The second and third cases do not have addMapper in this function, but they do have addMapper in the translation processmapperParser.parse(); }}else if (resource == null&& url ! =null && mapperClass == null) {                                                             
          // The third is
          ErrorContext.instance().resource(url);                                                                                         
          try(InputStream inputStream = Resources.getUrlAsStream(url)){                                                                  
            XMLMapperBuilder mapperParser = newXMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); }}else if (resource == null && url == null&& mapperClass ! =null) {                                                             
          / / a fourth is in < mappers > tags used within < mapper class = "com. Example. Demo. Mapper. XXXMapper / >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.");              
        }                                                                                                                                
      }                                                                                                                                  
    }                                                                                                                                    
  }                                                                                                                                      
}                                                                                                                                        
Copy the code
Next take a look at build(parse.parse()) in the build method
// Generate a default SqlSessionFactory based on the Configuration
public SqlSessionFactory build(Configuration config) { 
  return new DefaultSqlSessionFactory(config);         
}                                                      
Copy the code

(3) SqlSessionFactory obtain SqlSession

SqlSession sqlSession = factory.openSession();

//openSession() actually calls the openSession method in DefaultSqlSessionFactory
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);                                  
    // Generate the actuator
    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(); }}// Let's look at the method to generate the actuator
// If there is a custom plugin, it is added here
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // If executorType is null, set it to the default SimpleExecutor
    executorType = executorType == null ? defaultExecutorType : executorType;
    // The value of defaultExecutor is not null. // The value of defaultExecutor is not null
    // The following line of code always returns executorType, and does not go to executorType. SIMPLE
    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);
    }
    /** * Add plugins to all interceptors that intercept Executor */
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
Copy the code

2. Obtain the Mapper procedure

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Copy the code
  • Actually calls the getMapper method in DefaultSqlSession implementation class of SqlSession
  • And the UserMapper returned here is a UserMapper proxy class, which is the “enhanced” UserMapper
@Override                                              
public <T> T getMapper(Class<T> type) {                
  return configuration.getMapper(type, this);          
} 

// Look at the getMapper method in Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {                                      
  // In the XMLMapperBuilder, we saved the Mapper to the Confuguration of the mapperRegistry
  // Create an xxxMapper. class proxy class from the mapperRegistry. // Create a proxy class from the factory class.
  return mapperRegistry.getMapper(type, sqlSession);                                                
}  

Copy the code
  • Focus on the mapperRegistry. GetMapper (Type, sqlSession) method (important)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {                                                     
  // The previous process of registering mapper in The Configuration Registry is
  // Mapper MapperProxyFactory is stored in knownMappers
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);                 
  if (mapperProxyFactory == null) {                                                                                
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");                           
  }                                                                                                                
  try {                                                                                                            
    // After the agent factory class is obtained, the dynamic proxy mapper is generated
    return mapperProxyFactory.newInstance(sqlSession);                                                             
  } catch (Exception e) {                                                                                          
    throw new BindingException("Error getting mapper instance. Cause: "+ e, e); }}public <T> boolean hasMapper(Class<T> type) {                                                                      
  return knownMappers.containsKey(type);                                                                           
}                                                                                                                  
Copy the code
  • Now recall the addMapper method that was called earlier in MapperRegistry
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 {                                                                                              
      Mapper's agent factory class is placed under knownMappers
      knownMappers.put(type, new MapperProxyFactory<>(type));                                                                        
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);                      
      parser.parse();                                                                                  
      loadCompleted = true;                                                                            
    } finally {                                                                                        
      if(! loadCompleted) { knownMappers.remove(type); }}}}// Look at the agent factory class
public class MapperProxyFactory<T> {                                                                                    
                                                                                                                        
  private final Class<T> mapperInterface;                                                                               
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();                               
                                                                                                                        
  public MapperProxyFactory(Class<T> mapperInterface) {                                                                 
    this.mapperInterface = mapperInterface;                                                                             
  }                                                                                                                     
                                                                                                                        
  public Class<T> getMapperInterface(a) {                                                                                
    return mapperInterface;                                                                                             
  }                                                                                                                     
                                                                                                                        
  public Map<Method, MapperMethodInvoker> getMethodCache(a) {                                                            
    return methodCache;                                                                                                 
  }                                                                                                                     
                                                                                                                        
  @SuppressWarnings("unchecked")                                                                                        
  protected T newInstance(MapperProxy<T> mapperProxy) {                                                                 
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);  
  }                                                                                                                     
                                                                                                                        
  public T newInstance(SqlSession sqlSession) {                                                                         
    // Create a proxy class for xxxMapper.class.
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);                     
    returnnewInstance(mapperProxy); }}Copy the code

3. Execution process

List<User> userList = userMapper.selectUsersBySex("m");
Copy the code

(1) Access MapperProxy

// Enter the proxy class of xxxMapper. class and execute the "enhanced" method
@Override                                                                             
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                                           
  try {                                                                               
    if (Object.class.equals(method.getDeclaringClass())) {                            
      return method.invoke(this, args);                                               
    } else {                                                                          
      returncachedInvoker(method).invoke(proxy, method, args, sqlSession); }}catch (Throwable t) {                                                             
    throwExceptionUtil.unwrapThrowable(t); }}// The MapperMethodInvoker implementation class is called
// This class is a static inner class in MapperProxy
private static class PlainMethodInvoker implements MapperMethodInvoker {                                     
  private final MapperMethod mapperMethod;                                                                    
                                                                                                             
  public PlainMethodInvoker(MapperMethod mapperMethod) {                                                     
    super(a);this.mapperMethod = mapperMethod;                                                                        
  }                                                                                                          
                                                                                                             
  @Override                                                                                                  
  public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { 
    returnmapperMethod.execute(sqlSession, args); }}Copy the code

(2) Enter MapperMethod

// Use the command mode
// Select an execution method based on the type of the Sql command
public Object execute(SqlSession sqlSession, Object[] args) {                                                                                                               
  Object result;                                                                                                                                                          
  switch (command.getType()) {                                                                                                                                             
    case INSERT: {                                                                                                                                                        
      Object param = method.convertArgsToSqlCommandParam(args);                                                                                                           
      result = rowCountResult(sqlSession.insert(command.getName(), param));                                                                                               
      break;                                                                                                                                                              
    }                                                                                                                                                                     
    case UPDATE: {                                                                                                                                                        
      Object param = method.convertArgsToSqlCommandParam(args);                                                                                                           
      result = rowCountResult(sqlSession.update(command.getName(), param));                                                                                               
      break;                                                                                                                                                              
    }                                                                                                                                                                     
    case DELETE: {                                                                                                                                                        
      Object param = method.convertArgsToSqlCommandParam(args);                                                                                                           
      result = rowCountResult(sqlSession.delete(command.getName(), param));                                                                                               
      break;                                                                                                                                                              
    }                                                                                                                                                                     
    case SELECT:                                                                                                                                                          
      if (method.returnsVoid() && method.hasResultHandler()) {                                                                                                            
        executeWithResultHandler(sqlSession, args);                                                                                                                       
        result = null;                                                                                                                                                    
      } else if (method.returnsMany()) {   
        // This method is executed here
        result = executeForMany(sqlSession, args);                                                                                                                        
      } else if (method.returnsMap()) {                                                                                                                                   
        result = executeForMap(sqlSession, args);                                                                                                                         
      } else if (method.returnsCursor()) {                                                                                                                                
        result = executeForCursor(sqlSession, args);                                                                                                                      
      } else {                                                                                                                                                            
        Object param = method.convertArgsToSqlCommandParam(args);                                                                                                         
        result = sqlSession.selectOne(command.getName(), param);                                                                                                          
        if (method.returnsOptional()                                                                                                                                      
            && (result == null || !method.getReturnType().equals(result.getClass()))) {                                                                                   
          result = Optional.ofNullable(result);                                                                                                                           
        }                                                                                                                                                                 
      }                                                                                                                                                                   
      break;                                                                                                                                                              
    case FLUSH:                                                                                                                                                           
      result = sqlSession.flushStatements();                                                                                                                              
      break;                                                                                                                                                              
    default:                                                                                                                                                              
      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;                                                                                                                                                          
} 

// Look at the executeForMany method in the MapperMethod class
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {          
  List<E> result;                                                                
  / * * * the actual return here is a map, and should be a method of XXXMapper, into the parameter can be converted into a public key and value * like * List < User > selectUsersByDepartmentIdAndArea (@para(departmentId" Stri * can also be * public List<User> selectUsersBySex(String sex) */                                                                            
  Object param = method.convertArgsToSqlCommandParam(args);                        
  if (method.hasRowBounds()) {                                                   
    RowBounds rowBounds = method.extractRowBounds(args);                         
    result = sqlSession.selectList(command.getName(), param, rowBounds);         
  } else {                                               
    // Enter this method
    result = sqlSession.selectList(command.getName(), param);                    
  }                                                                                                                 
  if(! method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {                                      
      return convertToArray(result);                                             
    } else {                                                                     
      returnconvertToDeclaredCollection(sqlSession.getConfiguration(), result); }}return result;                                                                 
}   

Copy the code

(3) Enter the SqlSession implementation class DefaultSqlSession class

// Go to the selectList method in SqlSession
@Override                                                           
public <E> List<E> selectList(String statement, Object parameter) { 
  return this.selectList(statement, parameter, RowBounds.DEFAULT);    
}           

@Override                                                                                
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 
  return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);        
} 

// This is the method that is actually called
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { 
  try {                                                                                                          
    MappedStatement ms = configuration.getMappedStatement(statement);                                              
    Executor will be intercepted by plugins if an interceptor is defined to intercept Executor
    return executor.query(ms, wrapCollection(parameter), rowBounds, handler);                                    
  } catch (Exception e) {                                                                                        
    throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);                            
  } finally{ ErrorContext.instance().reset(); }}Copy the code

(4) Go to CacheExecutor

@Override                                                                                                                                     
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  
  / * * * getBoundSql (parameterObject), Select * from XXX where id =? Select * from XXX where id =? The * /                                                                                                                                         
  BoundSql boundSql = ms.getBoundSql(parameterObject);                                                                                        
                                                                                                                                              
  // Create a Key for the local cache
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);                                                                    
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);                                                                 
}  

// Execute query to enter the following method
@Override                                                                                                                                               
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 
    throws SQLException {                                                                                                                               
  Cache cache = ms.getCache();                                                                                                                           
  if(cache ! =null) {                                                                                                                                  
    flushCacheIfRequired(ms);                                                                                                                           
    if (ms.isUseCache() && resultHandler == null) {                                                                                                     
      ensureNoOutParams(ms, boundSql);                                                                                                                  
      @SuppressWarnings("unchecked")                                                                                                                    
      List<E> list = (List<E>) tcm.getObject(cache, key);                                                                                               
      if (list == null) {                                                                                                                               
        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);                                                            
        tcm.putObject(cache, key, list); // issue #578 and #116                                                                                         
      }                                                                                                                                                 
      returnlist; }}// Go this way
  // The executor is SimpleExecutor
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);                                                                  
}                


Copy the code

(5) Enter BaseExecutor

  • The delegate actually represents a SimpleExecutor
  • But the method called is in its parent BaseExecutor, so it enters BaseExecutor
@Override                                                                                                                                                               
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());                                                                  
  if (closed) {                                                                                                                                                          
    throw new ExecutorException("Executor was closed.");                                                                                                                
  }                                                                                                                                                                     
  if (queryStack == 0 && ms.isFlushCacheRequired()) {                                                                                                                   
    clearLocalCache();                                                                                                                                                  
  }                                                                                                                                                                     
  List<E> list;                                                                                                                                                         
  try {                                                                                                                                                                 
    queryStack++;                                                                                                                                                        
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;                                                                                           
    if(list ! =null) {                                                                                                                                                 
      // Output the parameter from the cache if it exists
      // I will not explain this until I write the cache module
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);                                                                                                
    } else {
      // Use this methodlist = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }}finally {                                                                                                                                                           
    queryStack--;                                                                                                                                                       
  }                                                                                                                                                                     
  if (queryStack == 0) {                                                                                                                                                
    for (DeferredLoad deferredLoad : deferredLoads) {                                                                                                                   
      deferredLoad.load();                                                                                                                                              
    }                                                                                                                                                                   
    // issue #601                                                                                                                                                       
    deferredLoads.clear();                                                                                                                                              
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {                                                                                              
      // issue #482                                                                                                                                                     clearLocalCache(); }}return list;                                                                                                                                                          
}                     

// Call the queryFromDatabase method of this class
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {         
  List<E> list;                                                                                                                                                                        
  // Store the Key of this query in the local cache
  localCache.putObject(key, EXECUTION_PLACEHOLDER);                                                                                                                                      
  try {                                                                                                                                                                                
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);                                                                                                                 
  } finally {                                                                                                                                                                          
    // Delete the Key, paving the way for putting the Key and list into the local cache later
    localCache.removeObject(key);                                                                                                                                                      
  }                                                                                                                                                                                    
  // Put the query results into the local cache, so that it can be called again
  localCache.putObject(key, list);                                                                                                                                                     
  if (ms.getStatementType() == StatementType.CALLABLE) {                                                                                                                               
    localOutputParameterCache.putObject(key, parameter);                                                                                                                               
  }                                                                                                                                                                                    
  return list;                                                                                                                                                                         
}                                                                                                                                                                                      
Copy the code

(6) Enter SimpleExecutor

@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();                                                                                                    
    //SQL statement processor
    // In this process, we create ParameterHandle and ResultSetHandler
    // If the plugin is defined
    // Plugin will also be added to intercept StatementHandler, ParameterHandler, and ResultSetHandler
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);                               
    // This side is ready to execute the STMT
    stmt = prepareStatement(handler, ms.getStatementLog());                                                                                                 
    return handler.query(stmt, resultHandler);                                                                                                              
  } finally{ closeStatement(stmt); }}PrepareStatementHandler (Handler, ms.getStatementLog())


private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { 
  Statement stmt;                                                                                     
  If the data source is set to pooled, if the connection can be obtained from the database connection pool, then create a connection and place it in the connection pool */                                                                                                
  Connection connection = getConnection(statementLog);                                                 
  // The statement is processed and retrieved based on the connection
  // This STMT is actually a PrepareStatement
  stmt = handler.prepare(connection, transaction.getTimeout());                                         
  // Handle the parameters and put them in the PrepareStatement
  handler.parameterize(stmt);                                                                        
  return stmt;                                                                                       
} 
// Loader.parameterize (STMS)
Copy the code

(7) PreparedStatementHandler

PrepareStatementHandler route to PrepareStatementHandler via RoutingStatementHandler
@Override                                                            
public void parameterize(Statement statement) throws SQLException {  
  parameterHandler.setParameters((PreparedStatement) statement);     
}         

// Call the DefaultStatementHandler set earlier to process the parameter
public void setParameters(PreparedStatement ps) {                                                                               
  PrepareHandler () PrepareHandler () PrepareHandler () PrepareHandler
  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++) {                                                                        
      // Get the parameters passed in by the mapper file, in the order they were passed
      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 {                                                                                                                   
          // Set the parameters in the PrepareStatement
          typeHandler.setParameter(ps, i + 1, value, jdbcType);                                                                 
        } catch (TypeException | SQLException e) {                                                                              
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);            
        }                                                                                                                       
      }                                                                                                                         
    }                                                                                                                           
  }                                                                                                                             
}                                                                                                                               
Copy the code

(8) Go back to SimpleExecutor

  • Now you can see that BoundSql was

  • After executing handler.parameterize(STMS)

com.mysql.cj.jdbc.ClientPreparedStatement: select * from t_user where sex = 'm'
Copy the code

(9) PreparedStatementHandler

  • Call RoutingStatementHandler from SimpleExecutor to route to PrepareStatementHandler
@Override                                                                                         
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {  
  PreparedStatement ps = (PreparedStatement) statement;                                           
  ps.execute();                                                                                   
  // Process the returned result
  return resultSetHandler.handleResultSets(ps);                                                   
}   
// Let's see what happens to the result
Copy the code

(10) Enter DefaultResultSetHandler

@Override                                                                                                      
public List<Object> handleResultSets(Statement stmt) throws SQLException {                                      
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());                        
                                                                                                               
  final List<Object> multipleResults = new ArrayList<>();                                                       
                                                                                                               
  int resultSetCount = 0;                                                                                       
  ResultSetWrapper rsw = getFirstResultSet(stmt);                                                               
                                                                                                               
  // Return the previously defined resultType in the xxxMapper. XML file
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();                                                 
  int resultMapCount = resultMaps.size();                                                                       
  validateResultMapsCount(rsw, resultMapCount);                                                                
  // Process the first result set
  while(rsw ! =null && resultMapCount > resultSetCount) {                                                      
    ResultMap resultMap = resultMaps.get(resultSetCount);                                                       
    // Process a single result set
    handleResultSet(rsw, resultMap, multipleResults, null);                                                    
    rsw = getNextResultSet(stmt);                                                                              
    cleanUpAfterHandlingResultSet();                                                                           
    resultSetCount++;                                                                                          
  }                                                                                                            
                                                                                                               
  String[] resultSets = mappedStatement.getResultSets();                                                         
  // More than one result set
  if(resultSets ! =null) {                                                                                    
    // Process the remaining result sets
    while(rsw ! =null && resultSetCount < resultSets.length) {                                                
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);                              
      if(parentMapping ! =null) {                                                                             
        String nestedResultMapId = parentMapping.getNestedResultMapId();                                       
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);                                    
        // Process a single result set
        handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; }}// If there is a single result set, return the result List of the corresponding query rows in the result set
  return collapseSingleResultList(multipleResults);                                                             
}                                                                                                              
Copy the code

3. To summarize

Summary figure

  • If the picture does not display properly, enter the following connection to view

Mybatis summary figure