[java學習筆記]MyBatis源碼學習筆記(四)

接下來看看MappedStatement這個對象是怎麼來的,一個mappedStatement對象代表的就是mapperxml內的一個select/insert/update/delete標籤,也就是一條sql語句

 1   private String resource;//mapper配置文件名,如:UserMapper.xml
 2   private Configuration configuration;//全局配置
 3   private String id;//節點的id屬性加命名空間,如:com.lucky.mybatis.dao.UserMapper.selectByExample
 4   private Integer fetchSize;
 5   private Integer timeout;//超時時間
 6   private StatementType statementType;//操作SQL的對象的類型
 7   private ResultSetType resultSetType;//結果類型
 8   private SqlSource sqlSource;//sql語句
 9   private Cache cache;//緩存
10   private ParameterMap parameterMap;
11   private List<ResultMap> resultMaps;
12   private boolean flushCacheRequired;
13   private boolean useCache;//是否使用緩存,默認爲true
14   private boolean resultOrdered;//結果是否排序
15   private SqlCommandType sqlCommandType;//sql語句的類型,如select、update、delete、insert
16   private KeyGenerator keyGenerator;
17   private String[] keyProperties;
18   private String[] keyColumns;
19   private boolean hasNestedResultMaps;
20   private String databaseId;//數據庫ID
21   private Log statementLog;
22   private LanguageDriver lang;
23   private String[] resultSets;

這是這個類內的成員,其中

  1. StatementType指操作SQL對象的類型,是個枚舉類型,值分別爲:
    STATEMENT(直接操作SQL,不進行預編譯),
    PREPARED(預處理參數,進行預編譯,獲取數據),
    CALLABLE(執行存儲過程)

  2. ResultSetType指返回結果集的類型,也是個枚舉類型,值分別爲:
    FORWARD_ONLY:結果集的遊標只能向下滾動
    SCROLL_INSENSITIVE:結果集的遊標可以上下移動,當數據庫變化時當前結果集不變
    SCROLL_SENSITIVE:結果集客自由滾動,數據庫變化時當前結果集同步改變

10   private void parseConfiguration(XNode root) {
11     try {
12       Properties settings = settingsAsPropertiess(root.evalNode("settings"));
13       //issue #117 read properties first
14       propertiesElement(root.evalNode("properties"));
15       loadCustomVfs(settings);
16       typeAliasesElement(root.evalNode("typeAliases"));
17       pluginElement(root.evalNode("plugins"));
18       objectFactoryElement(root.evalNode("objectFactory"));
19       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
20       reflectorFactoryElement(root.evalNode("reflectorFactory"));
21       settingsElement(settings);
22       // read it after objectFactory and objectWrapperFactory issue #631
23       environmentsElement(root.evalNode("environments"));
24       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
25       typeHandlerElement(root.evalNode("typeHandlers"));
26       mapperElement(root.evalNode("mappers"));//MappedStatement對象的初始化
27     } catch (Exception e) {
28       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
29     }
30   }

回到解析configuration標籤的這個方法,裏面正有解析mapper標籤的一步

 1 private void mapperElement(XNode parent) throws Exception {
 2     //parent是Configuration配置文件中的<mappers>標籤
 3     if (parent != null) {
 4        //遍歷<mappers>標籤下的所有子標籤
 5       for (XNode child : parent.getChildren()) {
 6         if ("package".equals(child.getName())) {
 7           //加載package包下的所有mapper
 8           String mapperPackage = child.getStringAttribute("name");
 9           configuration.addMappers(mapperPackage);//加載packege包下的所有mapper
10         } else {
11           //按resource或url或class加載單個mapper
12           String resource = child.getStringAttribute("resource");
13           String url = child.getStringAttribute("url");
14           String mapperClass = child.getStringAttribute("class");
15           if (resource != null && url == null && mapperClass == null) {
16             ErrorContext.instance().resource(resource);
17             InputStream inputStream = Resources.getResourceAsStream(resource);
18             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
19             mapperParser.parse();//解析xml文件流
20           } else if (resource == null && url != null && mapperClass == null) {
21             ErrorContext.instance().resource(url);
22             InputStream inputStream = Resources.getUrlAsStream(url);
23             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
24             mapperParser.parse();//解析xml文件流
25           } else if (resource == null && url == null && mapperClass != null) {
26             Class<?> mapperInterface = Resources.classForName(mapperClass);
27             configuration.addMapper(mapperInterface);//加載指定接口的mapper
28           } else {
29             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
30           }
31         }
32       }
33     }
34   }

