Maison Java javaDidacticiel 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)

深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)

Mar 02, 2017 am 11:10 AM

最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅。在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程, 好啦,鄙人不喜欢口水话,还是直接上干活吧:

1. SqlSessionFactory 与 SqlSession.

  通过前面的章节对于mybatis 的介绍及使用,大家都能体会到SqlSession的重要性了吧, 没错,从表面上来看,咱们都是通过SqlSession去执行sql语句(注意:是从表面看,实际的待会儿就会讲)。那么咱们就先看看是怎么获取SqlSession的吧:

(1)首先,SqlSessionFactoryBuilder去读取mybatis的配置文件,然后build一个DefaultSqlSessionFactory。源码如下:

/**
   * 一系列的构造方法最终都会调用本方法(配置文件为Reader时会调用本方法,还有一个InputStream方法与此对应)
   * @param reader
   * @param environment
   * @param properties
   * @return   */
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {    
  try {      //通过XMLConfigBuilder解析配置文件,解析的配置相关信息都会封装为一个Configuration对象
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);      
      //这儿创建DefaultSessionFactory对象
      return build(parser.parse());
    } catch (Exception e) {      
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();      
      try {
        reader.close();
      } catch (IOException e) {        
      // Intentionally ignore. Prefer previous error.      }
    }
  }  public SqlSessionFactory build(Configuration config) {    
  return new DefaultSqlSessionFactory(config);
  }
Copier après la connexion


(2)当我们获取到SqlSessionFactory之后,就可以通过SqlSessionFactory去获取SqlSession对象。源码如下:

/**
   * 通常一系列openSession方法最终都会调用本方法
   * @param execType 
   * @param level
   * @param autoCommit
   * @return
   */
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;    try {      
    //通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置
      final Environment environment = configuration.getEnvironment();      
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);      
      //之前说了,从表面上来看,咱们是用sqlSession在执行sql语句, 实际呢,其实是通过excutor执行, excutor是对于Statement的封装
      final Executor executor = configuration.newExecutor(tx, execType);      
      //关键看这儿,创建了一个DefaultSqlSession对象
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
Copier après la connexion


通过以上步骤,咱们已经得到SqlSession对象了。接下来就是该干嘛干嘛去了(话说还能干嘛,当然是执行sql语句咯)。看了上面,咱们也回想一下之前写的Demo, 

SqlSessionFactory sessionFactory = null;  
String resource = "mybatis-conf.xml";  
try {
     //SqlSessionFactoryBuilder读取配置文件
    sessionFactory = new SqlSessionFactoryBuilder().build(Resources  
              .getResourceAsReader(resource));
} catch (IOException e) {  
    e.printStackTrace();  
}    //通过SqlSessionFactory获取SqlSessionSqlSession sqlSession = sessionFactory.openSession();
Copier après la connexion

还真这么一回事儿,对吧! 

SqlSession咱们也拿到了,咱们可以调用SqlSession中一系列的select...,  insert..., update..., delete...方法轻松自如的进行CRUD操作了。 就这样? 那咱配置的映射文件去哪儿了?  别急, 咱们接着往下看:

 

2. 利器之MapperProxy:

 

在mybatis中,通过MapperProxy动态代理咱们的dao, 也就是说, 当咱们执行自己写的dao里面的方法的时候,其实是对应的mapperProxy在代理。那么,咱们就看看怎么获取MapperProxy对象吧:

(1)通过SqlSession从Configuration中获取。源码如下:

/**
   * 什么都不做,直接去configuration中找, 哥就是这么任性   */
  @Override  public <T> T getMapper(Class<T> type) {    
  return configuration.<T>getMapper(type, this);
  }
Copier après la connexion


(2)SqlSession把包袱甩给了Configuration, 接下来就看看Configuration。源码如下:

/**
   * 烫手的山芋,俺不要,你找mapperRegistry去要
   * @param type
   * @param sqlSession
   * @return
   */
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    
  return mapperRegistry.getMapper(type, sqlSession);
  }
Copier après la connexion


(3)Configuration不要这烫手的山芋,接着甩给了MapperRegistry, 那咱看看MapperRegistry。 源码如下:

/**
   * 烂活净让我来做了,没法了,下面没人了,我不做谁来做
   * @param type
   * @param sqlSession
   * @return
   */
  @SuppressWarnings("unchecked")  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    //能偷懒的就偷懒,俺把粗活交给MapperProxyFactory去做
    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);
    }
  }
Copier après la connexion


(4)MapperProxyFactory是个苦B的人,粗活最终交给它去做了。咱们看看源码:


/**
   * 别人虐我千百遍,我待别人如初恋
   * @param mapperProxy
   * @return
   */
  @SuppressWarnings("unchecked")  protected T newInstance(MapperProxy<T> mapperProxy) {    
  //动态代理我们写的dao接口
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }  
  public T newInstance(SqlSession sqlSession) {    
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);    
  return newInstance(mapperProxy);
  }
