MyBatis框架的使用及源碼分析(八) MapperMethod


從 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我們知道Mapper,通過MapperProxy代理類執行他的接口方法,當mapper方法被調用的時候對應的MapperProxy會生成相應的MapperMethod並且會緩存起來,這樣當多次調用同一個mapper方法時候只會生成一個MapperMethod,提高了時間和內存效率:

//這里會攔截Mapper接口的所有方法 
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) { //如果是Object中定義的方法,直接執行。如toString(),hashCode()等
      try {
        return method.invoke(this, args);//
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);  //其他Mapper接口定義的方法交由mapperMethod來執行
    return mapperMethod.execute(sqlSession, args);
  }

最后2句關鍵,我們執行所調用Mapper的每一個接口方法,最后返回的是MapperMethod.execute方法。每一個MapperMethod對應了一個mapper文件中配置的一個sql語句或FLUSH配置,對應的sql語句通過mapper對應的class文件名+方法名從Configuration對象中獲得。

我們看一下MapperMethod的源碼:

package org.apache.ibatis.binding;

import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;

/**
* MapperMethod代理Mapper所有方法
*/
public class MapperMethod { //一個內部封 封裝了SQL標簽的類型 insert update delete select private final SqlCommand command;
//一個內部類 封裝了方法的參數信息 返回類型信息等 
private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); }
/**
*
這個方法是對SqlSession的包裝,對應insert、delete、update、select四種操作
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;//返回結果
   //INSERT操作
if (SqlCommandType.INSERT == command.getType()) {
//處理參數
Object param = method.convertArgsToSqlCommandParam(args);
//調用sqlSession的insert方法 
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
//UPDATE操作 同上
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
//DELETE操作 同上
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
//如果返回void 並且參數有resultHandler  ,則調用 void select(String statement, Object parameter, ResultHandler handler);方法  
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//如果返回多行結果,executeForMany這個方法調用 <E> List<E> selectList(String statement, Object parameter);  
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
//如果返回類型是MAP 則調用executeForMap方法 
result = executeForMap(sqlSession, args);
} else {
//否則就是查詢單個對象
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
//接口方法沒有和sql命令綁定
throw new BindingException("Unknown execution method for: " + command.getName());
}
    //如果返回值為空 並且方法返回值類型是基礎類型 並且不是VOID 則拋出異常  
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; } private Object rowCountResult(int rowCount) { final Object result; if (method.returnsVoid()) { result = null; } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) { result = rowCount; } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) { result = (long) rowCount; } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) { result = (rowCount > 0); } else { throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType()); } return result; } private void executeWithResultHandler(SqlSession sqlSession, Object[] args) { MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName()); if (void.class.equals(ms.getResultMaps().get(0).getType())) { throw new BindingException("method " + command.getName() + " needs either a @ResultMap annotation, a @ResultType annotation," + " or a resultType attribute in XML so a ResultHandler can be used as a parameter."); } Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args)); } else { sqlSession.select(command.getName(), param, method.extractResultHandler(args)); } }
//返回多行結果 調用sqlSession.selectList方法 
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) {
//如果參數含有rowBounds則調用分頁的查詢  RowBounds rowBounds
= method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else {
//沒有分頁則調用普通查詢  result
= sqlSession.<E>selectList(command.getName(), param); } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; } private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) { Object collection = config.getObjectFactory().create(method.getReturnType()); MetaObject metaObject = config.newMetaObject(collection); metaObject.addAll(list); return collection; } @SuppressWarnings("unchecked") private <E> E[] convertToArray(List<E> list) { E[] array = (E[]) Array.newInstance(method.getReturnType().getComponentType(), list.size()); array = list.toArray(array); return array; } private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) { Map<K, V> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds); } else { result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey()); } return result; } public static class ParamMap<V> extends HashMap<String, V> { private static final long serialVersionUID = -2212268410512043556L; @Override public V get(Object key) { if (!super.containsKey(key)) { throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet()); } return super.get(key); } }
//封裝了具體執行的動作
public static class SqlCommand { //xml標簽的id  private final String name;
//insert update delete select的具體類型 
private final SqlCommandType type; public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
//拿到全名 比如 org.mybatis.example.UserMapper.selectByPrimaryKey String statementName
= mapperInterface.getName() + "." + method.getName();
//MappedStatement對象,封裝一個Mapper接口對應的sql操作  MappedStatement ms
= null; if (configuration.hasStatement(statementName)) {
//從Configuration對象查找是否有這個方法的全限定名稱,如果有則根據方法的全限定名稱獲取MappedStatement ms
= configuration.getMappedStatement(statementName); } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
//如果沒有在Configuration對象中找到這個方法,則向上父類中獲取全限定方法名 String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName(); if (configuration.hasStatement(parentStatementName)) { ms = configuration.getMappedStatement(parentStatementName); } } if (ms == null) { throw new BindingException("Invalid bound statement (not found): " + statementName); }
//這個ms.getId,其實就是我們在mapper.xml配置文件中配置一條sql語句設置的id屬性的值 name
= ms.getId();
//sql的類型(insert、update、delete、select) type
= ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) {
//判斷SQL標簽類型 未知就拋異常
throw new BindingException("Unknown execution method for: " + name); } } public String getName() { return name; } public SqlCommandType getType() { return type; } }
/**
* 方法簽名,封裝了接口當中方法的 參數類型 返回值類型 等信息
*/
public static class MethodSignature { private final boolean returnsMany;//是否返回多條結果  private final boolean returnsMap; //返回值是否是MAP  private final boolean returnsVoid;//返回值是否是VOID  private final Class<?> returnType; //返回值類型  private final String mapKey; private final Integer resultHandlerIndex;//resultHandler類型參數的位置  private final Integer rowBoundsIndex; //rowBound類型參數的位置 private final SortedMap<Integer, String> params;//用來存放參數信息 private final boolean hasNamedParameters;  //是否存在命名參數  public MethodSignature(Configuration configuration, Method method) throws BindingException { this.returnType = method.getReturnType(); this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); this.mapKey = getMapKey(method); this.returnsMap = (this.mapKey != null); this.hasNamedParameters = hasNamedParams(method); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters)); }
/**
* 創建SqlSession對象需要傳遞的參數邏輯
* args是用戶mapper所傳遞的方法參數列表, 如果方法沒有參數,則返回null. 
* 如果方法只包含一個參數並且不包含命名參數, 則返回傳遞的參數值。
* 如果包含多個參數或包含命名參數,則返回包含名字和對應值的map對象、
*/
public Object convertArgsToSqlCommandParam(Object[] args) { final int paramCount = params.size(); if (args == null || paramCount == 0) { return null; } else if (!hasNamedParameters && paramCount == 1) { return args[params.keySet().iterator().next()]; } else { final Map<String, Object> param = new ParamMap<Object>(); int i = 0; for (Map.Entry<Integer, String> entry : params.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); // issue #71, add param names as param1, param2...but ensure backward compatibility final String genericParamName = "param" + String.valueOf(i + 1); if (!param.containsKey(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } } public boolean hasRowBounds() { return (rowBoundsIndex != null); } public RowBounds extractRowBounds(Object[] args) { return (hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null); } public boolean hasResultHandler() { return (resultHandlerIndex != null); } public ResultHandler extractResultHandler(Object[] args) { return (hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null); } public String getMapKey() { return mapKey; } public Class<?> getReturnType() { return returnType; } public boolean returnsMany() { return returnsMany; } public boolean returnsMap() { return returnsMap; } public boolean returnsVoid() { return returnsVoid; } private Integer getUniqueParamIndex(Method method, Class<?> paramType) { Integer index = null; final Class<?>[] argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i++) { if (paramType.isAssignableFrom(argTypes[i])) { if (index == null) { index = i; } else { throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters"); } } } return index; } private String getMapKey(Method method) { String mapKey = null; if (Map.class.isAssignableFrom(method.getReturnType())) { final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class); if (mapKeyAnnotation != null) { mapKey = mapKeyAnnotation.value(); } } return mapKey; } private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) { final SortedMap<Integer, String> params = new TreeMap<Integer, String>(); final Class<?>[] argTypes = method.getParameterTypes(); for (int i = 0; i < argTypes.length; i++) { if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) { String paramName = String.valueOf(params.size()); if (hasNamedParameters) { paramName = getParamNameFromAnnotation(method, i, paramName); } params.put(i, paramName); } } return params; } private String getParamNameFromAnnotation(Method method, int i, String paramName) { final Object[] paramAnnos = method.getParameterAnnotations()[i]; for (Object paramAnno : paramAnnos) { if (paramAnno instanceof Param) { paramName = ((Param) paramAnno).value(); } } return paramName; } private boolean hasNamedParams(Method method) { boolean hasNamedParams = false; final Object[][] paramAnnos = method.getParameterAnnotations(); for (Object[] paramAnno : paramAnnos) { for (Object aParamAnno : paramAnno) { if (aParamAnno instanceof Param) { hasNamedParams = true; break; } } } return hasNamedParams; } } }

 

當執行MapperMethod的execute方法的時候,根據當前MapperMethod對應的mapper配置會執行Session的insert, update, delete, select, selectList, selectMap, selectCursor, selectOne或flushStatements方法。
具體執行Session對象的方法對照如下:

Mapper節點 SqlSession方法
insert insert
update update
delete delete
select select: 方法返回void,並且包含resultHandler配置
select selectList:方法返回數組或Collection子類
select selectMap: 存在MapKey注解
select selectCursor: 方法返回Cursor
select selectOne 其它
flush Flush注解


免責聲明!

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



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