MyBatis框架源码解析

MyBatis是一个优秀的持久层框架,通过简洁的XML配置方式就能消除以前传统
使用JDBC出现大量重复的代码,以及参数的设置和结果集的映射. 如果能够懂的底层代码和原理,在使用上就会更得心应手。而且出现错误,也能更快的找出错误的原因。解析源码
还有个好处,就是能够看一下大神的设计思想。这些设计思想也可以运用到实际开发中,而且如果自己明白框架怎么写之后,自己也可以尝试去写一个开源框架出来; 同时,阅读解析源码,也是高级程序员必备的技能之一;

下面我们就从一个简单的示例开始源码的分析:

public static void main(String[] args) {
    String resource = "Configuration.xml";
    Reader reader;
    try {
        //解析配置文件得到Reader
        reader = Resources.getResourceAsReader(resource);
        //通过配置文件得到SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //得到SqlSession
        SqlSession session = sqlSessionFactory.openSession();
        try {
            //调用查询方法 完成封装
            List<User> users =  session.selectList("cn.itsource.mapper.UserMapper.getUser");
            users.forEach(user->{
                 System.out.println(user);
             });
        } finally {
            session.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

(1) 第一步 得到SqlSessionFactory对象 ,关键方法 SqlSessionFactoryBuilder().build(reader).我们跟进源码SqlSessionFactoryBuilder类的build方法:

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
	// XMLConfigBuilder XML配置解析类
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
	//parser.parse()解析结果放入Configuration里面
	//build方法得到DefaultSqlSessionFactory
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            reader.close();
        } catch (IOException var13) {
            ;
        }

    }

    return var5;
}

通过build方法 最终得到DefaultSqlSessionFactory

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

已经得到SqlSessionFactory对象了,而且把配置文件信息存入了DefaultSqlSessionFactory 这个类的config变量上;

(2)通过 sqlSessionFactory.openSession()得到SqlSession对象
源码如下:

public SqlSession openSession() {
	通过该方法得到SqlSession
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}

openSessionFromDataSource方法如下:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
	//从配置类里面得到环境变量
        Environment environment = this.configuration.getEnvironment();
	//得到事务配置
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
	//得到事务对象
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
	//通过事务得到执行器
        Executor executor = this.configuration.newExecutor(tx, execType, autoCommit);
       //通过执行器得到DefaultSqlSession对象-关键代码
	 var8 = new DefaultSqlSession(this.configuration, executor);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

最终得到一个SqlSession对象(DefaultSqlSession是SqlSession实现类)

(3)通过sqlSession对象调用SelectList方法返回数据

List<User> users =  session.selectList("cn.itsource.mapper.UserMapper.getUser");

我们进入核心的selectList方法里面去:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var6;
    try {
	//从配置得到Mapper语句对象 –解析mapper.xml文件
        MappedStatement ms = this.configuration.getMappedStatement(statement);
	//通过执行器调用query方法 封装数据 返回结果
        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;
}

Query方法最终调用BaseExecutor里面query方法:

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//动态解析sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    //缓存
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

在这个方法里面做了下面一些事情:
(1) 解析动态的sql
(2) 加入缓存里面
(3) 进行查询

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;
    }
}

从上面的源码中 可以看出最终调用:queryFromDatabase方法
这个方法最终会调用SimpleExecutor的doQuery方法

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

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
	//通过handler调用query查询方法
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

最终调用PreparedStatementHandler类的query方法

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement)statement;
    ps.execute();
	//处理结果的封装
    return this.resultSetHandler.handleResultSets(ps);
}

大致的流程图:
得到SqlSession的过程
源码时代
调用方法的过程
源码时代
封装数据过程:
源码时代

好了,这次源码分析就到这个地方; 通过这次源码剖析,我们看到了mybatis从加载配置文件到调用方法以及返回数据进行封装的整个过程; 从源码里面我们看出,框架底层分别使用很多类和接口来完成开发,而且具备单一职责的原则,一个类就做一个事情,通过层层组合,就完成整套流程,当然 这次分析源码,就是一部分内容。下次在继续.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值