先來看個最簡單的,根據指定接口加載mapper,也就是configuration.addMapper(mapperInterface)方法

1 public <T> void addMapper(Class<T> type) {
2     mapperRegistry.addMapper(type);
3   }

調用了mapperRegistry的addMapper方法。mapperRegistry是Configuration類的一個屬性,看起來他應該是mapper的註冊中心

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

以下就是他的源碼

 1 public <T> void addMapper(Class<T> type) {
 2         if (type.isInterface()) {
 3           if (hasMapper(type)) {
 4             throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
 5           }
 6           boolean loadCompleted = false;
 7           try {
 8             knownMappers.put(type, new MapperProxyFactory<T>(type));//把代理工廠與type組成鍵值對放入map
 9             MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);//?
10             parser.parse();
11             loadCompleted = true;
12           } finally {
13             if (!loadCompleted) {
14               knownMappers.remove(type);
15             }
16           }
17         }
18       }

addMapper顯然最爲重要,那麼第9行和第10行就是解析xml並初始化MappedStatement對象的代碼了。那麼就先看MapperAnnotationBuilder(mapper註解構造類),如下是他對應的構造器

 1  public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
 2     String resource = type.getName().replace('.', '/') + ".java (best guess)";
 3     this.assistant = new MapperBuilderAssistant(configuration, resource);
 4     this.configuration = configuration;
 5     this.type = type;
 6 
 7     sqlAnnotationTypes.add(Select.class);
 8     sqlAnnotationTypes.add(Insert.class);
 9     sqlAnnotationTypes.add(Update.class);
10     sqlAnnotationTypes.add(Delete.class);
11 
12     sqlProviderAnnotationTypes.add(SelectProvider.class);
13     sqlProviderAnnotationTypes.add(InsertProvider.class);
14     sqlProviderAnnotationTypes.add(UpdateProvider.class);
15     sqlProviderAnnotationTypes.add(DeleteProvider.class);
16   }

顯然的初始化完成後也就能找到對應的mapper接口.java文件了

MapperAnnotationBuilder有一個parse方法,也是創建MappedStatement的方法,源碼如下:

 1 public void parse() {
 2     String resource = type.toString();//mapper接口名,如:interface com.zxd.mapper.UserMapper
 3     //Configuration中Set<String> loadedResources 保存着已經加載過的mapper信息
 4     if (!configuration.isResourceLoaded(resource)) {//判斷是否已經加載過
 5       loadXmlResource();//加載XML資源
 6       configuration.addLoadedResource(resource);//加載的resource添加到Configuration的loadedResources中
 7       assistant.setCurrentNamespace(type.getName());
 8       parseCache();
 9       parseCacheRef();
10       Method[] methods = type.getMethods();//遍歷mapper的所有方法
11       for (Method method : methods) {
12         try {
13           
14           if (!method.isBridge()) {
15             parseStatement(method);
16           }
17         } catch (IncompleteElementException e) {
18           configuration.addIncompleteMethod(new MethodResolver(this, method));
19         }
20       }
21     }
22     parsePendingMethods();
23   }

這是加載xml的方法

 1 private void loadXmlResource() {
 5     if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
 6       String xmlResource = type.getName().replace('.', '/') + ".xml";//通過mapper接口名找到對應的mapper.xml路徑
 7       InputStream inputStream = null;
 8       try {
 9         //讀取mapper.xml文件流
10         inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
11       } catch (IOException e) {
12         // ignore, resource is not required
13       }
14       if (inputStream != null) {
15         //根據mapper.xml文件流創建XMLMapperBuilder對象
16         XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
17         //執行parse方法
18         xmlParser.parse();
19       }
20     }
21   }

