zoukankan      html  css  js  c++  java
  • MyBatis源码解读(4)——SqlSession(上)

      在上一篇博客中提到MyBatis是如何实现代理类MapperProxy,并抛出了一个问题——是怎么执行一个具体的sql语句的,在文末中提到了MapperMethod的execute采用命令模式来判断是何种sql语句,并将具体语句的执行交由SqlSession处理。所以此篇博客正是要讲到SqlSession。

      在SqlSession接口中包含了所有可能执行的sql语句在这里不一一列举,请参考org.apache.ibatis.session.SqlSession源码。DefaultSqlSession是SqlSession的实现类,所以我们重点关注DefaultSqlSession类。在上一篇博客中提到了我们将选择MapperProxy的executeForMany方法。在此方法中调用了SqlSession.<E>selectList方法。所以我们来看看org.apache.ibatis.session.default.DefaultSqlSession类的selectList方法。

     1 //org.apache.ibatis.session.defaults.DefaultSqlSession
     2 ……
     3   @Override
     4   public <E> List<E> selectList(String statement) {
     5     return this.selectList(statement, null);
     6   }
     7 
     8   @Override
     9   public <E> List<E> selectList(String statement, Object parameter) {
    10     return this.selectList(statement, parameter, RowBounds.DEFAULT);
    11   }
    12 
    13   @Override
    14   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    15     try {
    16       MappedStatement ms = configuration.getMappedStatement(statement);
    17       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    18     } catch (Exception e) {
    19       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    20     } finally {
    21       ErrorContext.instance().reset();
    22     }
    23   }
    24 ……

    我们看到关于selectList有三个重载方法,最后调用的都是public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)。在此方法中第一个参数为String类型且命名为statement,第二个参数为Object命名为parameter,回到MapperMethod。

     1 //org.apache.ibatis.binding.MapperMethod
     2  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
     3     List<E> result;
     4     Object param = method.convertArgsToSqlCommandParam(args);
     5     if (method.hasRowBounds()) {
     6       RowBounds rowBounds = method.extractRowBounds(args);
     7       result = sqlSession.<E>selectList(command.getName(), param, rowBounds);  //注意传递了什么参数进来
     8     } else {
     9       result = sqlSession.<E>selectList(command.getName(), param);
    10     }
    11     // issue #510 Collections & arrays support
    12     if (!method.getReturnType().isAssignableFrom(result.getClass())) {
    13       if (method.getReturnType().isArray()) {
    14         return convertToArray(result);
    15       } else {
    16         return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    17       }
    18     }
    19     return result;
    20   }

      第7行代码可以看到传递了什么参数进来,跟踪command.getName()是怎么来的。

    private final SqlCommand command;

      看来是一个叫做SqlCommand的变量,进而我们可以发现这个SqlCommand是MapperMethod的静态内部类。

     1 //org.apache.ibatis.binding.MapperMethod
     2   public static class SqlCommand {
     3 
     4     private final String name;
     5     private final SqlCommandType type;
     6 
     7     public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
     8       String statementName = mapperInterface.getName() + "." + method.getName();  //获取接口+方法名
     9       MappedStatement ms = null;  //定义一个MappedStatement,这个MapperedStatement稍后介绍
    10       if (configuration.hasStatement(statementName)) {  //从Configuration对象查找是否有这个方法的全限定名称
    11         ms = configuration.getMappedStatement(statementName);  //有,则根据方法的全限定名称获取MappedStatement实例
    12       } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35//如果没有在Configuration对象中找到这个方法,则向上父类中获取全限定方法名
    13         String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
    14         if (configuration.hasStatement(parentStatementName)) {  //在向上的父类查找是否有这个全限定方法名
    15           ms = configuration.getMappedStatement(parentStatementName);   //有,则根据方法的全限定名称获取MappedStatement实例
    16         }
    17       }
    18       if (ms == null) {
    19         if(method.getAnnotation(Flush.class) != null){
    20           name = null;
    21           type = SqlCommandType.FLUSH;
    22         } else {
    23           throw new BindingException("Invalid bound statement (not found): " + statementName);
    24         }
    25       } else {
    26         name = ms.getId();  //这个ms.getId,其实就是我们在mapper.xml配置文件中配置一条sql语句时开头的<select id="……"……,即是接口的该方法名全限定名称
    27         type = ms.getSqlCommandType();  //显然这是将sql是何种类型(insert、update、delete、select)赋给type
    28         if (type == SqlCommandType.UNKNOWN) {
    29           throw new BindingException("Unknown execution method for: " + name);
    30         }
    31       }
    32     }
    33 
    34     public String getName() {
    35       return name;
    36     }
    37 
    38     public SqlCommandType getType() {
    39       return type;
    40     }
    41   }

      大致对MapperMethod的解读到此,再次回到DefaultSqlSession中,到这里可能稍微有点混乱。画个图来理解这一段,希望能帮助理解。

      这次又一次回到了DefaultSqlSession.selectList。此处不再贴出selectList所有方法,而是只贴出一句,根据接口方法的全限定名称获取MappedStatement。

    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

      在获取到MappedStatement后,碰到了第一个SqlSession下的四大对象之一:Executor执行器。看来还是没有讲到SqlSession,再放到下一节吧。可以思考下这个MappedStatement是什么意思,望文生义就能猜出个大概。

    这是一个能给程序员加buff的公众号

  • 相关阅读:
    sqlserver数据库的备份与还原——完整备份与还原
    sqlserver中为节约存储空间的收缩数据库机制
    sqlserver数据库的分离与附加
    sqlserver的数据库状态——脱机与联机
    sqlserver打开对象资源管理器管理的帮助文档的快捷键
    sqlserver使用SQL语句创建数据库登录对象、数据库用户以及对为该用户赋予操作权限
    sqlserver window身份验证时切换账户的快捷键
    向现有数据库中添加文件组和数据文件
    使用SQL语句创建数据库2——创建多个数据库文件和多个日志文件
    Java中怎样判断一个字符串是否是数字?
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/6076052.html
Copyright © 2011-2022 走看看