Copier après la connexion


通过以上的动态代理,咱们就可以方便地使用dao接口啦, 就像之前咱们写的demo那样:

 UserDao userMapper = sqlSession.getMapper(UserDao.class);  
 User insertUser = new User();
Copier après la connexion

这下方便多了吧, 呵呵, 貌似mybatis的源码就这么一回事儿啊。

别急,还没完, 咱们还没看具体是怎么执行sql语句的呢。

 

3. Excutor:

接下来,咱们才要真正去看sql的执行过程了。

上面,咱们拿到了MapperProxy, 每个MapperProxy对应一个dao接口, 那么咱们在使用的时候,MapperProxy是怎么做的呢? 源码奉上:

MapperProxy:

/**
   * MapperProxy在执行时会触发此方法   */
  @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);    
    //二话不说,主要交给MapperMethod自己去管
    return mapperMethod.execute(sqlSession, args);
  }
Copier après la connexion


MapperMethod:

 /**
   * 看着代码不少,不过其实就是先判断CRUD类型,然后根据类型去选择到底执行sqlSession中的哪个方法,绕了一圈,又转回sqlSession了
   * @param sqlSession
   * @param args
   * @return
   */
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {      
    if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {      throw new BindingException("Unknown execution method for: " + command.getName());
    }    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {      
    throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }    return result;
  }
Copier après la connexion


既然又回到SqlSession了, 那么咱们就看看SqlSession的CRUD方法了,为了省事,还是就选择其中的一个方法来做分析吧。这儿,咱们选择了selectList方法:


