Rumah Java javaTutorial 深入浅出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);
  }
Salin selepas log masuk


(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();
    }
  }
Salin selepas log masuk


通过以上步骤,咱们已经得到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();
Salin selepas log masuk

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

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);
  }
Salin selepas log masuk


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

/**
   * 烫手的山芋,俺不要,你找mapperRegistry去要
   * @param type
   * @param sqlSession
   * @return
   */
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    
  return mapperRegistry.getMapper(type, sqlSession);
  }
Salin selepas log masuk


(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);
    }
  }
Salin selepas log masuk


(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);
  }
Salin selepas log masuk


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

 UserDao userMapper = sqlSession.getMapper(UserDao.class);  
 User insertUser = new User();
Salin selepas log masuk

这下方便多了吧, 呵呵, 貌似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);
  }
Salin selepas log masuk


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;
  }
Salin selepas log masuk


既然又回到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();
    }
  }
Salin selepas log masuk


然后,通过一层一层的调用,最终会来到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);
    }
  }
Salin selepas log masuk


接下来,咱们看看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);
  }
Salin selepas log masuk


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

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

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


Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Akan R.E.P.O. Ada Crossplay?
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Apakah perbezaan antara HQL dan SQL dalam rangka kerja Hibernate? Apakah perbezaan antara HQL dan SQL dalam rangka kerja Hibernate? Apr 17, 2024 pm 02:57 PM

HQL dan SQL dibandingkan dalam rangka kerja Hibernate: HQL (1. Sintaks berorientasikan objek, 2. Pertanyaan bebas pangkalan data, 3. Keselamatan jenis), manakala SQL mengendalikan pangkalan data secara langsung (1. Piawaian bebas pangkalan data, 2. Boleh laku kompleks pertanyaan dan manipulasi data).

Penggunaan operasi bahagian dalam Oracle SQL Penggunaan operasi bahagian dalam Oracle SQL Mar 10, 2024 pm 03:06 PM

"Penggunaan Operasi Bahagian dalam OracleSQL" Dalam OracleSQL, operasi bahagi ialah salah satu operasi matematik yang biasa. Semasa pertanyaan dan pemprosesan data, operasi pembahagian boleh membantu kami mengira nisbah antara medan atau memperoleh hubungan logik antara nilai tertentu. Artikel ini akan memperkenalkan penggunaan operasi pembahagian dalam OracleSQL dan memberikan contoh kod khusus. 1. Dua cara operasi bahagi dalam OracleSQL Dalam OracleSQL, operasi bahagi boleh dilakukan dalam dua cara berbeza.

Perbandingan dan perbezaan sintaks SQL antara Oracle dan DB2 Perbandingan dan perbezaan sintaks SQL antara Oracle dan DB2 Mar 11, 2024 pm 12:09 PM

Oracle dan DB2 ialah dua sistem pengurusan pangkalan data hubungan yang biasa digunakan, setiap satunya mempunyai sintaks dan ciri SQL tersendiri. Artikel ini akan membandingkan dan membezakan antara sintaks SQL Oracle dan DB2, dan memberikan contoh kod khusus. Sambungan pangkalan data Dalam Oracle, gunakan pernyataan berikut untuk menyambung ke pangkalan data: CONNECTusername/password@database Dalam DB2, pernyataan untuk menyambung ke pangkalan data adalah seperti berikut: CONNECTTOdataba

Penjelasan terperinci tentang fungsi Set tag dalam teg SQL dinamik MyBatis Penjelasan terperinci tentang fungsi Set tag dalam teg SQL dinamik MyBatis Feb 26, 2024 pm 07:48 PM

Tafsiran teg SQL dinamik MyBatis: Penjelasan terperinci tentang penggunaan teg Set MyBatis ialah rangka kerja lapisan kegigihan yang sangat baik Ia menyediakan banyak teg SQL dinamik dan boleh membina pernyataan operasi pangkalan data secara fleksibel. Antaranya, tag Set ialah tag yang digunakan untuk menjana klausa SET dalam kenyataan UPDATE, yang sangat biasa digunakan dalam operasi kemas kini. Artikel ini akan menerangkan secara terperinci penggunaan teg Set dalam MyBatis dan menunjukkan kefungsiannya melalui contoh kod tertentu. Apakah itu Set tag Set tag digunakan dalam MyBati

