mybatis 源码解析之SQL调用过程

一、前言

本文只专注于myabtis Sql的调用过程,对于相关方法的细节并没有详细的介绍,如果有学习的欲望,可根据下文中的介绍的类和方法自行学习

二、正文

本文通过继承SqlSessionDaoSupport的方式直接通过sqlSession访问数据库。还有一种方式是通过定义接口来访问,其实这种方式的底层也是生成代理对象,通过sqlsession访问的。

@Repository
public class Dao  extends SqlSessionDaoSupport {
    /**
     * 设置工厂类
     *
     * @param sqlSessionFactory sql工厂类
     */
    @Autowired
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }
    public <T> T get(String prefix,String key,Object params){
        return getSqlSession().selectOne(prefix+key,params);
    }
    public void insert(String prefix,String key,Object params){
        getSqlSession().insert(prefix+key,params);
    }
    public void update(String prefix,String key,Object params){
        getSqlSession().update(prefix+key,params);
    }
    public void delete(String prefix,String key,Object params){
        getSqlSession().delete(prefix+key,params);
    }
    public <T> List<T> getList(String prefix, String key, Object params) {
        return this.getSqlSession().selectList(prefix + key, params);
    }

    /**
     * 获取分页的数据
     * @param prefix
     * @param key
     * @param params
     * @param page
     * @return
     */
    public Page page(String prefix, String key, Map<String,Object> params, Page page) {
        params.put("page",page);
        page.setList(this.getSqlSession().selectList(prefix + key, params));
        return page;
    }

SqlSession 接口中定义了很多增删改的方法,我们可以通过重写这个方法来进行数据库的访问。

public interface SqlSession extends Closeable {
    <T> T selectOne(String var1);

    <T> T selectOne(String var1, Object var2);

    <E> List<E> selectList(String var1);

    <E> List<E> selectList(String var1, Object var2);

    <E> List<E> selectList(String var1, Object var2, RowBounds var3);

    <K, V> Map<K, V> selectMap(String var1, String var2);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);

    void select(String var1, Object var2, ResultHandler var3);

    void select(String var1, ResultHandler var2);

    void select(String var1, Object var2, RowBounds var3, ResultHandler var4);

    int insert(String var1);

    int insert(String var1, Object var2);

    int update(String var1);

    int update(String var1, Object var2);

    int delete(String var1);

    int delete(String var1, Object var2);

    void commit();

    void commit(boolean var1);

    void rollback();

    void rollback(boolean var1);

    List<BatchResult> flushStatements();

    void close();

    void clearCache();

    Configuration getConfiguration();

    <T> T getMapper(Class<T> var1);

    Connection getConnection();
}

此处我们只针对于this.getSqlSession().selectList()的方法来跟踪sql的执行过程,我们先看getSqlSession方法看看到底是个什么东西。

public abstract class SqlSessionDaoSupport extends DaoSupport {
    private SqlSession sqlSession;
    private boolean externalSqlSession;

    public SqlSessionDaoSupport() {
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
       
            this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }

    }

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
    }

    public SqlSession getSqlSession() {
        return this.sqlSession;
    }

    protected void checkDaoConfig() {
        Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
    }
}

看的出来这个SqlSession实际类型是SqlSessionTemplate对象,下面我们看一下这个对象的构造方法

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
    }

    public SqlSessionFactory getSqlSessionFactory() {
        return this.sqlSessionFactory;
    }

    public ExecutorType getExecutorType() {
        return this.executorType;
    }

    public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
        return this.exceptionTranslator;
    }

    public <E> List<E> selectList(String statement) {
        return this.sqlSessionProxy.selectList(statement);
    }

    public <E> List<E> selectList(String statement, Object parameter) {
        return this.sqlSessionProxy.selectList(statement, parameter);
    }

看的出来,这里面selectList()方法其实是调用的this.sqlSessionProxy,是一个代理类,而这个代理类就是在构造方法中定义的,就是this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor())

下面我们看一下这个代理类的执行方法,就是new SqlSessionTemplate.SqlSessionInterceptor()里面的invoke()方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //获取sqlSession对象
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                //通过反射执行相应的方法并返回结果
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }

下一步我们去SqlSessionUtils.getSqlSession() 中去看看这个SqlSession 到底是什么东西。

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        //如果SqlSessionHolder存在,则直接获取sqlSession
        if (holder != null && holder.isSynchronizedWithTransaction()) {
            if (holder.getExecutorType() != executorType) {
                throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
            } else {
                holder.requested();
                if (logger.isDebugEnabled()) {
                    logger.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
                }

                return holder.getSqlSession();
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Creating a new SqlSession");
            }
            //这里是新创建一个SqlSession
            SqlSession session = sessionFactory.openSession(executorType);
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                Environment environment = sessionFactory.getConfiguration().getEnvironment();
                if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Registering transaction synchronization for SqlSession [" + session + "]");
                    }
                    //创建SqlSessionHolder
                    holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
                    TransactionSynchronizationManager.bindResource(sessionFactory, holder);
                    TransactionSynchronizationManager.registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
                    holder.setSynchronizedWithTransaction(true);
                    holder.requested();
                } else {
                    if (TransactionSynchronizationManager.getResource(environment.getDataSource()) != null) {
                        throw new TransientDataAccessResourceException("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
                    }

                    if (logger.isDebugEnabled()) {
                        logger.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
                    }
                }
            } else if (logger.isDebugEnabled()) {
                logger.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
            }

            return session;
        }
    }

这里的sessionFactory是上面博客中创建的DefaultSqlSessionFactory,看一下里面的openSession()方法

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

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

    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        DefaultSqlSession var8;
        try {
            boolean autoCommit;
            try {
                autoCommit = connection.getAutoCommit();
            } catch (SQLException var13) {
                autoCommit = true;
            }

            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            Transaction tx = transactionFactory.newTransaction(connection);
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var14, var14);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
    }

    private void closeTransaction(Transaction tx) {
        if (tx != null) {
            try {
                tx.close();
            } catch (SQLException var3) {
                ;
            }
        }

    }
}

可以看到openSession()调用了openSessionFromConnection()方法,里面创建了一个DefaultSqlSession对象。到这里我们已经获取了SqlSession对象,下一步看它实现的selestList()方法

/**
*这里面两个参数,第一个参数是mapper文件的命名空间+.+sql的id,第二个参数是我们的传参,第三个参数是分页的对象
**/
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var6;
        try {
            //我们通过这唯一的key去找到sql的定义类MappedStatement
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            //这里通过相应的mybatis执行器去执行相应的sql
            List<E> result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
            var6 = result;
        } catch (Exception var10) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var10, var10);
        } finally {
            ErrorContext.instance().reset();
        }

        return var6;
    }

通过跟踪代码可知 这个executor是SimpleExecutor,这里先不用关心怎么找到的SimpleExecutor,我们来看这个类里面的

query方法,这个类是继承的BaseExecutor,这个方法就在里面\

public abstract class BaseExecutor implements Executor {
...
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //boundSql 是sql的具体封装对象,里面有参数的定义,转化的sql语句和实际的传参
        BoundSql boundSql = ms.getBoundSql(parameter);
        //查找是否缓存
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                    //本文是没有缓存的,所以通过这个方法进行数据库的访问
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator i$ = this.deferredLoads.iterator();

                while(i$.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }
...
}
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
          
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            this.localCache.removeObject(key);
        }

        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }

这里面的doQuery()方法在它的子类SimpleExecutor中

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            //获取mybatis的配置类
            Configuration configuration = ms.getConfiguration();
            //获取StatementHandler,这个类的作用就是prepareStatement,
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

我们来看一下configuration.newStatementHandler方法

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        //这里实际上就生成的PreparedStatementHandler,
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        //这里是根据配置的拦截器生成相应的代理类
        StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

最后看一下handler.query()方法,也就是PreparedStatementHandler类中的query()方法

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        //可以看到底层也是通过jdbc进行数据库的访问的
        ps.execute();
        //这里通过myabtis的结果处理器,将ps返回的结果映射成我们定义的返回类型
        return this.resultSetHandler.handleResultSets(ps);
    }

这里的resultSetHandler默认是DefaultResultSetHandler

 public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
        List<Object> multipleResults = new ArrayList();
        int resultSetCount = 0;
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);

        while(rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
            this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }

        String[] resultSets = this.mappedStatement.getResulSets();
        if (resultSets != null) {
            while(rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                    this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
                }

                rsw = this.getNextResultSet(stmt);
                this.cleanUpAfterHandlingResultSet();
                ++resultSetCount;
            }
        }

        return this.collapseSingleResultList(multipleResults);
    }

最后这个方法就是通过mybatis默认的resultHandler ,通过反射映射成我们定义的resultType 对象,并返回。

三、结束语

其实mybatis整体的流程主线并不复杂,就是将我们定义的mapper 文件进行解析,并封装成一个个的mapperStatment,执行sql的时候通过各种解析器,执行器转化为普通jdbc的Statement对象,然后通过resultHandler 对返回结果进行解析并返回。但是里面的细节很多,设计的类也有很多,所以有时间的话我们可以研究研究某一步的执行细节。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值