首页 > 其他分享 >mybatis 查询原理

mybatis 查询原理

时间:2024-08-26 12:26:06浏览次数:9  
标签:var1 String Object 查询 ibatis apache org mybatis 原理

mybatis 所有操作都会通过 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);

    <T> Cursor<T> selectCursor(String var1);

    <T> Cursor<T> selectCursor(String var1, Object var2);

    <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);

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

  1. 获取 SqlSessionFactory(SqlSessionFactory 初始化)

    1. org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream),返回的是 DefaultSqlSessionFactory
    2. 里面再调用 parse 方法解析配置文件,构建一个 Configuration 对象(包括全局配置文件和mapper.xml映射文件)
      1. mapper 映射文件中的每个增删改查都会封装成一个 MappedStatement 对象,然后放到 mappedStatements里。mappedStatements 是一个 map,key 是 id,value 是 MappedStatement(configruation.mappedStatements
      2. 所有的 mapper 接口文件解析后的信息都放在 configruation.mapperRegistry 里。 mapperRegistry 里面还有个 knownMappers,这是一个 map 放着每个 mapper 接口和其代理对象的工厂对象,后面会根据这个创建 mapper 接口的代理对象(configruation.mapperRegistry.knownMappers
  2. 获取 SqlSession,里面包含了 Executor 和 Configuration

    1. org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(),返回的是 DefaultSqlSession

    2. org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#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 executor = this.configuration.newExecutor(tx, execType);
                  var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
              } catch (Exception var12) {
                  Exception e = var12;
                  this.closeTransaction(tx);
                  throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
              } finally {
                  ErrorContext.instance().reset();
              }
      
              return var8;
          }
      
    3. org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)

      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
              executorType = executorType == null ? this.defaultExecutorType : executorType;
              executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
              // 根据条件创建不同的 executor,默认是 Simple
              Object executor;
              if (ExecutorType.BATCH == executorType) {
                  executor = new BatchExecutor(this, transaction);
              } else if (ExecutorType.REUSE == executorType) {
                  executor = new ReuseExecutor(this, transaction);
              } else {
              	// 这个是默认的
                  executor = new SimpleExecutor(this, transaction);
              }
      		// 如果开启了缓存,再把 executor 包装一下,最终都是通过包装后的 executor 执行 db 操作
              if (this.cacheEnabled) {
                  executor = new CachingExecutor((Executor)executor);
              }
      		// 如果有插件,再包装一下,同上最终使用还是还是包装后的 executor
              Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
              return executor;
          }
      
  3. getMapper 获取代理对象

    1. org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper

      public <T> T getMapper(Class<T> type) {
      	return this.configuration.getMapper(type, this);
      }
      
    2. org.apache.ibatis.session.Configuration#getMapper

      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
          // 刚才说了初始化 SqlSessionFactoryBuilder 的时候设置了 configruation.mapperRegistry 并维护了 knownMappers
          // 这里 getMapper 的就是调用 MapperRegistry 的方法
          return this.mapperRegistry.getMapper(type, sqlSession);
      }
      
    3. org.apache.ibatis.binding.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 {
                  // 调用 newInstance 创建对象,这里就不继续点了
                  // 后面步骤也很清晰:通过JDK动态代理创建对象;代理对象里面含有 sqlSession(sqlSession 里面有 executor)
                  return mapperProxyFactory.newInstance(sqlSession);
              } catch (Exception var5) {
                  Exception e = var5;
                  throw new BindingException("Error getting mapper instance. Cause: " + e, e);
              }
          }
      }
      
  4. 根据 Mapper 获取到了对象,就该调用里面的方法了,比如 selectById,现根据方法名判断是否是 Object 的方法,比如 toString、equlas 等,不是 Object 的方法,就把当前方法包装成一个 MapperMethod,传入 SqlSession 来执行这个方法 mapperMethod.execute(this.sqlSession, args)

    public Object execute(SqlSession sqlSession, Object[] args) {
            Object result;
            Object param;
        	// command 有两个属性
        	// 属性1 String name:mapper xml 的 id,也就是 MappedStatement 的 id,也就是 mapper 接口的方法全限定名
        	// 属性2 SqlCommandType type:语句的类型,增删改成哪一种,是一个枚举
            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()) { // 返回 map 怎么执行
                        result = this.executeForMap(sqlSession, args);
                    } else if (this.method.returnsCursor()) { // 返回游标怎么执行
                        result = this.executeForCursor(sqlSession, args);
                    } else {
                        // 处理参数,如果只有一个参数直接返回,如果是多个就返回一个 map,param 里面都是参数信息
                        param = this.method.convertArgsToSqlCommandParam(args);
                        // 参数参数 sqlSession 方法并调用(底层还是会调用 sqlSession.selectList,返回第一个)
                        result = sqlSession.selectOne(this.command.getName(), param);
                        if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                            result = Optional.ofNullable(result);
                        }
                    }
                    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;
            }
        }
    
  5. org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)

    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            // 先拿到 MappedStatement,也就是 xml 定义的东西
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            // MappedStatement 传给 executor 的 query 方法。this.wrapCollection(parameter):再次包装参数,集合,map,还是单个参数
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            Exception e = var9;
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    
        return var5;
    }
    
  6. org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler) 这个 query 做了什么

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    	// 这个里面是所有 sql 信息的对象,参数是什么,值是什么,语句是什么(boundSql.getSql能拿到真正执行的sql,这里调试比较有用)
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 构建一个缓存 key,下面查询的时候用这个缓存 key 去查,有就返回,没有就查库
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        // 再调用 query 方法
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
    
  7. org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.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 var8 = this.deferredLoads.iterator();
    
                while(var8.hasNext()) {
                    DeferredLoad deferredLoad = (DeferredLoad)var8.next();
                    deferredLoad.load();
                }
    
                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }
    
            return list;
        }
    }
    
  8. queryFromDatabase 方法又调用了 doQuery 方法org.apache.ibatis.executor.SimpleExecutor#doQuery

    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // java.sql 这个包下面的,再接近 jdbc 了
        Statement stmt = null;
    
        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            // 先创建 Statement 处理器
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 根据 Statement 处理器拿到 Statement
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            // 再把 Statement 传给 query
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }
    
        return var9;
    }
    
  9. 然后参数处理,执行查询,结果映射,关闭连接等

