MyBatis源碼解讀(4)——SqlSession(上)


  在上一篇博客中提到MyBatis是如何實現代理類MapperProxy,並拋出了一個問題——是怎么執行一個具體的sql語句的,在文末中提到了MapperMethod的execute采用命令模式來判斷是何種sql語句,並將具體語句的執行交由SqlSession處理。所以此篇博客正是要講到SqlSession。

  在SqlSession接口中包含了所有可能執行的sql語句在這里不一一列舉,請參考org.apache.ibatis.session.SqlSession源碼。DefaultSqlSession是SqlSession的實現類,所以我們重點關注DefaultSqlSession類。在上一篇博客中提到了我們將選擇MapperProxy的executeForMany方法。在此方法中調用了SqlSession.<E>selectList方法。所以我們來看看org.apache.ibatis.session.default.DefaultSqlSession類的selectList方法。

 1 //org.apache.ibatis.session.defaults.DefaultSqlSession
 2 ……
 3   @Override
 4   public <E> List<E> selectList(String statement) {
 5     return this.selectList(statement, null);
 6   }
 7 
 8   @Override
 9   public <E> List<E> selectList(String statement, Object parameter) {
10     return this.selectList(statement, parameter, RowBounds.DEFAULT);
11   }
12 
13   @Override
14   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
15     try {
16       MappedStatement ms = configuration.getMappedStatement(statement);
17       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
18     } catch (Exception e) {
19       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
20     } finally {
21       ErrorContext.instance().reset();
22     }
23   }
24 ……

我們看到關於selectList有三個重載方法,最后調用的都是public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)。在此方法中第一個參數為String類型且命名為statement,第二個參數為Object命名為parameter,回到MapperMethod。

 1 //org.apache.ibatis.binding.MapperMethod
 2  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 3     List<E> result;
 4     Object param = method.convertArgsToSqlCommandParam(args);
 5     if (method.hasRowBounds()) {
 6       RowBounds rowBounds = method.extractRowBounds(args);
 7       result = sqlSession.<E>selectList(command.getName(), param, rowBounds);  //注意傳遞了什么參數進來
 8     } else {
 9       result = sqlSession.<E>selectList(command.getName(), param);
10     }
11     // issue #510 Collections & arrays support
12     if (!method.getReturnType().isAssignableFrom(result.getClass())) {
13       if (method.getReturnType().isArray()) {
14         return convertToArray(result);
15       } else {
16         return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
17       }
18     }
19     return result;
20   }

  第7行代碼可以看到傳遞了什么參數進來,跟蹤command.getName()是怎么來的。

private final SqlCommand command;

  看來是一個叫做SqlCommand的變量,進而我們可以發現這個SqlCommand是MapperMethod的靜態內部類。

 1 //org.apache.ibatis.binding.MapperMethod
 2   public static class SqlCommand {
 3 
 4     private final String name;
 5     private final SqlCommandType type;
 6 
 7     public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
 8       String statementName = mapperInterface.getName() + "." + method.getName();  //獲取接口+方法名
 9       MappedStatement ms = null;  //定義一個MappedStatement,這個MapperedStatement稍后介紹
10       if (configuration.hasStatement(statementName)) {  //從Configuration對象查找是否有這個方法的全限定名稱
11         ms = configuration.getMappedStatement(statementName);  //有,則根據方法的全限定名稱獲取MappedStatement實例
12       } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35//如果沒有在Configuration對象中找到這個方法,則向上父類中獲取全限定方法名
13         String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
14         if (configuration.hasStatement(parentStatementName)) {  //在向上的父類查找是否有這個全限定方法名
15           ms = configuration.getMappedStatement(parentStatementName);   //有,則根據方法的全限定名稱獲取MappedStatement實例
16         }
17       }
18       if (ms == null) {
19         if(method.getAnnotation(Flush.class) != null){
20           name = null;
21           type = SqlCommandType.FLUSH;
22         } else {
23           throw new BindingException("Invalid bound statement (not found): " + statementName);
24         }
25       } else {
26         name = ms.getId();  //這個ms.getId,其實就是我們在mapper.xml配置文件中配置一條sql語句時開頭的<select id="……"……,即是接口的該方法名全限定名稱
27         type = ms.getSqlCommandType();  //顯然這是將sql是何種類型(insert、update、delete、select)賦給type
28         if (type == SqlCommandType.UNKNOWN) {
29           throw new BindingException("Unknown execution method for: " + name);
30         }
31       }
32     }
33 
34     public String getName() {
35       return name;
36     }
37 
38     public SqlCommandType getType() {
39       return type;
40     }
41   }

  大致對MapperMethod的解讀到此,再次回到DefaultSqlSession中,到這里可能稍微有點混亂。畫個圖來理解這一段,希望能幫助理解。

  這次又一次回到了DefaultSqlSession.selectList。此處不再貼出selectList所有方法,而是只貼出一句,根據接口方法的全限定名稱獲取MappedStatement。

MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

  在獲取到MappedStatement后,碰到了第一個SqlSession下的四大對象之一:Executor執行器。看來還是沒有講到SqlSession,再放到下一節吧。可以思考下這個MappedStatement是什么意思,望文生義就能猜出個大概。

 

這是一個能給程序員加buff的公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM