MapperMethod中內部靜態類SqlCommand的作用
在MapperMethod初始化中,會首先初始化兩個內部靜態類,SqlCommand就是其中之一,SqlCommand的作用主要體現在MapperMethod類的execute()方法里,SqlCommand為其提供了查詢類型和方法id兩個信息,從而使用Sqlseesion執行不同的方法,那么SqlCommand是如何獲取到查詢類型和方法id的呢?其中又做了哪些操作呢?
首先看看構造器中的代碼執行順序。構造器需要傳入配置類,Mapper接口和Method類.
- 從Method類中獲取方法名和該方法所在的類路徑
- 根據mapper接口信息和配置類獲取到XML對應的MapperStatement對象
- 判斷映射語句對象是否為NULL,如果不為NULL,則從中獲取到語句的id和待執行的Sql類型,如果為NULL,再次判斷待執行方法上是否標注有Flush的注解,如果不滿足條件,則會拋出BindingException異常。
流程圖如下所示:
值得注意的是,在獲取映射語句對象是通過調用resolveMappedStatement()方法實現的。見如下的講述。
1 public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { 2 //獲取方法名 3 final String methodName = method.getName(); 4 //獲取方法所在的類 5 final Class<?> declaringClass = method.getDeclaringClass(); 6 //獲取映射語句信息 7 MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, 8 configuration); 9 //如果映射語句對象是NULL,那么查看該方法上是否標注了FLUSH標簽,如果存在,則設置查詢類型為FLUSH,否則拋出BindingException異常表示接口找不到定義的方法。 10 if (ms == null) { 11 if (method.getAnnotation(Flush.class) != null) { 12 name = null; 13 type = SqlCommandType.FLUSH; 14 } else { 15 throw new BindingException("Invalid bound statement (not found): " 16 + mapperInterface.getName() + "." + methodName); 17 } 18 } 19 //如果映射語句對象不為空,則設置指定的查詢類型,如果為UNKNOWN 類型,則直接拋出BindingException異常 20 else { 21 name = ms.getId(); 22 type = ms.getSqlCommandType(); 23 if (type == SqlCommandType.UNKNOWN) { 24 throw new BindingException("Unknown execution method for: " + name); 25 } 26 } 27 }
resolveMappedStatement()方法
該方法主要是獲取映射語句對象,在xml配置中,像select,update,delete,insert 都需要提供id號和sql語句以及入參和出參信息,而MappedStatement就是xml到java對象的映射,一個<select//>標簽就會對應一個MappedStatement對象,這個目前了解即可,在后續會專門進行介紹。現簡要說明下代碼的執行流程。
1. 獲取待執行語句的id號,id號形式為類路徑.方法名
2. 在配置類中是否能找到該語句的id號,如果能找到,則直接從配置類中返回MappedStatement實例,否則從父類接口中繼續查找,如果找不到就返回NULL
3. 如果入參接口路徑就是方法所在的類路徑,那么直接返回NULL
1 private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, 2 Class<?> declaringClass, Configuration configuration) { 3 //獲取語句id 形式為接口名.方法名 4 String statementId = mapperInterface.getName() + "." + methodName; 5 //判斷配置中是否存在該方法id 6 if (configuration.hasStatement(statementId)) { 7 //返回映射語句 8 return configuration.getMappedStatement(statementId); 9 } 10 //如果接口信息就是所在類的話,直接返回NULL 11 else if (mapperInterface.equals(declaringClass)) { 12 return null; 13 } 14 //獲取該類下的所有接口信息 15 for (Class<?> superInterface : mapperInterface.getInterfaces()) { 16 //從父接口中查找對應的方法ID,下列語句使用遞歸的方式進行查找 17 if (declaringClass.isAssignableFrom(superInterface)) { 18 MappedStatement ms = resolveMappedStatement(superInterface, methodName, 19 declaringClass, configuration); 20 if (ms != null) { 21 return ms; 22 } 23 } 24 } 25 return null; 26 } 27 }