binding 模块主要为了解决一个历史遗留问题,原先查询一个 VO 对象 时需要调用 SqlSession.queryForObject(“selectXXVOById”, primaryKey)方法,执行指定的 sql 语句,第一个参数 selectXXVOById 指定了执行的 sql 语句 id,如果我们不小心写错了参数,Mybatis 是无法在初始化时发现这个错误的,只会在实际调用 queryForObject(“selectXXVOById”, primaryKey)方法 时才会抛出异常,这对于工程师来说是非常难受的,就像泛型出来之前,很多类型转换不会在编译期发现错误一样。而 binding 模块 就像 Java 的泛型机制 一样,将程序的错误提前暴露出来,为开发人员省去不少排查问题的精力。

binding 模块 的解决方案是,定义一个 Mapper 接口,在接口中定义 sql 语句 对应的 方法名 Id 及 参数,这些方法在 Mybatis 的初始化过程中,会与该 Mapper 接口 对应的映射配置文件中的 sql 语句 相关联,如果存在无法关联的 sql 语句,Mybatis 就会抛出异常,帮助我们及时发现问题。示例代码如下:

  1. public interface HeroMapper {
  2. // 映射文件中会存在一个 <select>节点,id 为 “selectHeroVOById”
  3. public HeroVO selectHeroVOById(int id);
  4. }
  5. // 首先,获取 HeroMapper 对应的代理对象
  6. HeroMapper heroMapper = session.getMapper(HeroMapper.class);
  7. // 直接调用 HeroMapper接口 中的方法,获取结果集
  8. HeroVO heroVO = heroMapper.selectHeroVOById("23333");

1 MapperRegistry 和 MapperProxyFactory

MapperRegistry 是 Mapper 接口 及其对应的代理对象工厂的注册中心。Configuration 是 Mybatis 中全局性的配置对象,根据 Mybatis 的核心配置文件 mybatis-config.xml 解析而成。Configuration 通过 mapperRegistry 属性 持有该对象。

Mybatis 在初始化过程中会读取映射配置文件和 Mapper 接口 中的注解信息,并调用 MapperRegistry 的 addMappers()方法 填充 knownMappers 集合,在需要执行某 sql 语句 时,会先调用 getMapper()方法 获取实现了 Mapper 接口 的动态代理对象。

  1. public class MapperRegistry {
  2. // Mybatis 全局唯一的配置对象,包含了几乎所有配置信息
  3. private final Configuration config;
  4. // key:Mapper接口,value:MapperProxyFactory 为 Mapper接口 创建代理对象的工厂
  5. private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
  6. // 下面的两个重载方法 通过扫描指定的包目录,获取所有的 Mapper接口
  7. public void addMappers(String packageName) {
  8. addMappers(packageName, Object.class);
  9. }
  10. public void addMappers(String packageName, Class<?> superType) {
  11. ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  12. resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  13. Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  14. for (Class<?> mapperClass : mapperSet) {
  15. addMapper(mapperClass);
  16. }
  17. }
  18. public <T> void addMapper(Class<T> type) {
  19. // 该 type 是不是接口
  20. if (type.isInterface()) {
  21. // 是否已经加载过
  22. if (hasMapper(type)) {
  23. throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
  24. }
  25. boolean loadCompleted = false;
  26. try {
  27. // 将 Mapper接口 的 Class对象 和 对应的 MapperProxyFactory对象 添加到 knownMappers集合
  28. knownMappers.put(type, new MapperProxyFactory<>(type));
  29. // XML 解析和注解的处理
  30. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  31. parser.parse();
  32. loadCompleted = true;
  33. } finally {
  34. if (!loadCompleted) {
  35. knownMappers.remove(type);
  36. }
  37. }
  38. }
  39. }
  40. @SuppressWarnings("unchecked")
  41. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  42. // 获取 type 对应的 MapperProxyFactory对象
  43. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  44. if (mapperProxyFactory == null) {
  45. throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  46. }
  47. try {
  48. // 根据 sqlSession 创建 type接口 的代理对象
  49. return mapperProxyFactory.newInstance(sqlSession);
  50. } catch (Exception e) {
  51. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  52. }
  53. }
  54. // 获取所有的 MapperProxyFactory
  55. public Collection<Class<?>> getMappers() {
  56. return Collections.unmodifiableCollection(knownMappers.keySet());
  57. }
  58. // 初始化的时候会持有 Configuration对象
  59. public MapperRegistry(Configuration config) {
  60. this.config = config;
  61. }
  62. // 是否存在指定的 MapperProxyFactory
  63. public <T> boolean hasMapper(Class<T> type) {
  64. return knownMappers.containsKey(type);
  65. }
  66. }