//調用了構造器
 6   public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
 7     this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
 8         configuration, resource, sqlFragments);//調用下面一個構造方法
 9   }

那麼再看下XMLMapperBuilder 的 parse 方法,源碼如下:

 1 public void parse() {
 2     if (!configuration.isResourceLoaded(resource)) {
 3       configurationElement(parser.evalNode("/mapper"));//從XML解析類中獲取mapper標籤的內容
 4       configuration.addLoadedResource(resource);//將加載過的resource添加到Configuration中
 5       bindMapperForNamespace();//綁定mapper到對應mapper的命名空間中
 6     }
 7 
 8     parsePendingResultMaps();
 9     parsePendingChacheRefs();
10     parsePendingStatements();
11   }

//而解析xml的主要方法肯定就是configurationElement方法了,源碼如下:
 1 private void configurationElement(XNode context) {
 2     try {
 3       String namespace = context.getStringAttribute("namespace");//從xml中獲取namespace標籤內容
 4       if (namespace == null || namespace.equals("")) {
 5         throw new BuilderException("Mapper's namespace cannot be empty");
 6       }
 7       builderAssistant.setCurrentNamespace(namespace);
 8       cacheRefElement(context.evalNode("cache-ref"));//獲取cache-ref標籤內容
 9       cacheElement(context.evalNode("cache"));//獲取cache標籤內容
10       parameterMapElement(context.evalNodes("/mapper/parameterMap"));//獲取parameterMap內容
11       resultMapElements(context.evalNodes("/mapper/resultMap"));//獲取resultMap內容
12       sqlElement(context.evalNodes("/mapper/sql"));//獲取sql標籤內容
13       buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//解析獲取各種sql語句標籤內容
14     } catch (Exception e) {
15       throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
16     }
17   }

但是還是沒有發現mappedStatement的身影,回到builder裏面的parse方法,下面是對接口裏面的方法遍歷解析

for (Method method : methods) {
 8         try {
 9           // issue #237
10           if (!method.isBridge()) {
11             parseStatement(method);
12           }
13         } catch (IncompleteElementException e) {
14           configuration.addIncompleteMethod(new MethodResolver(this, method));
15         }
16       }

上面parseStatement方法是獲取方法的屬性,然後將xml中配置的標籤進行一一匹配,源碼如下:

 1  void parseStatement(Method method) {
 2     Class<?> parameterTypeClass = getParameterType(method);
 3     LanguageDriver languageDriver = getLanguageDriver(method);
 4     SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
 5     if (sqlSource != null) {
 6       Options options = method.getAnnotation(Options.class);
 7       final String mappedStatementId = type.getName() + "." + method.getName();
 8       Integer fetchSize = null;
 9       Integer timeout = null;
10       StatementType statementType = StatementType.PREPARED;
11       ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
12       SqlCommandType sqlCommandType = getSqlCommandType(method);
13       boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
14       boolean flushCache = !isSelect;
15       boolean useCache = isSelect;
16 
17       KeyGenerator keyGenerator;
18       String keyProperty = "id";
19       String keyColumn = null;
20       if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
21         // first check for SelectKey annotation - that overrides everything else
22         SelectKey selectKey = method.getAnnotation(SelectKey.class);
23         if (selectKey != null) {
24           keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
25           keyProperty = selectKey.keyProperty();
26         } else if (options == null) {
27           keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
28         } else {
29           keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
30           keyProperty = options.keyProperty();
31           keyColumn = options.keyColumn();
32         }
33       } else {
34         keyGenerator = new NoKeyGenerator();
35       }
36 
37       if (options != null) {
38         if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
39           flushCache = true;
40         } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
41           flushCache = false;
42         }
43         useCache = options.useCache();
44         fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
45         timeout = options.timeout() > -1 ? options.timeout() : null;
46         statementType = options.statementType();
47         resultSetType = options.resultSetType();
48       }
49 
50       String resultMapId = null;
51       ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
52       if (resultMapAnnotation != null) {
53         String[] resultMaps = resultMapAnnotation.value();
54         StringBuilder sb = new StringBuilder();
55         for (String resultMap : resultMaps) {
56           if (sb.length() > 0) {
57             sb.append(",");
58           }
59           sb.append(resultMap);
60         }
61         resultMapId = sb.toString();
62       } else if (isSelect) {
63         resultMapId = parseResultMap(method);
64       }
65 
66       assistant.addMappedStatement(
67           mappedStatementId,
68           sqlSource,
69           statementType,
70           sqlCommandType,
71           fetchSize,
72           timeout,
73           // ParameterMapID
74           null,
75           parameterTypeClass,
76           resultMapId,
77           getReturnType(method),
78           resultSetType,
79           flushCache,
80           useCache,
81           // TODO gcode issue #577
82           false,
83           keyGenerator,
84           keyProperty,
85           keyColumn,
86           // DatabaseID
87           null,
88           languageDriver,
89           // ResultSets
90           options != null ? nullOrEmpty(options.resultSets()) : null);
91     }
92   }