Bagaimana untuk menyelesaikan ralat 5120 dalam SQL Bagaimana untuk menyelesaikan ralat 5120 dalam SQL Mar 06, 2024 pm 04:33 PM

Penyelesaian: 1. Semak sama ada pengguna log masuk mempunyai kebenaran yang mencukupi untuk mengakses atau mengendalikan pangkalan data, dan pastikan pengguna mempunyai kebenaran yang betul 2. Semak sama ada akaun perkhidmatan SQL Server mempunyai kebenaran untuk mengakses fail yang ditentukan atau folder, dan pastikan akaun Mempunyai kebenaran yang mencukupi untuk membaca dan menulis fail atau folder 3. Semak sama ada fail pangkalan data yang ditentukan telah dibuka atau dikunci oleh proses lain, cuba tutup atau lepaskan fail, dan jalankan semula pertanyaan 4 . Cuba sebagai pentadbir Jalankan Studio Pengurusan seperti dsb.

Persaingan teknologi pangkalan data: Apakah perbezaan antara Oracle dan SQL? Persaingan teknologi pangkalan data: Apakah perbezaan antara Oracle dan SQL? Mar 09, 2024 am 08:30 AM

Persaingan teknologi pangkalan data: Apakah perbezaan antara Oracle dan SQL? Dalam bidang pangkalan data, Oracle dan SQL Server adalah dua sistem pengurusan pangkalan data hubungan yang sangat dihormati. Walaupun kedua-duanya tergolong dalam kategori pangkalan data hubungan, terdapat banyak perbezaan antara mereka. Dalam artikel ini, kami akan menyelidiki perbezaan antara Oracle dan SQL Server, serta ciri dan kelebihannya dalam aplikasi praktikal. Pertama sekali, terdapat perbezaan dalam sintaks antara Oracle dan SQL Server.

Penjelasan terperinci tentang cache peringkat pertama MyBatis: Bagaimana untuk meningkatkan kecekapan capaian data? Penjelasan terperinci tentang cache peringkat pertama MyBatis: Bagaimana untuk meningkatkan kecekapan capaian data? Feb 23, 2024 pm 08:13 PM

Penjelasan terperinci tentang cache peringkat pertama MyBatis: Bagaimana untuk meningkatkan kecekapan capaian data? Semasa proses pembangunan, capaian data yang cekap sentiasa menjadi salah satu fokus pengaturcara. Untuk rangka kerja lapisan kegigihan seperti MyBatis, caching ialah salah satu kaedah utama untuk meningkatkan kecekapan capaian data. MyBatis menyediakan dua mekanisme caching: cache peringkat pertama dan cache peringkat kedua. Cache peringkat pertama didayakan secara lalai. Artikel ini akan memperkenalkan mekanisme cache peringkat pertama MyBatis secara terperinci dan menyediakan contoh kod khusus untuk membantu pembaca memahami dengan lebih baik.

Menganalisis mekanisme caching MyBatis: bandingkan ciri dan penggunaan cache peringkat pertama dan cache peringkat kedua Menganalisis mekanisme caching MyBatis: bandingkan ciri dan penggunaan cache peringkat pertama dan cache peringkat kedua Feb 25, 2024 pm 12:30 PM

Analisis mekanisme caching MyBatis: Perbezaan dan aplikasi cache peringkat pertama dan cache peringkat kedua Dalam rangka kerja MyBatis, caching ialah ciri yang sangat penting yang boleh meningkatkan prestasi operasi pangkalan data dengan berkesan. Antaranya, cache tahap pertama dan cache tahap kedua adalah dua mekanisme caching yang biasa digunakan dalam MyBatis. Artikel ini akan menganalisis perbezaan dan aplikasi cache peringkat pertama dan cache peringkat kedua secara terperinci, dan memberikan contoh kod khusus untuk digambarkan. 1. Cache Tahap 1 Cache tahap 1 juga dipanggil cache setempat Ia didayakan secara lalai dan tidak boleh dimatikan. Cache tahap pertama ialah SqlSes

See all articles