mybatis原理快速上手插件--易学易用

一、mybatis运行过程分两步进行:

    1.读取配置文件缓存到Configuration对象,用来创建SqlSessionFactory对象

    2.SqlSession的执行过程。

SqlSessionFactory主要是提供创建核心接口SqlSession。(mybatis采用Builder模式创建SQLSessionFactory)

 

二、创建SqlSessionFactory过程:

    a.  XMLConfigBuilder类解析配置xml文件,Configuration类对象存入解析后的数据(Configuration采用单例模式)

    b.  使用Configuration创建SqlSessionFactory(接口,其默认实现类为DefaultSqlSessionFactory)

SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);

三、映射器

    插件和映射器之间有着频繁地交互!映射器内部组成部分:MappedStatement、SqlSource、BoundSql。

    a.MappedStatement:保存映射器节点内容(配置映射的sql文件)、查看源码可知其中SqlSource为该类的一个属性

    

public final class MappedStatement {

  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  //SQL源码
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
.....}

    b.SqlSource(接口):提供BoundSql对象。SqlSource根据上下文和参数解析生成需要的SQL,

/**
 * SQL源码
 * 
 */
public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

    有多个重要实现类,其中一个实现类DynamicSqlSource源码如下:

/**
 * 动态SQL源码
 * 
 */
public class DynamicSqlSource implements SqlSource {

  private Configuration configuration;
  private SqlNode rootSqlNode;

  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }

  //得到绑定的SQL
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    //生成一个动态上下文
    DynamicContext context = new DynamicContext(configuration, parameterObject);
	//这里SqlNode.apply只是将${}这种参数替换掉,并没有替换#{}这种参数
    rootSqlNode.apply(context);
	//调用SqlSourceBuilder
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
	//SqlSourceBuilder.parse,注意这里返回的是StaticSqlSource,解析完了就把那些参数都替换成?了,也就是最基本的JDBC的SQL写法
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
	//看似是又去递归调用SqlSource.getBoundSql,其实因为是StaticSqlSource,所以没问题,不是递归调用
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

}

c.BoundSql:绑定的SQL,是从SqlSource而来的结果。即SqlSource通过对Sql和参数解析后得到的SQL和参数,将动态内容都处理完成得到的SQL语句字符串,其中包括?,还有绑定的参数。(注意:通常插件就是在此处进行修改!)

BounSql有三个重要的属性:parameterMappings、parameterObject、sql。

public class BoundSql {

  private String sql;//sql语句
  private List<ParameterMapping> parameterMappings;//参数映射集
  private Object parameterObject;//参数对象
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;

  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<String, Object>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }

  public String getSql() {
    return sql;
  }

  public List<ParameterMapping> getParameterMappings() {
    return parameterMappings;
  }

  public Object getParameterObject() {
    return parameterObject;
  }

  public boolean hasAdditionalParameter(String name) {
    return metaParameters.hasGetter(name);
  }

  public void setAdditionalParameter(String name, Object value) {
    metaParameters.setValue(name, value);
  }

  public Object getAdditionalParameter(String name) {
    return metaParameters.getValue(name);
  }
}

    <1>sql属性:在映射器里被SqlSource解析后的SQL,可根据需要进行改写(如实现分页)

    <2>parameterMappings:它是一个List,其中的对象都是ParameterMapping对象。对象中有关于参数的描述:

/**
 * 参数映射
 * 
 */
public class ParameterMapping {

  private Configuration configuration;
  //property
  private String property;
  //mode
  private ParameterMode mode;
  //javaType=int
  private Class<?> javaType = Object.class;
  //jdbcType=NUMERIC
  private JdbcType jdbcType;
  //numericScale
  private Integer numericScale;
  private TypeHandler<?> typeHandler;
  private String resultMapId;
  //jdbcType=NUMERIC
  private String jdbcTypeName;
  private String expression;

  private ParameterMapping() {
  }......}

通过它就可以将SQL与参数结合。

<3>parameterObject属性:可以

        传递简单对象(int、String、....)、

        传递POJO或者Map、

       传递多个参数(把parameterObject变成Map<String,Object>)、

        使用@Param注解(把parameterObject变成Map<String,Object>对象),例如@Param("name") String name。

在插件中分离这些参数时:可以通过instanceof来进行判断,如

                     //获取分页参数
		    Page page=new Page();
		    Object paramObject=boundSql.getParameterObject();
		    if(paramObject instanceof Page)
		    {
		    	page=(Page)paramObject;
		    }
		    else if(paramObject instanceof Map)
		    {
		    	System.out.println("参数对象为Map类型");
		    	HashMap<String,Object> map=(HashMap<String, Object>) paramObject;
		    	Set<String> set=map.keySet();
		    	Iterator<String> iterator=set.iterator();
		    	while(iterator.hasNext())
		    	{
		    		String key=iterator.next();
		    		Object object=map.get(key);
		    		if(object instanceof Page)
		    		{
		    			page=(Page)object;
		    		}
		    	}
		    	
		    }

四、SqlSession

    前面对底层原理的各种穿插补空的,现在再来了解SQLSession(接口),查看源码:

/**
 * 这是MyBatis主要的一个类,用来执行SQL,获取映射器,管理事务
 * 通常情况下,我们在应用程序中使用的Mybatis的API就是这个接口定义的方法。
 *
 */
public interface SqlSession extends Closeable {

  /**
   * Retrieve a single row mapped from the statement key
   * 根据指定的SqlID获取一条记录的封装对象
   * @param <T> the returned object type 封装之后的对象类型
   * @param statement sqlID
   * @return Mapped object 封装之后的对象
   */
  <T> T selectOne(String statement);

  /**
   * Retrieve a single row mapped from the statement key and parameter.
   * 根据指定的SqlID获取一条记录的封装对象,只不过这个方法容许我们可以给sql传递一些参数
   * 一般在实际使用中,这个参数传递的是pojo,或者Map或者ImmutableMap
   * @param <T> the returned object type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @return Mapped object
   */
  <T> T selectOne(String statement, Object parameter);

  /**
   * Retrieve a list of mapped objects from the statement key and parameter.
   * 根据指定的sqlId获取多条记录
   * @param <E> the returned list element type
   * @param statement Unique identifier matching the statement to use.
   * @return List of mapped object
   */
  <E> List<E> selectList(String statement);

  /**
   * Retrieve a list of mapped objects from the statement key and parameter.
   * 获取多条记录,这个方法容许我们可以传递一些参数
   * @param <E> the returned list element type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @return List of mapped object
   */
  <E> List<E> selectList(String statement, Object parameter);

  /**
   * Retrieve a list of mapped objects from the statement key and parameter,
   * within the specified row bounds.
   * 获取多条记录,这个方法容许我们可以传递一些参数,不过这个方法容许我们进行
   * 分页查询。
   *
   * 需要注意的是默认情况下,Mybatis为了扩展性,仅仅支持内存分页。也就是会先把
   * 所有的数据查询出来以后,然后在内存中进行分页。因此在实际的情况中,需要注意
   * 这一点。
   *
   * 一般情况下公司都会编写自己的Mybatis 物理分页插件
   * @param <E> the returned list element type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param rowBounds  Bounds to limit object retrieval
   * @return List of mapped object
   */
  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

  /**
   * The selectMap is a special case in that it is designed to convert a list
   * of results into a Map based on one of the properties in the resulting
   * objects.
   * Eg. Return a of Map[Integer,Author] for selectMap("selectAuthors","id")
   * 将查询到的结果列表转换为Map类型。
   * @param <K> the returned Map keys type
   * @param <V> the returned Map values type
   * @param statement Unique identifier matching the statement to use.
   * @param mapKey The property to use as key for each value in the list. 这个参数会作为结果map的key
   * @return Map containing key pair data.
   */
  <K, V> Map<K, V> selectMap(String statement, String mapKey);

  /**
   * The selectMap is a special case in that it is designed to convert a list
   * of results into a Map based on one of the properties in the resulting
   * objects.
   * 将查询到的结果列表转换为Map类型。这个方法容许我们传入需要的参数
   * @param <K> the returned Map keys type
   * @param <V> the returned Map values type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param mapKey The property to use as key for each value in the list.
   * @return Map containing key pair data.
   */
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

  /**
   * The selectMap is a special case in that it is designed to convert a list
   * of results into a Map based on one of the properties in the resulting
   * objects.
   * 获取多条记录,加上分页,并存入Map
   * @param <K> the returned Map keys type
   * @param <V> the returned Map values type
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param mapKey The property to use as key for each value in the list.
   * @param rowBounds  Bounds to limit object retrieval
   * @return Map containing key pair data.
   */
  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

  /**
   * Retrieve a single row mapped from the statement key and parameter
   * using a {@code ResultHandler}.
   *
   * @param statement Unique identifier matching the statement to use.
   * @param parameter A parameter object to pass to the statement.
   * @param handler ResultHandler that will handle each retrieved row
   * @return Mapped object
   */
  void select(String statement, Object parameter, ResultHandler handler);

  /**
   * Retrieve a single row mapped from the statement
   * using a {@code ResultHandler}.
   * 获取一条记录,并转交给ResultHandler处理。这个方法容许我们自己定义对
   * 查询到的行的处理方式。不过一般用的并不是很多
   * @param statement Unique identifier matching the statement to use.
   * @param handler ResultHandler that will handle each retrieved row
   * @return Mapped object
   */
  void select(String statement, ResultHandler handler);

  /**
   * Retrieve a single row mapped from the statement key and parameter
   * using a {@code ResultHandler} and {@code RowBounds}
   * 获取一条记录,加上分页,并转交给ResultHandler处理
   * @param statement Unique identifier matching the statement to use.
   * @param rowBounds RowBound instance to limit the query results
   * @param handler ResultHandler that will handle each retrieved row
   * @return Mapped object
   */
  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

  /**
   * Execute an insert statement.
   * 插入记录。一般情况下这个语句在实际项目中用的并不是太多,而且更多使用带参数的insert函数
   * @param statement Unique identifier matching the statement to execute.
   * @return int The number of rows affected by the insert.
   */
  int insert(String statement);

  /**
   * Execute an insert statement with the given parameter object. Any generated
   * autoincrement values or selectKey entries will modify the given parameter
   * object properties. Only the number of rows affected will be returned.
   * 插入记录,容许传入参数。
   * @param statement Unique identifier matching the statement to execute.
   * @param parameter A parameter object to pass to the statement.
   * @return int The number of rows affected by the insert. 注意返回的是受影响的行数
   */
  int insert(String statement, Object parameter);

  /**
   * Execute an update statement. The number of rows affected will be returned.
   * 更新记录。返回的是受影响的行数
   * @param statement Unique identifier matching the statement to execute.
   * @return int The number of rows affected by the update.
   */
  int update(String statement);

  /**
   * Execute an update statement. The number of rows affected will be returned.
   * 更新记录
   * @param statement Unique identifier matching the statement to execute.
   * @param parameter A parameter object to pass to the statement.
   * @return int The number of rows affected by the update. 返回的是受影响的行数
   */
  int update(String statement, Object parameter);

  /**
   * Execute a delete statement. The number of rows affected will be returned.
   * 删除记录
   * @param statement Unique identifier matching the statement to execute.
   * @return int The number of rows affected by the delete. 返回的是受影响的行数
   */
  int delete(String statement);

  /**
   * Execute a delete statement. The number of rows affected will be returned.
   * 删除记录
   * @param statement Unique identifier matching the statement to execute.
   * @param parameter A parameter object to pass to the statement.
   * @return int The number of rows affected by the delete. 返回的是受影响的行数
   */
  int delete(String statement, Object parameter);