最終是調用MapperBuilderAssistant對象的addMappedStatement方法創建了MappedStatement對象,源碼如下:

 1 public MappedStatement addMappedStatement(
 2       String id,
 3       SqlSource sqlSource,
 4       StatementType statementType,
 5       SqlCommandType sqlCommandType,
 6       Integer fetchSize,
 7       Integer timeout,
 8       String parameterMap,
 9       Class<?> parameterType,
10       String resultMap,
11       Class<?> resultType,
12       ResultSetType resultSetType,
13       boolean flushCache,
14       boolean useCache,
15       boolean resultOrdered,
16       KeyGenerator keyGenerator,
17       String keyProperty,
18       String keyColumn,
19       String databaseId,
20       LanguageDriver lang,
21       String resultSets) {
22 
23     if (unresolvedCacheRef) {
24       throw new IncompleteElementException("Cache-ref not yet resolved");
25     }
26 
27     id = applyCurrentNamespace(id, false);
28     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
29 
30     MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
31         .resource(resource)
32         .fetchSize(fetchSize)
33         .timeout(timeout)
34         .statementType(statementType)
35         .keyGenerator(keyGenerator)
36         .keyProperty(keyProperty)
37         .keyColumn(keyColumn)
38         .databaseId(databaseId)
39         .lang(lang)
40         .resultOrdered(resultOrdered)
41         .resultSets(resultSets)
42         .resultMaps(getStatementResultMaps(resultMap, resultType, id))
43         .resultSetType(resultSetType)
44         .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
45         .useCache(valueOrDefault(useCache, isSelect))
46         .cache(currentCache);
47 
48     ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
49     if (statementParameterMap != null) {
50       statementBuilder.parameterMap(statementParameterMap);
51     }
52 
53     MappedStatement statement = statementBuilder.build();
54     configuration.addMappedStatement(statement);
55     return statement;
56   }

statementBuilder調用build方法創建了MappedStatement對象之後,就調用了Configuration的addMappedStatement方法,從而添加到了Configuration中的Map<String, MappedStatement> mappedStatements集合中,而key就是MappedStatement的id

現在mappedStatement被放在了map裏面與方法id匹配,而mapper代理工廠也之前被放到map裏面和type接口匹配
knownMappers.put(type, new MapperProxyFactory<T>(type));//把代理工廠與type組成鍵值對放入map
那麼關鍵就是他們怎麼建立聯繫,生成對應的mapper代理對象,而Mapper接口只定義了接口沒有實現類,那麼猜想可知返回的應該就是更加mapper.xml生成的實例了。具體是如何實現的呢, 先看下這個方法是如何實現的?

DefaultSqlSession實現該方法的代碼如下:

1 @Override
2   public <T> T getMapper(Class<T> type) {
3     return configuration.<T>getMapper(type, this);
4   }

方法很簡單,調用了Configuration對象的getMapper方法,跟上次的addMapper是不一樣的,那麼接下來再看下Configuration裏面是如何實現的。代碼如下:

1 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
2     return mapperRegistry.getMapper(type, sqlSession);
3   }

調用了mapperRegistry的getMapper方法,參數分別是Class對象和sqlSession,再繼續看MapperRegistry的實現,代碼如下:

 1  @SuppressWarnings("unchecked")
 2     public <T> T getMapper(Class<T> type, SqlSession sqlSession)
 3     {
 4         // 從konwMappers獲取MapperProxyFactory對象
 5         final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>)knownMappers.get(type);
 6         if (mapperProxyFactory == null)
 7         {
 8             throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 9         }
10         try
11         {
12             // 通過mapper代理工廠創建新實例
13             return mapperProxyFactory.newInstance(sqlSession);
14         }
15         catch (Exception e)
16         {
17             throw new BindingException("Error getting mapper instance. Cause: " + e, e);
18         }
19     }

顯然正常情況下knowmappers應該這個時候是已經註冊了這個工廠的了,那麼他這裏的newInstance就是返回對應的代理對象了

 1  @SuppressWarnings("unchecked")
 2     protected T newInstance(MapperProxy<T> mapperProxy) {
 3         //通過代理獲取mapper接口的新實例
 4       return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
 5     }
 6 
 7     public T newInstance(SqlSession sqlSession) {
 8       //創建mapper代理對象,調用newInstance方法
 9       final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
10       return newInstance(mapperProxy);
11     }

newProxyInstance就是我們jdk中提供的動態代理方法,那麼這個mapperProxy應該是實現了invocationHandler接口的invoke方法

 1 @Override
 2     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 3       //判斷執行的方法是否是來自父類Object類的方法,也就是如toString、hashCode等方法
 4       //如果是則直接通過反射執行該方法,如果不是Object的方法則再往下走,如果不加這個判斷會發生什麼呢?
 5       //由於mapper接口除了定義的接口方法還包括繼承於Object的方法,如果不加判斷則會繼續往下走,而下面的執行過程是從mapper.xml尋找對應的實現方法,
 6       //由於mapper.xml只實現了mapper中的接口方法,而沒有toString和hashCode方法,從而就會導致這些方法無法被實現。
 7       if (Object.class.equals(method.getDeclaringClass())) {
 8         try {
 9           return method.invoke(this, args);
10         } catch (Throwable t) {
11           throw ExceptionUtil.unwrapThrowable(t);
12         }
13       }
14       final MapperMethod mapperMethod = cachedMapperMethod(method);
15       //執行mapperMethod對象的execute方法
16       return mapperMethod.execute(sqlSession, args);
17     }
18 
19     private MapperMethod cachedMapperMethod(Method method) {
20       //從緩存中根據method對象獲取MapperMethod對象
21       MapperMethod mapperMethod = methodCache.get(method);
22       if (mapperMethod == null) {
23         //如果mapperMethod爲空則新建MapperMethod方法
24         mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
25         methodCache.put(method, mapperMethod);
26       }
27       return mapperMethod;
28     }

那麼這個mapperMethod實際就是我們真正執行的增刪查改方法了,而且他也有緩存機制,被放到map保存,也就不用每次都封裝一個方法爲mapperMethod,不妨看看MapperMethod是被創建的。

1   private final SqlCommand command;
2   private final MethodSignature method;
3 
4   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
5     this.command = new SqlCommand(config, mapperInterface, method);
6     this.method = new MethodSignature(config, mapperInterface, method);
7   }

這裏涉及到了兩個類SqlCommand和MethodSignature,先從SqlCommand看起。

 1 public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
 2         String statementName = mapperInterface.getName() + "." + method.getName();//接口方法名,也就是id
 3         MappedStatement ms = null;
 4         if (configuration.hasStatement(statementName)) {
 5             //從configuration中根據接口名獲取MappedStatement對象
 6           ms = configuration.getMappedStatement(statementName);
 7         } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35
 8             //如果該方法不是該mapper接口的方法,則從mapper的父類中找尋該接口對應的MappedStatement對象
 9           String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