标签:var1,String,Object,查询,ibatis,apache,org,mybatis,原理
From: https://www.cnblogs.com/cyrushuang/p/18278160

相关文章

  • 【计算机组成原理】2.2.3_3 补码加减运算电路
    2.2.3_3补码加减运算电路00:00各位同学大家好,在这个视频中我们会探讨怎么用电路去实现补码的加减运算。在之前的学习中我们知道了N比特加法器它的一个实现原理,给加法器输入N比特的被加数A以及N比特的加数B,同时加法器还可以接收来自于更低位的进位信息,经过加法器的电路处......
  • 在 SQLAlchemy 中实现数据处理的时候,实现表自引用、多对多、联合查询,有序id等常见的一
    有时候,我们在使用SQLAlchemy操作某些表的时候,需要使用外键关系来实现一对多或者多对多的关系引用,以及对多表的联合查询,有序列的uuid值或者自增id值,字符串的分拆等常见处理操作。1、在SQLAlchemy中定义具有嵌套children关系的表要在SQLAlchemy中定义具有嵌套children关系......
  • oracle 查询表空间,新建表空间,赋权表空间,扩展表空间
    oracle查看表空间的方法一、Oracle查询表空间占用情况SELECTa.tablespace_name"表空间名",       total"表空间大小",       free"表空间剩余大小",       (total-free)"表占用空间大小",       ROUND((total-free)/total*100,2......
  • MySQL空间管理:查询、优化与碎片清理
    1.查询MySQL表空间和磁盘碎片查询表空间使用情况使用以下SQL语句可以查看数据库中各个表的表空间使用情况,包括数据大小、索引大小和空闲空间(碎片):SELECTtable_schemaAS`Database`,table_nameAS`Table`,ROUND(data_length/1024/1024,2)AS......
  • go连接MySQL数据库并查询数据
    前言要在Goland中连接数据库并查询数据,你可以按照以下步骤进行操作:提示:以下是本篇文章正文内容,下面案例可供参考一、导入所需的数据库驱动程序在你的Go代码中使用import语句导入所需的数据库驱动程序。例如,如果你使用的是MySQL数据库,可以使用以下语句导入MySQL驱动程序......
  • 10、Flink动态表之 DataStream 上的关系查询详解
    a)DataStream上的关系查询下表比较了传统的关系代数和流处理与输入数据、执行和输出结果的关系。关系代数/SQL流处理关系(或表)是有界(多)元组集合。流是一个无限元组序列。对批数据(例如关系数据库中的表)执行的查询可以访问完整的输入数据。流式查询在启动时不能访问......
  • 红黑树原理详解
    文章目录红黑树原理详解一、引言二、红黑树的基本性质1、基本性质2、红黑树的效率三、红黑树的操作1、插入操作1.1、插入节点1.2、调整颜色和结构1.3、修复2、删除操作2.1、删除节点2.2、调整颜色和结构2.3、修复四、总结红黑树原理详解一、引言红黑树(Red-Blac......
  • 效率神器!一款方便、快捷的数据库文档查询、导出工具!
    大家好,我是Java陈序员。之前给大家推荐过一款简洁好用的数据库表结构文档生成器。永不生锈的螺丝钉!一款简洁好用的数据库表结构文档生成器今天,再给大家介绍一款开源的数据库操作工具,功能更加强大!支持文档导出、SQL查询、代码生成...以及十几种实用的工具!关注微信公众号:【Jav......
  • 2-网络攻击原理与常用方法
    2.1网络攻击概述1)概念:指损害网络系统安全属性的危害行为。危害行为导致网络系统的机密性、完整性、可用性、可控性、真实性、抗抵赖性等受到不同程度的破坏。常见的危害行为有四个基本类型:信息泄漏攻击完整性破坏攻击拒绝服务攻击非法使用攻击自治主体:攻击者初始化......
  • 【VUE声明式导航跳转如何传参】router-link查询参数传参&动态路由传参
    VUE声明式导航跳转如何传参文章目录VUE声明式导航跳转如何传参前言一、查询参数传参语法实现步骤1.实现【首页】和【搜索页】的基础点击功能2.实现【首页】向【搜索页】跳转时的传参功能JS中如何获取传值二、动态路由传参语法实现步骤1.实现首页和搜索页的基础功能2.......