mybatis系列(四)--mybatis的核心:sql的执行流程(深入源码)


先看mybaits是如何拿到sqlSession并执行sql的,当然sqlSession只是表面表面上执行sql,下面代码会讲到为什么。


                                                           (图片转载)

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
     try {
       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
       return build(parser.parse());
     } catch (Exception e) {
       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
     } finally {
       ErrorContext.instance().reset();
       try {
         inputStream.close();
       }
       catch (IOException e) {}
     }
   }


   public SqlSessionFactory build(Configuration config)
   {
     return new DefaultSqlSessionFactory(config);
   }
 }


拿到DefaultSqlSessionFactory之后,调用他的的openSession()方法

 public SqlSession openSession() {
     return openSessionFromDataSource(this.configuration.getDefaultExecutorType(), null, false);
   }

然后调用openSessionFromDataSource()方法,创建DefaultSqlSession对象

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
     Transaction tx = null;
     try {
       //通过configuration获取xml的配置信息之后,用Environment封装起来
       Environment environment = this.configuration.getEnvironment();
       TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
       tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
       //这里的excutor很关键,其实是他去执行sql的,而不是sqlsession
       Executor executor = this.configuration.newExecutor(tx, execType);
       //然后创建DefaultSqlSession对象
       return new DefaultSqlSession(this.configuration, executor, autoCommit);
     } catch (Exception e) {
       closeTransaction(tx);
       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
     } finally {
       ErrorContext.instance().reset();
     }
   }


 

创建DefaultSqlSession对象之后,看到里面的方法名就知道,我们可以去执行sql了。

现在虽然拿到DefaultSqlSession了,但是还不知道怎么执行sql的。

在mybatis中是通过MapperProxy动态代理我们的Dao接口的。这也是mybatis的核心。先看拿到DefaultSqlSession后的执行流程

                                                              (图片转载)

public static void main(String[] args) {
		SqlSession sqlSession=SqlSessionFactoryUtil.openSession();
		//拿到sqlSession之后,执行getMapper方法
		StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class);
		Student student=new Student("我",11);
		int result=studentMapper.add(student);
		sqlSession.commit();
		if(result>0){
			System.out.println("添加成功!");
		}
	}

执行getMapper方法之后

//没有做什么,直接给了configuration去执行。
   public <T> T getMapper(Class<T> type) {
     return this.configuration.getMapper(type, this);
   }


//configuration还是什么都没做,直接给了mapperRegistry(mapper注册)去执行
   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     return this.mapperRegistry.getMapper(type, sqlSession);
   }

看看mapperRegistry的getMapper做了什么

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	 MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
     if (mapperProxyFactory == null)
       throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
     try {
    	 //跟进去看看newInstance方法做了什么
       return mapperProxyFactory.newInstance(sqlSession);
     } catch (Exception e) {
       throw new BindingException("Error getting mapper instance. Cause: " + e, e);
     }
   }


拿到MapperProxy之后,为每个dao创建动态代理

protected T newInstance(MapperProxy<T> mapperProxy)
   {
	   //这个总算到了mybatis的核心了,这里创建了一个动态代理类
     return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[] { this.mapperInterface }, mapperProxy);
   }
   
   
   public T newInstance(SqlSession sqlSession) {
     MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
     return newInstance(mapperProxy);
   }
 }


拿到MapperProxy之后,看下执行流程


(图片转载)


下面看是看看sql的具体执行过程,注意下面的return method.invoke(this, args);它永远不会执行,因为dao是接口

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     if (Object.class.equals(method.getDeclaringClass())) {
       try {
         return method.invoke(this, args);//这里的代码不会执行,而且永远不会执行,因为dao是接口
       } catch (Throwable t) {
         throw ExceptionUtil.unwrapThrowable(t);
       }
     }
     //执行mapperMethod的execute方法
     MapperMethod mapperMethod = cachedMapperMethod(method);
     return mapperMethod.execute(this.sqlSession, args);
   }

MapperMethod:可以看到这里判断要执行哪个方法

public Object execute(SqlSession sqlSession, Object[] args) {
     Object result;
     if (SqlCommandType.INSERT == this.command.getType()) {
       Object param = this.method.convertArgsToSqlCommandParam(args);
       result = rowCountResult(sqlSession.insert(this.command.getName(), param)); } else { Object result;
       if (SqlCommandType.UPDATE == this.command.getType()) {
         Object param = this.method.convertArgsToSqlCommandParam(args);
         result = rowCountResult(sqlSession.update(this.command.getName(), param)); } else { Object result;
         if (SqlCommandType.DELETE == this.command.getType()) {
           Object param = this.method.convertArgsToSqlCommandParam(args);
           result = rowCountResult(sqlSession.delete(this.command.getName(), param)); } else { Object result;
           if (SqlCommandType.SELECT == this.command.getType()) { Object result;
             if ((this.method.returnsVoid()) && (this.method.hasResultHandler())) {
               executeWithResultHandler(sqlSession, args);
               result = null; } else { Object result;
               if (this.method.returnsMany()) {
                 result = executeForMany(sqlSession, args); } else { Object result;
                 if (this.method.returnsMap()) {
                   result = executeForMap(sqlSession, args);
                 } else {
                   Object param = this.method.convertArgsToSqlCommandParam(args);
                   result = sqlSession.selectOne(this.command.getName(), param);
                 }
               }
             } } else { throw new BindingException("Unknown execution method for: " + this.command.getName()); } } } }
     Object result;
     if ((result == null) && (this.method.getReturnType().isPrimitive()) && (!this.method.returnsVoid())) {
       throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
     }


执行sqlsession的selectList方法

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      //执行query方法
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }


最后调用doQuery方法

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler封装了Statement, 让 StatementHandler 去处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }


最后看到JDBC的代码了,没错mybatis是对JDBC的分装
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //最后看到PreparedStatement
   PreparedStatement ps = (PreparedStatement) statement;
   ps.execute();
   //结果交给了ResultSetHandler 去处理
   return resultSetHandler.<E> handleResultSets(ps);
 }

这样一条sql就执行完了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值