10           if (configuration.hasStatement(parentStatementName)) {
11             ms = configuration.getMappedStatement(parentStatementName);
12           }
13         }
14         if (ms == null) {
15           if(method.getAnnotation(Flush.class) != null){
16             name = null;
17             type = SqlCommandType.FLUSH;
18           } else {
19             throw new BindingException("Invalid bound statement (not found): " + statementName);
20           }
21         } else {
22           name = ms.getId();//設置name爲MappedStatement的id,而id的值就是xml中對應的sql語句
23           type = ms.getSqlCommandType();//設置type爲MappedStatement的sql類型
24           if (type == SqlCommandType.UNKNOWN) {
25             throw new BindingException("Unknown execution method for: " + name);
26           }
27         }
28       }

執行execute方法,代碼如下:

 1 public Object execute(SqlSession sqlSession, Object[] args) {
 2         Object result;
 3         if (SqlCommandType.INSERT == command.getType()) {//如果執行insert命令
 4           Object param = method.convertArgsToSqlCommandParam(args);//構建參數
 5           result = rowCountResult(sqlSession.insert(command.getName(), param));//調用sqlSession的insert方法
 6         } else if (SqlCommandType.UPDATE == command.getType()) {//如果執行update命令
 7           Object param = method.convertArgsToSqlCommandParam(args);//構建參數
 8           result = rowCountResult(sqlSession.update(command.getName(), param));//調用sqlSession的update方法
 9         } else if (SqlCommandType.DELETE == command.getType()) {//如果執行delete命令
10           Object param = method.convertArgsToSqlCommandParam(args);//構建參數
11           result = rowCountResult(sqlSession.delete(command.getName(), param));//調用sqlSession的delete方法
12         } else if (SqlCommandType.SELECT == command.getType()) {//如果執行select命令
13           if (method.returnsVoid() && method.hasResultHandler()) {//判斷接口返回類型,更加返回數據類型執行對應的select語句
14             executeWithResultHandler(sqlSession, args);
15             result = null;
16           } else if (method.returnsMany()) {
17             result = executeForMany(sqlSession, args);
18           } else if (method.returnsMap()) {
19             result = executeForMap(sqlSession, args);
20           } else if (method.returnsCursor()) {
21             result = executeForCursor(sqlSession, args);
22           } else {
23             Object param = method.convertArgsToSqlCommandParam(args);
24             result = sqlSession.selectOne(command.getName(), param);
25           }
26         } else if (SqlCommandType.FLUSH == command.getType()) {
27             result = sqlSession.flushStatements();
28         } else {
29           throw new BindingException("Unknown execution method for: " + command.getName());
30         }
31         if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
32           throw new BindingException("Mapper method '" + command.getName() 
33               + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
34         }
35         return result;
36       }

到這裏終於是看到了MappedStatement的身影,根據mapper的Class對象和method對象從Configuration的集合對象中獲取指定的MappedStatement對象,然後根據MappedStatement對象的值初始化SqlCommand對象的屬性(xml裏面的id和sql類型)。而MethodSignature則是sql語句的簽名,主要作用就是對sql參數與返回結果類型的判斷。

總結:

  1. sqlSession調用configuration對象的getMapper方法,configuration調用mapperRegistry的getMapper方法

  2. mapperRegistry根據mapper的type獲取對應的Mapper代理工廠

  3. 通過mapper代理工廠創建mapper的代理

  4. 執行mapper方法時,通過代理調用invoke,在緩存中尋找或者創建該mapper方法的MapperMethod對象

  5. MapperMethod對象的創建是通過從configuration對象中獲取指定的MappedStatement對象來獲取具體的sql語句以及參數和返回結果類型

  6. 調用sqlSession對應的insert、update、delete和select方法執行mapper的方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章