  //以下是事务控制方法,commit,rollback
  /**
   * Flushes batch statements and commits database connection.
   * Note that database connection will not be committed if no updates/deletes/inserts were called.
   * To force the commit call {@link SqlSession#commit(boolean)}
   */
  void commit();

  /**
   * Flushes batch statements and commits database connection.
   * @param force forces connection commit
   */
  void commit(boolean force);

  /**
   * Discards pending batch statements and rolls database connection back.
   * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
   * To force the rollback call {@link SqlSession#rollback(boolean)}
   */
  void rollback();

  /**
   * Discards pending batch statements and rolls database connection back.
   * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
   * @param force forces connection rollback
   */
  void rollback(boolean force);

  /**
   * Flushes batch statements.
   * 刷新批处理语句,返回批处理结果
   * @return BatchResult list of updated records
   * @since 3.0.6
   */
  List<BatchResult> flushStatements();

  /**
   * Closes the session
   * 关闭Session
   */
  @Override
  void close();

  /**
   * Clears local session cache
   * 清理Session缓存
   */
  void clearCache();

  /**
   * Retrieves current configuration
   * 得到配置
   * @return Configuration
   */
  Configuration getConfiguration();

  /**
   * Retrieves a mapper.
   * 得到映射器
   * 这个巧妙的使用了泛型,使得类型安全
   * 到了MyBatis 3,还可以用注解,这样xml都不用写了
   * @param <T> the mapper type
   * @param type Mapper interface class
   * @return a mapper bound to this SqlSession
   */
  <T> T getMapper(Class<T> type);

  /**
   * Retrieves inner database connection
   * 得到数据库连接
   * @return Connection
   */
  Connection getConnection();
}

在新版Mybatis中推荐使用Mapper,但是理解Mybatis底层核心知识却是必要的。

五、映射器(Mapper)动态代理

    上面SqlSession的方法getMapper来得到映射器,SqlSession有个默认实现类DefaultSqlSession,查看源码实现方法

public class DefaultSqlSession implements SqlSession {

  private Configuration configuration;
  private Executor executor;
...........  @Override
  public <T> T getMapper(Class<T> type) {
    //最后会去调用MapperRegistry.getMapper
    return configuration.<T>getMapper(type, this);
  }
.....

}

然后调用的是Configuration中的getMapper方法,查看源码:

//将包下所有类加入到mapper
  public void addMappers(String packageName, Class<?> superType) {
    mapperRegistry.addMappers(packageName, superType);
  }

  public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }

  public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }
   //追踪getMapper
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

  public boolean hasMapper(Class<?> type) {
    return mapperRegistry.hasMapper(type);
  }

再调用MapperRegistry的方法,在Configuration中的存在 MapperRegistry的属性:

 //映射注册机
  protected MapperRegistry mapperRegistry = new MapperRegistry(this);

继续追踪:

/**
 * 映射器注册机
 *
 */
public class MapperRegistry {

  private Configuration config;
  //将已经添加的映射都放入HashMap
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  //返回代理类
  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);
    }
  }
...}

启动一个MapperProxyFactory工厂生成带来实例:

/**
 * 映射器代理工厂
 */
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }
    
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    //用JDK自带的动态代理生成映射器
    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);
  }

}

Mapper映射通过动态代理实现,代理的方法放到了MapperProxy类中:

/**
 * 映射器代理,代理模式
 *
 */
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 {
    //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
    //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
    if (Object.class.equals(method.getDeclaringClass())) {
      try {//Mapper是一个接口不是类,会判断失败
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //这里优化了,去缓存中找MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行
    return mapperMethod.execute(sqlSession, args);
  }

  //去缓存中找MapperMethod
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //找不到才去new
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

现在走到了MapperMethod:

/**
 * 映射器方法
 *
 */
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, method);
  }

  //执行
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        //如果有结果处理器
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        //如果结果有多条记录
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        //如果结果是map
        result = executeForMap(sqlSession, args);
      } else {
        //否则就是一条记录
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      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())) {
      //如果返回值是大int或小int
      result = Integer.valueOf(rowCount);
    } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
      //如果返回值是大long或小long
      result = Long.valueOf(rowCount);
    } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
      //如果返回值是大boolean或小boolean
      result = Boolean.valueOf(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);
    //代入RowBounds
    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 <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;
  }

  //参数map,静态内部类,更严格的get方法,如果没有相应的key,报错
  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);
    }

  }

  //SQL命令,静态内部类
  public static class SqlCommand {

    private final String name;
    private final SqlCommandType type;

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      String statementName = mapperInterface.getName() + "." + method.getName();
      MappedStatement ms = null;
      if (configuration.hasStatement(statementName)) {
        ms = configuration.getMappedStatement(statementName);
      } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
        //如果不是这个mapper接口的方法,再去查父类
        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);
      }
      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;
    }
  }

  //方法签名,静态内部类
  public static class MethodSignature {

    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final SortedMap<Integer, String> params;
    private final boolean hasNamedParameters;

    public MethodSignature(Configuration configuration, Method method) {
      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);
      //以下重复循环2遍调用getUniqueParamIndex,是不是降低效率了
      //记下RowBounds是第几个参数
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      //记下ResultHandler是第几个参数
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
    }

    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().intValue()];
      } else {
        //否则,返回一个ParamMap,修改参数名,参数名就是其位置
        final Map<String, Object> param = new ParamMap<Object>();
        int i = 0;
        for (Map.Entry<Integer, String> entry : params.entrySet()) {
          //1.先加一个#{0},#{1},#{2}...参数
          param.put(entry.getValue(), args[entry.getKey().intValue()]);
          // issue #71, add param names as param1, param2...but ensure backward compatibility
          final String genericParamName = "param" + String.valueOf(i + 1);
          if (!param.containsKey(genericParamName)) {
            //2.再加一个#{param1},#{param2}...参数
            //你可以传递多个参数给一个映射器方法。如果你这样做了, 
            //默认情况下它们将会以它们在参数列表中的位置来命名,比如:#{param1},#{param2}等。
            //如果你想改变参数的名称(只在多参数情况下) ,那么你可以在参数上使用@Param(“paramName”)注解。 
            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())) {
        //如果返回类型是map类型的,查看该method是否有MapKey注解。如果有这个注解,将这个注解的值作为map的key
        final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
        if (mapKeyAnnotation != null) {
          mapKey = mapKeyAnnotation.value();
        }
      }
      return mapKey;
    }

    //得到所有参数
    private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) {
      //用一个TreeMap,这样就保证还是按参数的先后顺序
      final SortedMap<Integer, String> params = new TreeMap<Integer, String>();
      final Class<?>[] argTypes = method.getParameterTypes();
      for (int i = 0; i < argTypes.length; i++) {
        //是否不是RowBounds/ResultHandler类型的参数
        if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {
          //参数名字默认为0,1,2,这就是为什么xml里面可以用#{1}这样的写法来表示参数了
          String paramName = String.valueOf(params.size());
          if (hasNamedParameters) {
            //还可以用注解@Param来重命名参数
            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) {
            //查找@Param注解,一般不会用注解吧,可以忽略
            hasNamedParams = true;
            break;
          }
        }
      }
      return hasNamedParams;
    }

  }

}

至此,Mapper到最后还是调用了SqlSession接口的方法来执行相应的SQL语句,Mapper的xml文件的命名空间对应了这个接口的全限定名,而方法就是那条SQL的id

<mapper namespace="com.lyy.dao.PoetriesMapper">
package com.lyy.dao;
public interface PoetriesMapper {....}

    




















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盒曰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值