MapperProxyFactory 主要负责创建代理对象。

  1. public class MapperProxyFactory<T> {
  2. // 要创建的动态代理对象 所实现的接口
  3. private final Class<T> mapperInterface;
  4. // 缓存 mapperInterface接口 中 Method对象 和其对应的 MapperMethod对象
  5. private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
  6. // 初始化时为 mapperInterface 注入值
  7. public MapperProxyFactory(Class<T> mapperInterface) {
  8. this.mapperInterface = mapperInterface;
  9. }
  10. public Class<T> getMapperInterface() {
  11. return mapperInterface;
  12. }
  13. public Map<Method, MapperMethod> getMethodCache() {
  14. return methodCache;
  15. }
  16. public T newInstance(SqlSession sqlSession) {
  17. // 每次都会创建一个新的 MapperProxy对象
  18. final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  19. return newInstance(mapperProxy);
  20. }
  21. /**
  22. * 非常眼熟的 JDK动态代理 代码,创建了实现 mapperInterface接口 的代理对象
  23. * 根据国际惯例,mapperProxy对应的类 肯定实现了 InvocationHandler接口,
  24. * 为 mapperInterface接口方法的调用 织入统一处理逻辑
  25. */
  26. protected T newInstance(MapperProxy<T> mapperProxy) {
  27. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  28. }
  29. }

2 MapperProxy

MapperProxy 实现了 InvocationHandler 接口,为 Mapper 接口 的方法调用织入了统一处理。

  1. public class MapperProxy<T> implements InvocationHandler, Serializable {
  2. private static final long serialVersionUID = -6424540398559729838L;
  3. // 记录关联的 sqlSession对象
  4. private final SqlSession sqlSession;
  5. // 对应的 Mapper接口 的 Class对象
  6. private final Class<T> mapperInterface;
  7. // 用于缓存 MapperMethod对象,key:Mapper接口 中方法对应的 Method对象,
  8. // value:MapperMethod对象(该对象会完成参数转换 及 sql语句 的执行功能)
  9. private final Map<Method, MapperMethod> methodCache;
  10. public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
  11. this.sqlSession = sqlSession;
  12. this.mapperInterface = mapperInterface;
  13. this.methodCache = methodCache;
  14. }
  15. // 为被代理对象的方法 织入统一处理
  16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  17. try {
  18. // 如果目标方法继承自 Object,则直接调用目标方法
  19. if (Object.class.equals(method.getDeclaringClass())) {
  20. return method.invoke(this, args);
  21. } else if (method.isDefault()) {
  22. return invokeDefaultMethod(proxy, method, args);
  23. }
  24. } catch (Throwable t) {
  25. throw ExceptionUtil.unwrapThrowable(t);
  26. }
  27. // 从缓存中获取 mapperMethod对象,如果没有就创建新的
  28. final MapperMethod mapperMethod = cachedMapperMethod(method);
  29. // 执行 sql语句,返回结果集
  30. return mapperMethod.execute(sqlSession, args);
  31. }
  32. // 主要负责维护 methodCache 缓存
  33. private MapperMethod cachedMapperMethod(Method method) {
  34. // 这里用到了 Java8 的新特性,computeIfAbsent() 是 Java8 新增的方法,Lambda表达式 也是 Java8中 最重要的新特性之一
  35. // computeIfAbsent()方法 表示 当前map中,若 key 对应的 value 为空,则执行传入的 Lambda表达式,将 key 和表达式的 value
  36. // 存入 当前map,并返回 value值
  37. // 在这段代码中的意思是:若 methodCache 中没有 method 对应的 value,就执行右侧的 Lambda表达式,并将表达式的结果
  38. // 存入 methodCache 并返回
  39. return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  40. }
  41. }

3 MapperMethod

MapperMethod 中封装了 Mapper 接口 中对应方法的信息,和对应 sql 语句 的信息,是连接 Mapper 接口 及映射配置文件中定义的 sql 语句 的桥梁。

MapperMethod 中持有两个非常重要的属性,这两个属性对应的类 都是 MapperMethod 中的静态内部类。另外,MapperMethod 在被实例化时就对这两个属性进行了初始化。

  1. public class MapperMethod {
  2. /** 下面这俩货都是内部类,而且还是 public static 的 */
  3. private final SqlCommand command;
  4. private final MethodSignature method;
  5. public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
  6. this.command = new SqlCommand(config, mapperInterface, method);
  7. this.method = new MethodSignature(config, mapperInterface, method);
  8. }
  9. }

