浅入浅出Mybatis(二)

150 阅读2分钟

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

ORM存在的意义就是提升业务开发效率,提升业务代码与数据库交互的性能。Mybatis框架可以说是ORM的常青树,伴随spring的发展到Spring boot是很多业务开发者的首选。时至今日,有一部分人会觉得xml是万恶之源,讨厌这样的东西存在于自己的代码中,可能是Spring boot带起来的风潮吧。但实际上,这东西除了长的和Java代码不一样之外,性能上并不会降低多少。下面从整体上看一下Mybatis是如何运行的。

初始化

mybatis在执行前会初始化一下,一般情况下会默认读取mybatis-config.xml文件。初始化仅仅会在项目启动时执行一次,以获取mybatis运行时必要的配置信息:数据库url,user,password。这是基础信息,mybatis是支持插件的,比如分页PageHelper;还有一些设置信息,比如自动生成主键,驼峰命名自动转换,超时时间等。以下是初始化获取配置的方法:

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

以上解析获得了一个Configuration对象,这是构建SqlSessionFactory的基础,这个又是获得SqlSeesion的接口,而SqlSeesion提供了大量的CRUD方法,是日常使用最多的方法。在数据读写时首先会获取SqlSession:

SqlSession session=sqlSessionFactory.openSession();

数据读写

初始化完成之后,就可以开始进行数据的读写了。这个过程可以简单概括为以下几步:

  1. 寻找Java接口与xml的映射
  2. 组装参数
  3. 执行接口调用返回结果集

首先在MapperRegistry类中通过getMapper方法找到对应的接口实现,然后通过动态代理获取接口的参数列表并触发MapperMethod中的execute方法执行SQL。附上最后的执行方法:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (this.method.returnsMany()) {
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

以上就是简单的mybatis执行过程,只是一个大致的梳理。