mybatis

本文深入解析MyBatis的工作原理,从Configuration配置管理,到Mapper注册与解析,再到SqlSession的执行流程,详述了MyBatis如何处理SQL映射、参数绑定与结果集映射。同时,探讨了Executor执行器的角色及不同类型的执行策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis 原理解析

 先看mybatis的简单样例代码:
    DataSource ds = new DataSource();
    JdbcTransactionFactory jtf = new JdbcTransactionFactory();
    Environment env = new Environment("dev", jtf, ds);
    Configuration conf = new Configuration(env);
    conf.addMappers("mapper path");
    SqlSessionFactory ssf =    new SqlSessionFactoryBuilder().build(conf);
    try(SqlSession ss = ssf.openSession()){
        ss.selectOne("key", args);
        or
        ss.getMapper(Mapper.class).method(arg);        
    }

    以上简单代码使用java code形式代替config.xml, 可以更加直观了解mybatis做了什么. 其实mybatis执行sql获取结果封装成对象所需要的外部条件只需要DataSource和sql, SqlSession内部也只是对
    传统的Datasource操作做了封装. 以上代码最重要的类是Configuration, 封装了datasource, SqlSession的构建也依赖于他.

    首先来分析Configuration.addMappers做了什么, 通过源码调用过Configuration.MapperRegistry.addMappers(packageName)-获取packageName下的所有class(Object的子类)-> MapperRegistry.addMapper(Class) 该class未被解析过则放MapperRegistry中的Map<Class, MapperProxyFactory>中, 构建MapperAnnotationBuilder(Configuration, Class)去解析-> MapperAnnotationBuilder.parse 首先判断class是否解析过(通过class的全限定名, 解析过得放入Configuration中的Set<String> loadedResources中), 未解析的话就会去加载对应位置的Mapper.xml(class的全限定名+.xml), 通过XMLMapperBuilder.parse
    解析的结果存储在Configuration的Map<String, MappedStatement> mappedStatements中, 其中key为mapper.xml 中的namespace+id, value为MappedStatement,该类包含了sql的详细信息(sql, cache, 参数map, 返回值类型, useCache,
    timeout等),(这也就是为什么mapper.xml必须要和mapper.java位于相同目录下, 其实通过MapperAnnoattionBuilder.loadXmlResource的执行过程可知,我们可以通过XMLMapperBuilder自己手动加载指定目录下的mapper.xml, spring也是这么做的), 加载对应的mapper.xml文件后也会去解析Class所有方法, 因为mybatis提供了给予注解的方式(@Select, @Delete)同样会加载到Configuration的mappedStatements中, 如果同时提供了注解和mapper.xml 声明会报错.

    接下来看SqlSession的创建和执行过程. new SqlSessionFactoryBuilder().build(conf)-> new DefaultSqlSessionFactory(config) , SqlSessionFactory.openSession() -> new DefaultSqlSession(configuration, executor,     autoCommit). 这里比较重要的类就是Executor, 文档没有给出详细的说明, 但通过其声明的接口可知,所有的数据库操作都是通过Executor执行的, 而Executor的实现类有SimpleExecurot, ClosedExecutor, ResuExecutor, BatchExecutor, CacingExecutor(Executor的类型可以通过Configuration中的defaultExecutorType设置, 默认为ExecutorType.SIMPLE, CachingExecutor通过cacheEnable), 以SimpleExecutor为例, DefaultSqlSession.selectOne -> 从 Configuration  中通过key获取MappedStatement交由Executor.query(MappedStatement, wrapCollection(parameter), RowBounds, ResultHandler)->  BaseExecutor.query 从MappedStatement获取BoundSql, 构建CacheKey, ResultHandler==null 从localcaceh获取结果否则queryFromDatabase-> doQuery(MappedStatement, parameter, RouwBounds, ResultHandler) ->  SimpleExecutor实现对应的方法, 通过Configuration.newStatementHandler(Executor, MappedStatement, parameter, RowBounds, ResultHandler, BoundSql)构造StatementHandler, StatementHandler通过MappedStatement获取PreparedStatement,然后StatementHandler.query(Statement, ResultHandler), 这里具体的StatementHandler为RoutingStatementHandler采用委托模式具体的StatementHandler 根据MappedStatement.getStatementType而定( 默认的为StatementType.PREPARED) -> PreparedStatementHandler.query, PreparedStatement.execute().

return ResultSetHandler.handleResultSets(preparedStatement).


    还有一种方式是通过SqlSession.getMapper去执行, Mapper为接口, 显然此处使用了jdk的动态代理, 从Configuration中的mapperRegistry中获取代理对象工厂进而获取代理对象, 所有的Mapper接口都会生成对应的MapperProxy,MapperProxy implements InvocationHandler, invoke方法通过MapperMethod.execute(SqlSession, args), 还是通过SqlSession 去执行, 通过方法名和类名就可以从Configuration中获取对应的MapperStatement.


    mybatis Configuration 中的InterceptorChain 可以对StatementHandler, Executor, ParameterHandler 进行拦截处理, 实现原理也是基于jdk动态代理, 相应的interceptor要声明@Interceptors其中@Signature中type参数为接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值