Mybatis詳解(二)--動態代理(未集成Spring)

mybatis: xxMapper ================> xxMapper.xml
2.SqlSession表明和數據庫的一次會話,用完必須關閉。
3.SqlSession和Connection同樣,都是非線程安全的,每次使用都是應該去獲取新的對象,不要將這個對象定義在類變量中使用!
4.mapper接口沒有實現類,可是mybatis這個接口生成一個代理對象
<!--將接口與xml文件進行綁定 -->
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
5.兩個重要的配置文件
mybatis的全局配置文件:包含數據庫鏈接池信息,事務管理器信息等..系統運行環境信息。
sql映射文件:保存了每個sql語句的映射信息。html

 

 

在開始動態代理的原理講解之前,咱們先看一下集成mybatis之後dao層不使用動態代理以及使用動態代理的兩種實現方式,經過對比咱們本身實現dao層接口以及mybatis動態代理能夠更加直觀的展示出mybatis動態代理替咱們所作的工做,有利於咱們理解動態代理的過程,講解完之後咱們再進行動態代理的原理解析,此講解基於mybatis的環境已經搭建完成,而且已經實現了基本的用戶類編寫以及用戶類的Dao接口的聲明,下面是Dao層的接口代碼java

public interface UserDao {
/*
查詢全部用戶信息
*/
List<User> findAll();sql

/**
* 保存用戶
* @param user
*/
void save(User user);數據庫

/**
* 更新用戶
* @return
*/
void update(User user);
/**
* 刪除用戶
*/
void delete(Integer userId);安全

/**
* 查找一個用戶
* @param userId
* @return
*/
User findOne(Integer userId);session

/**
* 根據名字模糊查詢
* @param name
* @return
*/
List<User> findByName(String name);
/**
* 根據組合對象進行模糊查詢
* @param vo
* @return
*/
List<User> findByQueryVo(QueryVo vo);
}mybatis


複製代碼
 1 public interface UserDao {
 2     /*
 3     查詢全部用戶信息
 4      */
 5     List<User> findAll();
 6 
 7     /**
 8      * 保存用戶
 9      * @param user
10      */
11     void save(User user);
12 
13     /**
14      * 更新用戶
15      * @return
16      */
17     void update(User user);
18     /**
19      * 刪除用戶
20      */
21     void delete(Integer userId);
22 
23     /**
24      * 查找一個用戶
25      * @param userId
26      * @return
27      */
28     User findOne(Integer userId);
29 
30     /**
31      * 根據名字模糊查詢
32      * @param name
33      * @return
34      */
35     List<User> findByName(String name);
36     /**
37      * 根據組合對象進行模糊查詢
38      * @param vo
39      * @return
40      */
41     List<User> findByQueryVo(QueryVo vo);
42 }
複製代碼

1、Mybatis dao層兩種實現方式的對比

1.dao層不使用動態代理

 dao層不使用動態代理的話,就須要咱們本身實現dao層的接口,爲了簡便起見,我只是實現了Dao接口中的findAll方法,以此方法爲例子來展示咱們本身實現Dao的方式的狀況,讓咱們來看代碼:app

public class UserDaoImpl implements UserDao{
private SqlSessionFactory factory;
public UserDaoImpl(SqlSessionFactory factory){
this.factory = factory;
}
public List<User> findAll() {
//1.獲取sqlSession對象
SqlSession sqlSession = factory.openSession();
//2.調用selectList方法
List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll");
//3.關閉流
sqlSession.close();
return list;
}ide

public void save(User user) {this

}

public void update(User user) {

}

public void delete(Integer userId) {

}

public User findOne(Integer userId) {
return null;
}

public List<User> findByName(String name) {
return null;
}

public List<User> findByQueryVo(QueryVo vo) {
return null;
}


複製代碼
 1 public class UserDaoImpl implements UserDao{
 2     private SqlSessionFactory factory;
 3     public UserDaoImpl(SqlSessionFactory factory){
 4         this.factory = factory;
 5     }
 6     public List<User> findAll() {
 7         //1.獲取sqlSession對象
 8         SqlSession sqlSession = factory.openSession();
 9         //2.調用selectList方法
10         List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll");
11         //3.關閉流
12         sqlSession.close();
13         return list;
14     }
15 
16     public void save(User user) {
17 
18     }
19 
20     public void update(User user) {
21 
22     }
23 
24     public void delete(Integer userId) {
25 
26     }
27 
28     public User findOne(Integer userId) {
29         return null;
30     }
31 
32     public List<User> findByName(String name) {
33         return null;
34     }
35 
36     public List<User> findByQueryVo(QueryVo vo) {
37         return null;
38     }
複製代碼

這裏的關鍵代碼 List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),須要咱們本身手動調用SqlSession裏面的方法,基於動態代理的方式最後的目標也是成功的調用到這裏。

注意:若是是添加,更新或者刪除操做的話須要在方法中增長事務的提交。

2.dao層使用Mybatis的動態代理

使用動態代理的話Dao層的接口聲明完成之後只須要在使用的時候經過SqlSession對象的getMapper方法獲取對應Dao接口的代理對象,關鍵代碼以下:

//3.獲取SqlSession對象

SqlSession session = factory.openSession();

//4.獲取dao的代理對象

UserDao mapper = session.getMapper(UserDao.class);
//5.執行查詢全部的方法

List<User> list = mapper.findAll();

獲取到dao層的代理對象之後經過代理對象調用查詢方法就能夠實現查詢全部用戶列表的功能。

2、Mybatis動態代理實現方式的原理解析

動態代理中最重要的類:SqlSession、MapperProxy、MapperMethod,下面開始從入口方法到調用結束的過程分析。