MapperMethod 中的核心方法 execute() 就主要用到了这两个类,所以我们先看一下 SqlCommand 和 MethodSignature 的源码。

3.1 SqlCommand

  1. public static class SqlCommand {
  2. // sql语句的id
  3. private final String name;
  4. // sql语句的类型,SqlCommandType 是枚举类型,持有常用的 增、删、改、查等操作类型
  5. private final SqlCommandType type;
  6. public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
  7. // 方法名
  8. final String methodName = method.getName();
  9. // 该方法对应的类的 Class对象
  10. final Class<?> declaringClass = method.getDeclaringClass();
  11. // MappedStatement 封装了 sql语句 相关的信息,在 Mybatis初始化 时创建
  12. MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
  13. if (ms == null) {
  14. // 处理 Flush注解
  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): "
  20. + mapperInterface.getName() + "." + methodName);
  21. }
  22. } else {
  23. // 初始化 name 和 type
  24. name = ms.getId();
  25. type = ms.getSqlCommandType();
  26. if (type == SqlCommandType.UNKNOWN) {
  27. throw new BindingException("Unknown execution method for: " + name);
  28. }
  29. }
  30. }
  31. private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
  32. Class<?> declaringClass, Configuration configuration) {
  33. // sql语句 的名称默认是由 Mapper接口方法 的 包名.类名.方法名
  34. String statementId = mapperInterface.getName() + "." + methodName;
  35. // 检测是否有该名称的 sql语句
  36. if (configuration.hasStatement(statementId)) {
  37. // 从 configuration 的 mappedStatements容器 中获取 statementId 对应的 MappedStatement对象
  38. return configuration.getMappedStatement(statementId);
  39. // 如果此方法不是 mapperInterface接口 定义的,则返回空
  40. } else if (mapperInterface.equals(declaringClass)) {
  41. return null;
  42. }
  43. // 对 mapperInterface 的父接口 进行递归处理
  44. for (Class<?> superInterface : mapperInterface.getInterfaces()) {
  45. if (declaringClass.isAssignableFrom(superInterface)) {
  46. MappedStatement ms = resolveMappedStatement(superInterface, methodName,
  47. declaringClass, configuration);
  48. if (ms != null) {
  49. return ms;
  50. }
  51. }
  52. }
  53. return null;
  54. }
  55. public String getName() {
  56. return name;
  57. }
  58. public SqlCommandType getType() {
  59. return type;
  60. }
  61. }

3.2 MethodSignature

  1. public static class MethodSignature {
  2. // 返回值类型是否为 集合 或 数组
  3. private final boolean returnsMany;
  4. // 返回值类型是否为 Map类型
  5. private final boolean returnsMap;
  6. // 返回值类型是否为 void
  7. private final boolean returnsVoid;
  8. // 返回值类型是否为 Cursor
  9. private final boolean returnsCursor;
  10. // 返回值类型是否为 Optional
  11. private final boolean returnsOptional;
  12. // 返回值类型的 Class对象
  13. private final Class<?> returnType;
  14. // 如果返回值类型为 Map,则用该字段记录了作为 key 的列名
  15. private final String mapKey;
  16. // 标记该方法参数列表中 ResultHandler类型参数 的位置
  17. private final Integer resultHandlerIndex;
  18. // 标记该方法参数列表中 RowBounds类型参数 的位置
  19. private final Integer rowBoundsIndex;
  20. /**
  21. * 顾名思义,这是一个处理 Mapper接口 中 方法参数列表的解析器,它使用了一个 SortedMap<Integer, String>
  22. * 类型的容器,记录了参数在参数列表中的位置索引 与 参数名之间的对应关系,key参数 在参数列表中的索引位置,
  23. * value参数名(参数名可用@Param注解指定,默认使用参数索引作为其名称)
  24. */
  25. private final ParamNameResolver paramNameResolver;
  26. /**
  27. * MethodSignature 的构造方法会解析对应的 method,并初始化上述字段
  28. */
  29. public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
  30. // 获取 method方法 的返回值类型
  31. Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
  32. if (resolvedReturnType instanceof Class<?>) {
  33. this.returnType = (Class<?>) resolvedReturnType;
  34. } else if (resolvedReturnType instanceof ParameterizedType) {
  35. this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
  36. } else {
  37. this.returnType = method.getReturnType();
  38. }
  39. // 对 MethodSignature 持有的各属性 进行初始化
  40. this.returnsVoid = void.class.equals(this.returnType);
  41. this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
  42. this.returnsCursor = Cursor.class.equals(this.returnType);
  43. this.returnsOptional = Optional.class.equals(this.returnType);
  44. this.mapKey = getMapKey(method);
  45. this.returnsMap = this.mapKey != null;
  46. this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
  47. this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
  48. this.paramNameResolver = new ParamNameResolver(configuration, method);
  49. }
  50. /**
  51. * 查找指定类型的参数在参数列表中的位置,要查找的参数类型在参数列表中必须是唯一的
  52. * 如果参数列表中存在多个 要查找的参数类型,则会抛出异常
  53. */
  54. private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
  55. Integer index = null;
  56. final Class<?>[] argTypes = method.getParameterTypes();
  57. for (int i = 0; i < argTypes.length; i++) {
  58. if (paramType.isAssignableFrom(argTypes[i])) {
  59. if (index == null) {
  60. index = i;
  61. } else {
  62. throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
  63. }
  64. }
  65. }
  66. return index;
  67. }
  68. }

