聊聊Mybatis的实现原理

这篇具有很好参考价值的文章主要介绍了聊聊Mybatis的实现原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用示例

平时我们使用的一般是集成了Spring或是Spring Boot的Mybatis,封装了一层,看源码不直接;如下,看看原生的Mybatis使用示例

聊聊Mybatis的实现原理

示例解析

通过代码可以清晰地看出,MyBatis的操作主要分为两大阶段:

  • 第一阶段:MyBatis初始化阶段。该阶段用来完成MyBatis运行环境的准备工作,读取配置并初始化关键的对象,提供给后续使用,只在 MyBatis启动时运行一次。
  • 第二阶段:数据读写阶段。该阶段由数据读写操作触发,将根据要求完成具体的增、删、改、查等数据库操作。

在第一阶段,最关键的就是SqlSessionFactory对象。在Spring集成Mybatis的源码中,SqlSessionFactoryBean也是做这个事情,读取配置并初始化构建SqlSessionFactory

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    // Spring Bean的生命周期会调用此方法
    public void afterPropertiesSet() throws Exception {
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }
    protected SqlSessionFactory buildSqlSessionFactory(){
        // 构建Configuration....
        Configuration configuration;
        if (this.configuration != null) {
            configuration = this.configuration;
            if (configuration.getVariables() == null) {
                configuration.setVariables(this.configurationProperties);
            } else if (this.configurationProperties != null) {
                configuration.getVariables().putAll(this.configurationProperties);
            }
        } else if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
            configuration = xmlConfigBuilder.getConfiguration();
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property `configuration` or 'configLocation' not specified, using default MyBatis Configuration");
            }

            configuration = new Configuration();
            configuration.setVariables(this.configurationProperties);
        }

        /// 其它code...

        return this.sqlSessionFactoryBuilder.build(configuration);
    }
}

配置文件的解析,最终会生成一个Configuration对象。

private void parseConfiguration(XNode root) {
    try {
        Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
        this.propertiesElement(root.evalNode("properties"));
        this.loadCustomVfs(settings);
        this.typeAliasesElement(root.evalNode("typeAliases"));
        this.pluginElement(root.evalNode("plugins"));
        this.objectFactoryElement(root.evalNode("objectFactory"));
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
        this.settingsElement(settings);
        this.environmentsElement(root.evalNode("environments"));
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        this.typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析mappers节点
        this.mapperElement(root.evalNode("mappers"));
    } catch (Exception var3) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    }
}

前期的准备已就绪,关键的配置已解析且构建并初始化了SqlSessionFactory了。接下来就是创建数据库连接并执行业务的CRUD。在第二阶段的OpenSession方法负责创建并打开数据库链接。

public SqlSession openSession(Connection connection) {
    return this.openSessionFromConnection(this.configuration.getDefaultExecutorType(), connection);
}

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);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}

最后就是调用Mapper接口的业务方法,返回业务数据。

//SqlSession.getMapper()
public <T> T getMapper(Class<T> type) {
    return this.configuration.getMapper(type, this);
}

// configuration.getMapper()
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.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

// mapperProxyFactory.newInstance()
protected T newInstance(MapperProxy<T> mapperProxy) {
    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 this.newInstance(mapperProxy);
}

最后,打完收工,示例代码所涉及到的关键代码就这些。

反思

上面的示例是比较简单的,那么其实现思路到底是什么样的?首先就有几个问题:

  1. Mapper接口中的方法没有实现,那客户端调用接口的方法时,返回的数据是从哪来的?
  2. Mapper文件与Mapper接口是怎么关联绑定上的?

第一个问题,绝对离不开动态代理,因为只有接口的时候,那么一定会有动态代理生成代理类同时有拦截处理器(InvocationHandler)来增强其执行逻辑。

第二个问题,Mapper文件中有一个<mapper>节点,其namespace就是接口的全限定名称,而其下节点<select>|<update>|..都有一个id值,该值与接口的方法是一致的。因此从这里就可以看出来,业务的crud操作节点是通过namespace+id来对应mapper接口及其方法的。那么我们就可以考虑到,在第一个问题中的拦截处理器执行方法method时,我们就可以通过此关联关系找到要执行的SQL。

如上,这么一分析来看,其实大概的实现思路已经出来了。就是动态代理+<mapper>节点解析实现了接口方法的调用与业务SQL的执行。

因此在第一阶段的解析时,Mapper文件里的<Mapper>节点解析出来的对象就起到了关键的作用。如下,有几个关键的抽象:

MapperRegistry类的knownMappers属性保存着接口及其代理对象的关系。

// type = interface
knownMappers.put(type, new MapperProxyFactory(type));

MappedStatement类对应着<mapper>节点下的CRUD节点。

public void parseStatementNode() {
    String id = this.context.getStringAttribute("id");
    if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        Integer fetchSize = this.context.getIntAttribute("fetchSize");
        Integer timeout = this.context.getIntAttribute("timeout");
        String parameterMap = this.context.getStringAttribute("parameterMap");
        String parameterType = this.context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = this.resolveClass(parameterType);
        String resultMap = this.context.getStringAttribute("resultMap");
        String resultType = this.context.getStringAttribute("resultType");
        // 解析其他属性...
        this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}