  1. 調用方法的開始:
    //4.獲取dao的代理對象
    UserDao mapper = session.getMapper(UserDao.class); 由於SqlSesseion爲接口,因此咱們經過Debug方式發現這裏使用的實現類爲DefaultSqlSession。
  2. 找到DeaultSqlSession中的getMapper方法,發現這裏沒有作其餘的動做,只是將工做繼續拋到了Configuration類中,Configuration爲類不是接口,能夠直接進入該類的getMapper方法中
@Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

   3. 找到Configuration類的getMapper方法,這裏也是將工做繼續交到MapperRegistry的getMapper的方法中,因此咱們繼續向下進行。

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

   4. 找到MapperRegistry的getMapper的方法,看到這裏發現和之前不同了,經過MapperProxyFactory的命名方式咱們知道這裏將經過這個工廠生成咱們所關注的MapperProxy的代理類,而後咱們經過mapperProxyFactory.newInstance(sqlSession);進入MapperProxyFactory的newInstance方法中

複製代碼
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    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);
    }
  }
複製代碼

  5. 找到MapperProxyFactory的newIntance方法,經過參數類型SqlSession能夠得知,上面的調用先進入第二個newInstance方法中並建立咱們所須要重點關注的MapperProxy對象,第二個方法中再調用第一個newInstance方法並將MapperProxy對象傳入進去,根據該對象建立代理類並返回。這裏已經獲得須要的代理類了,可是咱們的代理類所作的工做還得繼續向下看MapperProxy類。

複製代碼
 protected T newInstance(MapperProxy<T> mapperProxy) {
    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);
  }
複製代碼

  6. 找到MapperProxy類,發現其確實實現了JDK動態代理必須實現的接口InvocationHandler,因此咱們重點關注invoke()方法,這裏看到在invoke方法裏先獲取MapperMethod類,而後調用mapperMethod.execute(),因此咱們繼續查看MapperMethod類的execute方法。 

