MyBatis系列之浅谈SQL执行流程分析

   日期:2020-05-15     浏览:110    评论:0    
核心提示:目录独立使用MybatisMybatis执行流程SqlSessionFactory\\SqlSession##MapperProxyExcutor独立使用Mybatis这篇文章主要以分析Mybatis框架执行SQL的流程。回忆曾经独立使用Mybatis半自动化框架时,我们需要执行以下步骤:读取配置文件(mybatis-config.xml),初始化配置类即configuration;创建SQLSessionFactory;创建SqlSession;执行SQL,处理结果集对应如下代码:pujava

目录

  • 独立使用Mybatis
  • Mybatis执行流程
    • SqlSessionFactory\SqlSession
    • MapperProxy
    • Excutor

独立使用Mybatis

这篇文章主要以分析Mybatis框架执行SQL的流程。
回忆曾经独立使用Mybatis半自动化框架时,我们需要执行以下步骤:

  1. 读取配置文件(mybatis-config.xml),初始化配置类即configuration;
  2. 创建SQLSessionFactory;
  3. 创建SqlSession;
  4. 执行SQL,处理结果集
    对应如下代码:
public class MyBatisUtils {
    private final static SqlSessionFactory SQL_SESSION_FACTORY;
    static {
        String resource = "mybatis-config.xml";
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(reader);
    }
    public static SqlSessionFactory getSqlSessionFactory() {
        return SQL_SESSION_FACTORY;
    }
}

//单元测试
public class MapperTest {
    static SqlSessionFactory sqlSessionFactory = null;
    static {
        sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();
    }

    @Test
    public void testAdd() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = new User();
            user.setUsername("hello");
            user.setAge(5);
            userMapper.insert(user);
            sqlSession.commit();    // 这里一定要提交,不然数据进不去数据库中
        } finally {
            sqlSession.close();
        }
    }

    @Test
    public void getUser() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = userMapper.selectByPrimaryKey(32L);
            System.out.println("name: " + user.getUsername() + "| age: " + user.getAge());
        } finally {
        }
    }
}

Mybatis执行流程

基于上面说明,我们基本了解了执行流程,下面我们从源码层面解释一下流程。

SqlSessionFactory\SqlSession


独立使用Mybatis时,第一步要读取配置文件,因此,我们将从读取配置文件开始。

  1. SqlSessionFactoryBuilder读取mybatis的配置文件,调用build方法创建DefaultSqlSessionFactory
 
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            //parse()方法XMLConfigBuilder类解析xml文件,同时完成configuration属性的创建
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

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

  1. 获取SqlsessionFactory之后,调用openSession()方法创建SQLSession,这里说明一下SQLSession仅仅向外提供了对数据库操作的支持,真正执行对数据库的操作是execute的职责。
DefaultSqlSessionFactory类

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //之前说了,从表面上来看,咱们是用sqlSession在执行sql语句, 实际呢,其实是通过excutor执行, excutor是对于Statement的封装
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建SqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      // may have fetched a connection so lets call close()
      closeTransaction(tx);
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

获取SQLSession之后,对数据进行CRUD操作的准备工作就正式结束。

MapperProxy


主要处理我们Mybatis的映射文件。该类主要负责代理开发中的mapper。那么思考一下该代理对象如何获取呢?
下面我们从SQLSession中跟踪。

DefaultSQLSession类中
public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

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



    @SuppressWarnings("unchecked")
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        //从缓存中获取该Mapper接口的代理工厂对象
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        //如果该Mapper接口没有注册过,则抛异常
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            /
    @SuppressWarnings("unchecked")
    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<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }

一路跟踪下来,我们发现MapperProxy对象是在MapperProxyFactory里创建完成。

Excutor


上面我们提到Excutor的职责是负责对数据的crud操作,上面的时序图,详细地说明了SQL的执行过程。
对于每一个MapperProxy对应开发人员自定的Mapper(dao)接口,下面我们将从源码追踪,如何实现的。

  • MappProxy:
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            //判断是否为通用类,mybatis使用JDK代理方式,即面向接口,false
            //method.getDeclaringClass()返回底层的类对象的class
            if (Object.class.equals(method.getDeclaringClass())) {
                //如果是类,则利用反射机制,返回目标对象
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                //如果是默认方法,则执行默认方法,java1.8提供;
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        //从缓存中获取MapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        //执行方法对应的SQL语句,返回查询的结果
        
        return mapperMethod.execute(sqlSession, args);
    }
  • MapperMethod:

    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
            case INSERT: {
                //param 为参数名和参数值的对应关系,即map集合
                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()) {
                    //void类型且方法中有ResultHandler参数(特殊参数),调用sqlSession.select执行
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (method.returnsMany()) {
                    //返回类型是集合或者数组,调用sqlSession.<E>selectList执行
                    result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                    //返回 map ,调用 sqlSession.<K, V>selectMap
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                    result = executeForCursor(sqlSession, args);
                } else {
                    Object param = method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(command.getName(), param);
                    if (method.returnsOptional() &&
                            (result == null || !method.getReturnType().equals(result.getClass()))) {
                        result = Optional.ofNullable(result);
                    }
                }
                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;
    }

本篇文章主要是浅析SQL的执行流程,因此我们建议以selectList为例,带领大家熟悉一下流程,后面会专门介绍
各个步骤的流程。
回归到SQLSession

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      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();
    }
  }
  • BaseExecutor:

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //获取查询SQL
        BoundSql boundSql = ms.getBoundSql(parameter);
        //创建缓存的key,即作为HashMap中的key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        //执行查询
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

  • SimpleExecutor:
@Override
  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());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  • preparedStatement:
@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

参考资料:Spring源码深度解析第二版

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服