如上是抽象出来整个执行过程的简单流程。最后就是基于JDBC创建Statement对象,并执行execute方法。文章来源地址https://www.toymoban.com/news/detail-453197.html

到了这里,关于聊聊Mybatis的实现原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 聊聊mybatis的Interceptor机制

    本文主要研究一下mybatis的Interceptor机制 org/apache/ibatis/plugin/Interceptor.java Interceptor定义了intercept方法,其参数为Invocation类型,同时默认提供了plugin方法,通过Plugin.wrap(target, this)进行包装 org/apache/ibatis/plugin/Invocation.java Invocation定义了target、method、args属性,提供了proceed方法则是

    2024年02月10日
    浏览(36)
  • 使用Spring Boot、MyBatis Plus和Elasticsearch配置的简单示例

    下面是一个使用Spring Boot、MyBatis Plus和Elasticsearch的简单示例: 首先,在pom.xml文件中添加以下依赖: 创建一个Spring Boot应用程序: 在application.properties文件中配置Elasticsearch: spring.data.elasticsearch.cluster-name:设置Elasticsearch集群的名称。 spring.data.elasticsearch.cluster-nodes:设置Elast

    2024年02月11日
    浏览(44)
  • 聊聊mybatis-plus的SafetyEncryptProcessor

    本文主要研究一下mybatis-plus的SafetyEncryptProcessor mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/SafetyEncryptProcessor.java SafetyEncryptProcessor实现了EnvironmentPostProcessor接口,在postProcessEnvironment方法中先是找到mpw.key,然后遍历所有PropertySource的所有属性,找到mpw:开头的,然

    2024年02月10日
    浏览(33)
  • 平时很少使用的c和c++语法逻辑

    1. goto语句 example: #includestdio.h #includestdlib.h int main(){ string hello=\\\"\\\"; string world=\\\"\\\"; while(cinhello){         goto Tiaoshu; } Tiaoshu:         cout\\\"hellow world!\\\"endl; return 0; } 2. break 和continue #includestdio.h #includestdlib.h int main(){         int a=0; while(cina){ swtch(a){         case 常量表达式0:      

    2024年02月02日
    浏览(44)
  • Mybatis分页方式及实现原理

    一、mybatis的4种分页方式(物理分页、逻辑分页) 1、借助Sql语句Q进行分页(物理分页) 2、拦截器分页(物理分页)通过拦截器给sq语句末尾加Eimt语句来查询 3、借助 数组Q进行分页(逻辑分页) 4、RowBounds分页插件实现分页(逻辑分页) 二、mybatis分页的原理 mybatis分页原理是:在MyBatis内部

    2024年02月16日
    浏览(32)
  • 聊聊 神经网络模型 示例程序——数字的推理预测

    之前学习了解过了神经网络、CNN、RNN、Transformer的内容,但除了在魔塔上玩过demo,也没有比较深入的从示例去梳理走一遍神经网络的运行流程。从数字推测这个常用的示例走一遍主流程。 MNIST是机器学习领域 最有名的数据集之一,被应用于从简单的实验到发表的论文研究等各

    2024年02月05日
    浏览(49)
  • 聊聊mybatis-plus的sql加载顺序

    本文主要研究一下如果mybatis mapper定义了多个同名方法会不会有问题 com/baomidou/mybatisplus/core/MybatisConfiguration.java com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java MybatisSqlSessionFactoryBean的buildSqlSessionFactory方法会根据mapperLocations的配置取加载xml配置,即加载xml的mapper信息

    2024年02月10日
    浏览(42)
  • MyBatis插件原理探究和自定义插件实现

    ⼀般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者⾃⾏拓展。这样的好处是显⽽易⻅的,⼀是增加了框架的灵活性。⼆是开发者可以结合实际需求,对框架进⾏拓展,使其能够更好的⼯作。以MyBatis为例,我们可基于MyBati s插件机制实现分⻚、分表,监控等功

    2024年02月15日
    浏览(39)
  • 聊聊分布式架构06——[NIO入门]简单的Netty NIO示例

    目录 Java NIO和Netty NIO比较 Java NIO: Netty: Netty NIO中的主要模块 Transport(传输层) Buffer(缓冲区) Codec(编解码器) Handler(处理器) EventLoop(事件循环) Bootstrap和Channel(引导和通道) Future和Promise(异步编程) Netty示例 服务端时序图 服务端代码 客户端时序图 客户端代码

    2024年02月07日
    浏览(36)
  • MyBatis实战指南(二):工作原理与基础使用详解

    MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。那么,它是如何工作的呢?又如何进行基础的使用呢?本文将带你了解MyBatis的工作原理及基础使用。 1.1 MyBatis的工作原理 工作原理图示: 1、读取MyBatis配置文件 mybatis-config.xml为MyBatis的全局配置文件

    2024年02月02日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包