複製代碼
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

  @UsesJava7
  private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
      throws Throwable {
    final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    final Class<?> declaringClass = method.getDeclaringClass();
    return constructor
        .newInstance(declaringClass,
            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  }

  /**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return ((method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
        && method.getDeclaringClass().isInterface();
  }
}
複製代碼

7. 找到類MapperMethod類的execute方法,發現execute中經過調用本類中的其餘方法獲取並封裝返回結果,咱們來看一下MapperMethod整個類。

複製代碼
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        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 if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        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;
  }
複製代碼

8. MapperMethod類是整個代理機制的核心類,對SqlSession中的操做進行了封裝使用。 該類裏有兩個內部類SqlCommand和MethodSignature。 SqlCommand用來封裝CRUD操做,也就是咱們在xml中配置的操做的節點。每一個節點都會生成一個MappedStatement類。MethodSignature用來封裝方法的參數以及返回類型,在execute的方法中咱們發如今這裏又回到了SqlSession中的接口調用,和咱們本身實現UerDao接口的方式中直接用SqlSession對象調用DefaultSqlSession的實現類的方法是同樣的,通過一大圈的代理又回到了原地,這就是整個動態代理的實現過程了。

public class MapperMethod {

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, mapperInterface, method);
}

public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
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 if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
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;
}

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));
}
}

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
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 <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
Cursor<T> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
} else {
result = sqlSession.<T>selectCursor(command.getName(), param);
}
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> Object convertToArray(List<E> list) {
Class<?> arrayComponentType = method.getReturnType().getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
} else {
return list.toArray((E[])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 {

private final String name;
private final SqlCommandType type;

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}

public String getName() {
return name;
}

public SqlCommandType getType() {
return type;
}

private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}

public static class MethodSignature {

private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;

public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.returnsCursor = Cursor.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}

public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}

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;
}

public boolean returnsCursor() {
return returnsCursor;
}

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;
}
}
轉自: http://www.javashuo.com/article/p-wsvlrqfo-nc.html
複製代碼
  1 public class MapperMethod {
  2 
  3   private final SqlCommand command;
  4   private final MethodSignature method;
  5 
  6   public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
  7     this.command = new SqlCommand(config, mapperInterface, method);
  8     this.method = new MethodSignature(config, mapperInterface, method);
  9   }
 10 
 11   public Object execute(SqlSession sqlSession, Object[] args) {
 12     Object result;
 13     switch (command.getType()) {
 14       case INSERT: {
 15         Object param = method.convertArgsToSqlCommandParam(args);
 16         result = rowCountResult(sqlSession.insert(command.getName(), param));
 17         break;
 18       }
 19       case UPDATE: {
 20         Object param = method.convertArgsToSqlCommandParam(args);
 21         result = rowCountResult(sqlSession.update(command.getName(), param));
 22         break;
 23       }
 24       case DELETE: {
 25         Object param = method.convertArgsToSqlCommandParam(args);
 26         result = rowCountResult(sqlSession.delete(command.getName(), param));
 27         break;
 28       }
 29       case SELECT:
 30         if (method.returnsVoid() && method.hasResultHandler()) {
 31           executeWithResultHandler(sqlSession, args);
 32           result = null;
 33         } else if (method.returnsMany()) {
 34           result = executeForMany(sqlSession, args);
 35         } else if (method.returnsMap()) {
 36           result = executeForMap(sqlSession, args);
 37         } else if (method.returnsCursor()) {
 38           result = executeForCursor(sqlSession, args);
 39         } else {
 40           Object param = method.convertArgsToSqlCommandParam(args);
 41           result = sqlSession.selectOne(command.getName(), param);
 42         }
 43         break;
 44       case FLUSH:
 45         result = sqlSession.flushStatements();
 46         break;
 47       default:
 48         throw new BindingException("Unknown execution method for: " + command.getName());
 49     }
 50     if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
 51       throw new BindingException("Mapper method '" + command.getName() 
 52           + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
 53     }
 54     return result;
 55   }
 56 
 57   private Object rowCountResult(int rowCount) {
 58     final Object result;
 59     if (method.returnsVoid()) {
 60       result = null;
 61     } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
 62       result = rowCount;
 63     } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
 64       result = (long)rowCount;
 65     } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
 66       result = rowCount > 0;
 67     } else {
 68       throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
 69     }
 70     return result;
 71   }
 72 
 73   private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
 74     MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
 75     if (void.class.equals(ms.getResultMaps().get(0).getType())) {
 76       throw new BindingException("method " + command.getName() 
 77           + " needs either a @ResultMap annotation, a @ResultType annotation," 
 78           + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
 79     }
 80     Object param = method.convertArgsToSqlCommandParam(args);
 81     if (method.hasRowBounds()) {
 82       RowBounds rowBounds = method.extractRowBounds(args);
 83       sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
 84     } else {
 85       sqlSession.select(command.getName(), param, method.extractResultHandler(args));
 86     }
 87   }
 88 
 89   private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 90     List<E> result;
 91     Object param = method.convertArgsToSqlCommandParam(args);
 92     if (method.hasRowBounds()) {
 93       RowBounds rowBounds = method.extractRowBounds(args);
 94       result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
 95     } else {
 96       result = sqlSession.<E>selectList(command.getName(), param);
 97     }
 98     // issue #510 Collections & arrays support
 99     if (!method.getReturnType().isAssignableFrom(result.getClass())) {
100       if (method.getReturnType().isArray()) {
101         return convertToArray(result);
102       } else {
103         return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
104       }
105     }
106     return result;
107   }
108 
109   private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
110     Cursor<T> result;
111     Object param = method.convertArgsToSqlCommandParam(args);
112     if (method.hasRowBounds()) {
113       RowBounds rowBounds = method.extractRowBounds(args);
114       result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
115     } else {
116       result = sqlSession.<T>selectCursor(command.getName(), param);
117     }
118     return result;
119   }
120 
121   private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
122     Object collection = config.getObjectFactory().create(method.getReturnType());
123     MetaObject metaObject = config.newMetaObject(collection);
124     metaObject.addAll(list);
125     return collection;
126   }
127 
128   @SuppressWarnings("unchecked")
129   private <E> Object convertToArray(List<E> list) {
130     Class<?> arrayComponentType = method.getReturnType().getComponentType();
131     Object array = Array.newInstance(arrayComponentType, list.size());
132     if (arrayComponentType.isPrimitive()) {
133       for (int i = 0; i < list.size(); i++) {
134         Array.set(array, i, list.get(i));
135       }
136       return array;
137     } else {
138       return list.toArray((E[])array);
139     }
140   }
141 
142   private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
143     Map<K, V> result;
144     Object param = method.convertArgsToSqlCommandParam(args);
145     if (method.hasRowBounds()) {
146       RowBounds rowBounds = method.extractRowBounds(args);
147       result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
148     } else {
149       result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
150     }
151     return result;
152   }
153 
154   public static class ParamMap<V> extends HashMap<String, V> {
155 
156     private static final long serialVersionUID = -2212268410512043556L;
157 
158     @Override
159     public V get(Object key) {
160       if (!super.containsKey(key)) {
161         throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
162       }
163       return super.get(key);
164     }
165 
166   }
167 
168   public static class SqlCommand {
169 
170     private final String name;
171     private final SqlCommandType type;
172 
173     public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
174       final String methodName = method.getName();
175       final Class<?> declaringClass = method.getDeclaringClass();
176       MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
177           configuration);
178       if (ms == null) {
179         if (method.getAnnotation(Flush.class) != null) {
180           name = null;
181           type = SqlCommandType.FLUSH;
182         } else {
183           throw new BindingException("Invalid bound statement (not found): "
184               + mapperInterface.getName() + "." + methodName);
185         }
186       } else {
187         name = ms.getId();
188         type = ms.getSqlCommandType();
189         if (type == SqlCommandType.UNKNOWN) {
190           throw new BindingException("Unknown execution method for: " + name);
191         }
192       }
193     }
194 
195     public String getName() {
196       return name;
197     }
198 
199     public SqlCommandType getType() {
200       return type;
201     }
202 
203     private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
204         Class<?> declaringClass, Configuration configuration) {
205       String statementId = mapperInterface.getName() + "." + methodName;
206       if (configuration.hasStatement(statementId)) {
207         return configuration.getMappedStatement(statementId);
208       } else if (mapperInterface.equals(declaringClass)) {
209         return null;
210       }
211       for (Class<?> superInterface : mapperInterface.getInterfaces()) {
212         if (declaringClass.isAssignableFrom(superInterface)) {
213           MappedStatement ms = resolveMappedStatement(superInterface, methodName,
214               declaringClass, configuration);
215           if (ms != null) {
216             return ms;
217           }
218         }
219       }
220       return null;
221     }
222   }
223 
224   public static class MethodSignature {
225 
226     private final boolean returnsMany;
227     private final boolean returnsMap;
228     private final boolean returnsVoid;
229     private final boolean returnsCursor;
230     private final Class<?> returnType;
231     private final String mapKey;
232     private final Integer resultHandlerIndex;
233     private final Integer rowBoundsIndex;
234     private final ParamNameResolver paramNameResolver;
235 
236     public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
237       Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
238       if (resolvedReturnType instanceof Class<?>) {
239         this.returnType = (Class<?>) resolvedReturnType;
240       } else if (resolvedReturnType instanceof ParameterizedType) {
241         this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
242       } else {
243         this.returnType = method.getReturnType();
244       }
245       this.returnsVoid = void.class.equals(this.returnType);
246       this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
247       this.returnsCursor = Cursor.class.equals(this.returnType);
248       this.mapKey = getMapKey(method);
249       this.returnsMap = (this.mapKey != null);
250       this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
251       this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
252       this.paramNameResolver = new ParamNameResolver(configuration, method);
253     }
254 
255     public Object convertArgsToSqlCommandParam(Object[] args) {
256       return paramNameResolver.getNamedParams(args);
257     }
258 
259     public boolean hasRowBounds() {
260       return rowBoundsIndex != null;
261     }
262 
263     public RowBounds extractRowBounds(Object[] args) {
264       return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
265     }
266 
267     public boolean hasResultHandler() {
268       return resultHandlerIndex != null;
269     }
270 
271     public ResultHandler extractResultHandler(Object[] args) {
272       return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
273     }
274 
275     public String getMapKey() {
276       return mapKey;
277     }
278 
279     public Class<?> getReturnType() {
280       return returnType;
281     }
282 
283     public boolean returnsMany() {
284       return returnsMany;
285     }
286 
287     public boolean returnsMap() {
288       return returnsMap;
289     }
290 
291     public boolean returnsVoid() {
292       return returnsVoid;
293     }
294 
295     public boolean returnsCursor() {
296       return returnsCursor;
297     }
298 
299     private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
300       Integer index = null;
301       final Class<?>[] argTypes = method.getParameterTypes();
302       for (int i = 0; i < argTypes.length; i++) {
303         if (paramType.isAssignableFrom(argTypes[i])) {
304           if (index == null) {
305             index = i;
306           } else {
307             throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
308           }
309         }
310       }
311       return index;
312     }
313 
314     private String getMapKey(Method method) {
315       String mapKey = null;
316       if (Map.class.isAssignableFrom(method.getReturnType())) {
317         final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
318         if (mapKeyAnnotation != null) {
319           mapKey = mapKeyAnnotation.value();
320         }
321       }
322       return mapKey;
323     }
324   }
複製代碼
相關文章
相關標籤/搜索