public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    
try {
      MappedStatement ms = configuration.getMappedStatement(statement);      
      //CRUD实际上是交给Excetor去处理, excutor其实也只是穿了个马甲而已,小样,别以为穿个马甲我就不认识你嘞!
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {      
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
Copier après la connexion


然后,通过一层一层的调用,最终会来到doQuery方法, 这儿咱们就随便找个Excutor看看doQuery方法的实现吧,我这儿选择了SimpleExecutor:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;   
     try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());      
      //StatementHandler封装了Statement, 让 StatementHandler 去处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
Copier après la connexion


接下来,咱们看看StatementHandler 的一个实现类 PreparedStatementHandler(这也是我们最常用的,封装的是PreparedStatement), 看看它使怎么去处理的:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {     
//到此,原形毕露, PreparedStatement, 这个大家都已经滚瓜烂熟了吧
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();    
    //结果交给了ResultSetHandler 去处理
    return resultSetHandler.<E> handleResultSets(ps);
  }
Copier après la connexion


到此, 一次sql的执行流程就完了。 我这儿仅抛砖引玉,建议有兴趣的去看看Mybatis3的源码。

好啦,本次就到此结束啦,最近太忙了, 又该忙去啦。

 以上就是深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. Vous avez un jeu croisé?
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Quelle est la différence entre HQL et SQL dans le framework Hibernate ? Quelle est la différence entre HQL et SQL dans le framework Hibernate ? Apr 17, 2024 pm 02:57 PM

HQL et SQL sont comparés dans le framework Hibernate : HQL (1. Syntaxe orientée objet, 2. Requêtes indépendantes de la base de données, 3. Sécurité des types), tandis que SQL exploite directement la base de données (1. Normes indépendantes de la base de données, 2. Exécutable complexe requêtes et manipulation de données).

Utilisation de l'opération de division dans Oracle SQL Utilisation de l'opération de division dans Oracle SQL Mar 10, 2024 pm 03:06 PM

"Utilisation de l'opération de division dans OracleSQL" Dans OracleSQL, l'opération de division est l'une des opérations mathématiques courantes. Lors de l'interrogation et du traitement des données, les opérations de division peuvent nous aider à calculer le rapport entre les champs ou à dériver la relation logique entre des valeurs spécifiques. Cet article présentera l'utilisation de l'opération de division dans OracleSQL et fournira des exemples de code spécifiques. 1. Deux méthodes d'opérations de division dans OracleSQL Dans OracleSQL, les opérations de division peuvent être effectuées de deux manières différentes.

Comparaison et différences de syntaxe SQL entre Oracle et DB2 Comparaison et différences de syntaxe SQL entre Oracle et DB2 Mar 11, 2024 pm 12:09 PM

Oracle et DB2 sont deux systèmes de gestion de bases de données relationnelles couramment utilisés, chacun possédant sa propre syntaxe et ses propres caractéristiques SQL. Cet article comparera et différera la syntaxe SQL d'Oracle et de DB2, et fournira des exemples de code spécifiques. Connexion à la base de données Dans Oracle, utilisez l'instruction suivante pour vous connecter à la base de données : CONNECTusername/password@database Dans DB2, l'instruction pour vous connecter à la base de données est la suivante : CONNECTTOdataba.

Explication détaillée de la fonction Définir la balise dans les balises SQL dynamiques MyBatis Explication détaillée de la fonction Définir la balise dans les balises SQL dynamiques MyBatis Feb 26, 2024 pm 07:48 PM

Interprétation des balises SQL dynamiques MyBatis : explication détaillée de l'utilisation des balises Set MyBatis est un excellent cadre de couche de persistance. Il fournit une multitude de balises SQL dynamiques et peut construire de manière flexible des instructions d'opération de base de données. Parmi elles, la balise Set est utilisée pour générer la clause SET dans l'instruction UPDATE, qui est très couramment utilisée dans les opérations de mise à jour. Cet article expliquera en détail l'utilisation de la balise Set dans MyBatis et démontrera ses fonctionnalités à travers des exemples de code spécifiques. Qu'est-ce que Set tag Set tag est utilisé dans MyBati

Comment résoudre l'erreur 5120 dans SQL Comment résoudre l'erreur 5120 dans SQL Mar 06, 2024 pm 04:33 PM

Solution : 1. Vérifiez si l'utilisateur connecté dispose des autorisations suffisantes pour accéder ou utiliser la base de données, et assurez-vous que l'utilisateur dispose des autorisations appropriées ; 2. Vérifiez si le compte du service SQL Server est autorisé à accéder au fichier spécifié ou ; dossier et assurez-vous que le compte dispose des autorisations suffisantes pour lire et écrire le fichier ou le dossier ; 3. Vérifiez si le fichier de base de données spécifié a été ouvert ou verrouillé par d'autres processus, essayez de fermer ou de libérer le fichier et réexécutez la requête ; . Essayez en tant qu'administrateur, exécutez Management Studio en tant que etc.

Concurrence en technologie de base de données : quelles sont les différences entre Oracle et SQL ? Concurrence en technologie de base de données : quelles sont les différences entre Oracle et SQL ? Mar 09, 2024 am 08:30 AM

Concurrence en technologie de base de données : quelles sont les différences entre Oracle et SQL ? Dans le domaine des bases de données, Oracle et SQL Server sont deux systèmes de gestion de bases de données relationnelles très respectés. Bien qu’elles appartiennent toutes deux à la catégorie des bases de données relationnelles, il existe de nombreuses différences entre elles. Dans cet article, nous examinerons les différences entre Oracle et SQL Server, ainsi que leurs fonctionnalités et avantages dans les applications pratiques. Tout d'abord, il existe des différences de syntaxe entre Oracle et SQL Server.

Explication détaillée du cache de premier niveau MyBatis : Comment améliorer l'efficacité de l'accès aux données ? Explication détaillée du cache de premier niveau MyBatis : Comment améliorer l'efficacité de l'accès aux données ? Feb 23, 2024 pm 08:13 PM

Explication détaillée du cache de premier niveau MyBatis : Comment améliorer l'efficacité de l'accès aux données ? Au cours du processus de développement, un accès efficace aux données a toujours été l’une des priorités des programmeurs. Pour les frameworks de couche de persistance comme MyBatis, la mise en cache est l'une des méthodes clés pour améliorer l'efficacité de l'accès aux données. MyBatis fournit deux mécanismes de mise en cache : le cache de premier niveau et le cache de deuxième niveau. Le cache de premier niveau est activé par défaut. Cet article présentera en détail le mécanisme du cache de premier niveau MyBatis et fournira des exemples de code spécifiques pour aider les lecteurs à mieux comprendre

Analyser le mécanisme de mise en cache de MyBatis : comparer les caractéristiques et l'utilisation du cache de premier niveau et du cache de deuxième niveau Analyser le mécanisme de mise en cache de MyBatis : comparer les caractéristiques et l'utilisation du cache de premier niveau et du cache de deuxième niveau Feb 25, 2024 pm 12:30 PM

Analyse du mécanisme de mise en cache MyBatis : la différence et l'application du cache de premier niveau et du cache de deuxième niveau Dans le framework MyBatis, la mise en cache est une fonctionnalité très importante qui peut améliorer efficacement les performances des opérations de base de données. Parmi eux, le cache de premier niveau et le cache de deuxième niveau sont deux mécanismes de mise en cache couramment utilisés dans MyBatis. Cet article analysera en détail les différences et les applications du cache de premier niveau et du cache de deuxième niveau, et fournira des exemples de code spécifiques pour illustrer. 1. Cache de niveau 1 Le cache de niveau 1 est également appelé cache local. Il est activé par défaut et ne peut pas être désactivé. Le cache de premier niveau est SqlSes

See all articles