3.3 execute()方法

execute()方法 会根据 sql 语句 的类型(CRUD)调用 SqlSession 对应的方法完成数据库操作,SqlSession 是 Mybatis 的核心组件之一,后面会详细解读。

  1. public class MapperMethod {
  2. public Object execute(SqlSession sqlSession, Object[] args) {
  3. Object result;
  4. // 根据 sql语句 的类型 调用 sqlSession 对应的方法
  5. switch (command.getType()) {
  6. case INSERT: {
  7. // 使用 ParamNameResolver 处理 args实参列表,将用户传入的实参与
  8. // 指定参数名称关联起来
  9. Object param = method.convertArgsToSqlCommandParam(args);
  10. // 获取返回结果,rowCountResult()方法 会根据 method属性 中的 returnType,
  11. // 对结果的类型进行转换
  12. result = rowCountResult(sqlSession.insert(command.getName(), param));
  13. break;
  14. }
  15. case UPDATE: {
  16. Object param = method.convertArgsToSqlCommandParam(args);
  17. result = rowCountResult(sqlSession.update(command.getName(), param));
  18. break;
  19. }
  20. case DELETE: {
  21. Object param = method.convertArgsToSqlCommandParam(args);
  22. result = rowCountResult(sqlSession.delete(command.getName(), param));
  23. break;
  24. }
  25. case SELECT:
  26. // 处理返回值为 void 且 ResultSet 通过 ResultHandler 处理的方法
  27. if (method.returnsVoid() && method.hasResultHandler()) {
  28. executeWithResultHandler(sqlSession, args);
  29. result = null;
  30. // 处理返回值为集合 或 数组的方法
  31. } else if (method.returnsMany()) {
  32. result = executeForMany(sqlSession, args);
  33. // 处理返回值为 Map 的方法
  34. } else if (method.returnsMap()) {
  35. result = executeForMap(sqlSession, args);
  36. // 处理返回值为 Cursor 的方法
  37. } else if (method.returnsCursor()) {
  38. result = executeForCursor(sqlSession, args);
  39. } else {
  40. // 处理返回值为单一对象的方法
  41. Object param = method.convertArgsToSqlCommandParam(args);
  42. result = sqlSession.selectOne(command.getName(), param);
  43. // 处理返回值为 Optional 的方法
  44. if (method.returnsOptional()
  45. && (result == null || !method.getReturnType().equals(result.getClass()))) {
  46. result = Optional.ofNullable(result);
  47. }
  48. }
  49. break;
  50. case FLUSH:
  51. result = sqlSession.flushStatements();
  52. break;
  53. default:
  54. throw new BindingException("Unknown execution method for: " + command.getName());
  55. }
  56. if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  57. throw new BindingException("Mapper method '" + command.getName()
  58. + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  59. }
  60. return result;
  61. }
  62. /**
  63. * 当执行 insert、update、delete 类型的 sql语句 时,其执行结果都要经过本方法处理
  64. */
  65. private Object rowCountResult(int rowCount) {
  66. final Object result;
  67. // 方法的返回值为 void 时
  68. if (method.returnsVoid()) {
  69. result = null;
  70. // 方法的返回值为 Integer 时
  71. } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
  72. result = rowCount;
  73. // 方法的返回值为 Long 时
  74. } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
  75. result = (long)rowCount;
  76. // 方法的返回值为 Boolean 时
  77. } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
  78. result = rowCount > 0;
  79. } else {
  80. throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
  81. }
  82. return result;
  83. }
  84. /**
  85. * 如果 Mapper接口 中定义的方法准备使用 ResultHandler 处理查询结果集,则通过此方法处理
  86. */
  87. private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
  88. // 获取 sql语句 对应的 MappedStatement对象,该对象中记录了 sql语句 相关信息
  89. MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
  90. // 当使用 ResultHandler 处理结果集时,必须指定 ResultMap 或 ResultType
  91. if (!StatementType.CALLABLE.equals(ms.getStatementType())
  92. && void.class.equals(ms.getResultMaps().get(0).getType())) {
  93. throw new BindingException("method " + command.getName()
  94. + " needs either a @ResultMap annotation, a @ResultType annotation,"
  95. + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
  96. }
  97. // 转换实参列表
  98. Object param = method.convertArgsToSqlCommandParam(args);
  99. // 如果实参列表中有 RowBounds类型参数
  100. if (method.hasRowBounds()) {
  101. // 从 args参数列表 中获取 RowBounds对象
  102. RowBounds rowBounds = method.extractRowBounds(args);
  103. // 执行查询,并用指定的 ResultHandler 处理结果对象
  104. sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
  105. } else {
  106. sqlSession.select(command.getName(), param, method.extractResultHandler(args));
  107. }
  108. }
  109. /**
  110. * 如果 Mapper接口 中对应方法的返回值为集合(Collection接口实现类) 或 数组,
  111. * 则调用本方法将结果集处理成 相应的集合或数组
  112. */
  113. private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
  114. List<E> result;
  115. // 参数列表转换
  116. Object param = method.convertArgsToSqlCommandParam(args);
  117. // 参数列表中是否有 RowBounds类型的参数
  118. if (method.hasRowBounds()) {
  119. RowBounds rowBounds = method.extractRowBounds(args);
  120. // 这里使用了 selectList()方法 进行查询,所以返回的结果集就是 List类型的
  121. result = sqlSession.selectList(command.getName(), param, rowBounds);
  122. } else {
  123. result = sqlSession.selectList(command.getName(), param);
  124. }
  125. // 将结果集转换为数组或 Collection集合
  126. if (!method.getReturnType().isAssignableFrom(result.getClass())) {
  127. if (method.getReturnType().isArray()) {
  128. return convertToArray(result);
  129. } else {
  130. return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
  131. }
  132. }
  133. return result;
  134. }
  135. /**
  136. * 将结果集转换成 Collection集合
  137. */
  138. private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
  139. // 使用前面介绍的 ObjectFactory,通过反射方式创建集合对象
  140. Object collection = config.getObjectFactory().create(method.getReturnType());
  141. MetaObject metaObject = config.newMetaObject(collection);
  142. // 实际上就是调用了 Collection 的 addAll()方法
  143. metaObject.addAll(list);
  144. return collection;
  145. }
  146. /**
  147. * 本方法和上面的 convertToDeclaredCollection()功能 类似,主要负责将结果对象转换成数组
  148. */
  149. @SuppressWarnings("unchecked")
  150. private <E> Object convertToArray(List<E> list) {
  151. // 获取数组中元素的 类型Class
  152. Class<?> arrayComponentType = method.getReturnType().getComponentType();
  153. // 根据元素类型 和 元素数量 初始化数组
  154. Object array = Array.newInstance(arrayComponentType, list.size());
  155. // 将 List 转换成数组
  156. if (arrayComponentType.isPrimitive()) {
  157. for (int i = 0; i < list.size(); i++) {
  158. Array.set(array, i, list.get(i));
  159. }
  160. return array;
  161. } else {
  162. return list.toArray((E[])array);
  163. }
  164. }
  165. /**
  166. * 如果 Mapper接口 中对应方法的返回值为类型为 Map,则调用此方法执行 sql语句
  167. */
  168. private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
  169. Map<K, V> result;
  170. // 转换实参列表
  171. Object param = method.convertArgsToSqlCommandParam(args);
  172. if (method.hasRowBounds()) {
  173. RowBounds rowBounds = method.extractRowBounds(args);
  174. // 注意这里调用的是 SqlSession 的 selectMap()方法,返回的是一个 Map类型结果集
  175. result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
  176. } else {
  177. result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
  178. }
  179. return result;
  180. }
  181. /**
  182. * 本方法与上面的 executeForMap()方法 类似,只不过 sqlSession 调用的是 selectCursor()
  183. */
  184. private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
  185. Cursor<T> result;
  186. Object param = method.convertArgsToSqlCommandParam(args);
  187. if (method.hasRowBounds()) {
  188. RowBounds rowBounds = method.extractRowBounds(args);
  189. result = sqlSession.selectCursor(command.getName(), param, rowBounds);
  190. } else {
  191. result = sqlSession.selectCursor(command.getName(), param);
  192. }
  193. return result;
  194. }
  195. }