iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >浅谈MyBatis 如何执行一条 SQL语句
  • 806
分享到

浅谈MyBatis 如何执行一条 SQL语句

2024-04-02 19:04:59 806人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录前言 基础组件 工作流程 初步使用 详细流程 获取 MapperProxy 对象 缓存执行方法 构造参数 获取需要执行的 sql 对象 执行 SQL 语句 总结 前言 myba

前言

mybatis 是 Java 开发中比较常用的 ORM 框架。在日常工作中,我们都是直接通过 Spring Boot 自动配置,并直接使用,但是却不知道 Mybatis 是如何执行一条 SQL 语句的,而这篇文章就是来揭开 Mybatis 的神秘面纱。

基础组件

我们要理解 Mybatis 的执行过程,就必须先了解 Mybatis 中都有哪一些重要的类,这些类的职责都是什么?

SqlSession

我们都很熟悉,它对外提供用户和数据库之间交互需要使用的方法,隐藏了底层的细节。它默认是实现类是 DefaultSqlSession

Executor

这个是执行器,SqlSession 中对数据库的操作都是委托给它。它有多个实现类,可以使用不同的功能。

Configuration

它是一个很重要的配置类,它包含了 Mybatis 的所有有用信息,包括 xml 配置,动态 sql 语句等等,我们到处都可以看到这个类的身影。

MapperProxy

这是一个很重要的代理类,它代理的就是 Mybatis 中映射 SQL 的接口。也就是我们常写的 Dao 接口。

工作流程

初步使用

首先,我们需要得到一个 SqlSessionFactory 对象,该对象的作用是可以获取 SqlSession  对象。


// 读取配置
InputStream resourceAsStream = Resources.getResourceAsStream("config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 创建一个 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);

当我们得到一个 SqlSessionFactory 对象之后,就可以通过它的 openSession 方法得到一个 SqlSession 对象。


 SqlSession sqlSession = sqlSessionFactory.openSession(true);

最后,我们通过 SqlSession 对象获取 Mapper ,从而可以从数据库获取数据。


// 获取 Mapper 对象
HeroMapper mapper = sqlSession.getMapper(HeroMapper.class);
// 执行方法,从数据库中获取数据
Hero hero = mapper.selectById(1);

详细流程

获取 MapperProxy 对象

我们现在主要关注的就是 getMapper 方法,该方法为我们创建一个代理对象,该代理对象为我们执行 SQL 语句提供了重要的支持。


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

getMapper  方法里面委托 Configuration 对象去获取对应的 Mapper 代理对象,之前说过 Configuration 对象里面包含了 Mybatis 中所有重要的信息,其中就包括我们需要的 Mapper 代理对象,而这些信息都是在读取配置信息的时候完成的,也就是执行sqlSessionFactoryBuilder.build 方法。


// Configuration 对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperReGIStry.getMapper(type, sqlSession);
}

我们可以看到它又将获取 Mapper 代理对象的操作委托给了 MapperRegistry 对象(搁着俄罗斯套娃呢?),这个 MapperRegistry 对象里面就存放了我们想要的 Mapper 代理对象,如果你这么想,就错了,实际上,它存放的并不是我们想要的 Mapper 代理对象,而是 Mapper 代理对象的工厂,Mybatis 这里使用到了工厂模式。


public class MapperRegistry {

  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
}

我只保留了 getMapper 方法和 addMapper 方法。

在 getMapper 方法中,它获取的是 MapperProxyFactory 对象,我们通过名称可以得出这是一个 Mapper 代理对象工厂,但是我们是要得到一个 MapperProxy 对象,而不是一个工厂对象,我们再来看 getMapper 方法,它通过 mapperProxyFactory.newInstance 来创建代理对象。


protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

创建了一个 MapperProxy 对象,并且通过 Proxy.newProxyInstance 方法(不会还有人不知道这是 jdk 动态代理吧),创建一个代理对象处理,这个代理对象就是我们想要的结果。这里没有体现出来代理了哪个对象啊?其实 mapperInterface 这是一个成员变量,它引用了需要被代理的对象。而这个成员变量实在创建 MapperProxyFactory 对象的时候赋值的,所以我们每一个需要被代理的接口,在 Mybatis 中都会为它生成一个 MapperProxyFactory 对象,该对象的作用就是为了创建所需要的代理对象。

缓存执行方法

当我们获取到代理对象 mapper 之后,就可以执行它里面的方法。
这里使用一个例子:


// Myabtis 所需要的接口
public interface HeroMapper {
    Hero selectById(Integer id);
}

// HeroMapper 接口所对应的 xml 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "Http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test.HeroMapper">
    <select id="selectById" resultType="test.Hero">
        select * from hero where id = #{id}
    </select>
</mapper>

我们执行 selectById 方法,获取一个用户的信息。


// 获取 Mapper 对象
HeroMapper mapper = sqlSession.getMapper(HeroMapper.class);
// 执行方法,从数据库中获取数据
Hero hero = mapper.selectById(1);

通过上面的解析已经知道,这里的 mapper 是一个代理对象的引用,而这个代理类则是 MapperProxy,所以我们主要是去了解 MapperProxy 这个代理类做了什么事情。


public class MapperProxy<T> implements InvocationHandler, Serializable {
    
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclarinGClass())) {
        return method.invoke(this, args);
      } else {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
      return methodCache.computeIfAbsent(method, m -> {
           return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
  }
    
  private static class PlainMethodInvoker implements MapperMethodInvoker {
      private final MapperMethod mapperMethod;

      public PlainMethodInvoker(MapperMethod mapperMethod) {
          super();
          this.mapperMethod = mapperMethod;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
          return mapperMethod.execute(sqlSession, args);
      }
  }
}

代理对象执行方法时都是直接执行 invoke() 方法,在这个方法中,我们主要就看一条语句 cachedInvoker(method).invoke(proxy, method, args, sqlSession);

我们首先看 cachedInvoker 方法,它的参数是 Method 类型,所以这个 method 表示的就是我们执行的方法 HeroMapper.selectById,它首先从缓存中获取是否之前已经创建过一个该方法的方法执行器 PlainMethodInvoker 对象,其实这只是一个包装类,可有可无,在工程上来说,有了这个包装类,会更加易于维护。而这个执行器里面只有一个成员对象,这个成员对象就是 MapperMethod,并且这个 MapperMethod 的构造函数中需要传递  HeroMapper、HeroMapper.selectById、Cofiguration 这三个参数。

以上步骤都执行完成之后,接下来我们可以看到执行了 PlainMethodInvoker 的 invoke 方法,而它又将真正的操作委托给了 MapperMethod,执行 MapperMethod 下的 execute 方法,这个方法就是本文章的重点所在。

构造参数

从上面的解析可以知道,最后会执行到这个方法之中。


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

这个方法中,我们可以看到熟悉的几个关键字:select、update、delete、insert,这个就是为了找到执行方式,我们因为是 select 语句,所以分支会走向 select,并且最终会执行到 sqlSession.selectOne 方法中,所以最终饶了一大圈,依然还是回到了我们一开始就提到的 SqlSession 对象中。
在这个方法中,首先会构造参数,也就是我们看到的 convertArgsToSqlCommandParam 方法,它的内部执行方式是按照如下方式来转换参数的:

使用 @param 自定义命名
amethod(@Param int a, @Param int b)  则会构造 map  ->  [{"a", a_arg}, {"b", b_arg}, {"param1",  a_arg}, {"param2", b_arg}],a 和 param1 是对参数 a 的命名,a_arg 是传递的实际的值。
虽然只有两个参数,但是最后却会在 Map 存在四个键值对,因为 Mybatis 最后自己会生成以 param 为前缀的参数名称,名称按照参数的位置进行命名。

不使用 @param

amethod(int a, int b),则会构造 map -> [{"arg0", a_arg}, {"arg1", b_arg}, {"param1", a_arg}, {"param2", b_arg}],因为没有对参数进行自定义命名,所以 Myabtis 就对参数取了一个默认的名称,以 arg 为前缀,位置为后缀进行命名。

在参数只有一个,并且参数为集合的情况下,会存放多个键值对:

  • amethod(Collection<Integer> a),这种情况下,会构造 map -> [{"arg0", a_arg}, {"collection", a_arg}]
  • amethod(List<Integer> a),这种情况下,会构造 map -> [{"arg0", a_arg}, {"collection", a_arg}, {"list", a_arg}]
  • amethod(Integer[] a),这种情况下,会构造 map -> [{"arg0", a_arg}, {"array", a_arg}]
  • 但是,如果有两个参数,那么就不会这么存放,而是按照常规的方式:
  • amethod(List<Integer> a,List<Integer> b)  则会构造 map -> [{"arg0", a_arg}, {"arg1", b_arg}, {"param1", a_arg}, {"param2", b_arg}]
  • amethod(List<Integer> a,int b)  则会构造 map -> [{"arg0", a_arg}, {"arg1", b_arg}, {"param1", a_arg}, {"param2", b_arg}]

不会作为参数的对象
在 Mybatis 中有两个特殊的对象:RowBounds、ResultHandler,这两个对象如果作为参数则不会放入到 map 中,但是会占据位置。

amethod(int a,RowBounds rb, int b),这种情况下,会构造 map -> [{"arg0", a_arg}, {"arg2", b_arg}, {"param1", a_arg}, {"param2", b_arg}]

注意这里的 b 参数的命名分别是 arg2 和 param2,arg2 是因为它的位置在参数的第 3 位,而 param2 则是因为它是第 2 个有效参数。

获取需要执行的 SQL 对象

参数构造完成之后,我们就需要寻找需要执行的 SQL 语句了。


@Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

这里的 statement 虽然是 String 类型的,但是它并不是真正的 SQL 语句,它是一个寻找对应 MapperStatement 对象的名称,在我们的例子中,它就是 test.HeroMapper.selectById ,Mybatis 通过这个名称可以寻找到包含了 SQL 语句的对象。

我们跟踪代码的执行,最后会来到下面这个方法,这是一个包含三个参数的重载方法。


@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

在第四行代码中,可以得知它通过 statement 从 Configuration 对象中获取了一个 MapperStatement 对象, MapperStatement 对象包含的信息是由 <select>、<update>、<delete> 、<insert> 元素提供的,我们在这些元素中定义的信息都会保存在该对象中,如:Sql 语句、resultMap、fetchSize 等等。

执行 SQL 语句

获取到包含 SQL 语句信息的对象之后,就会交给 Execute 执行器对象去执行后续的处理,也就是 executor.query 方法。


@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

获取需要自行的 Sql 语句,然后创建一个缓存使用的 key,用于二级缓存。


@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    // ....
    // 跟缓存有关,如果缓存中存在数据,则直接从缓存中返回,否则从数据库中查询
 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
 return list;
}

最后会执行到一个 doQuery 方法


@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}

这段代码创建了一个 Statement 对象的处理器 StatementHandler,这个处理器主要的工作就是完成 JDBC 中 PrepareStatement 对象的一些准备工作,包括:创建 PrepareStatement 对象,设置需要执行的 sql 语句,为 sql 语句中的参数赋值。完成这些工作之后,就开始从数据库获取数据了。


@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
}

第四行代码即执行对应的 Sql 查询,后续则是对结果进行处理。

总结

Mybatis 通过 MapperProxy 代理了我们的 Dao 接口类,以此来帮助我们执行预定义的 Sql 语句,通过 Cache 来缓存对应的执行结果,通过 StatementHandler  创建 PrepareStatement 对象,通过 jdbc 执行 SQL 操作。

到此这篇关于浅谈MyBatis 如何执行一条 SQL语句的文章就介绍到这了,更多相关MyBatis 执行SQL语句内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 浅谈MyBatis 如何执行一条 SQL语句

本文链接: https://www.lsjlt.com/news/125983.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅谈MyBatis 如何执行一条 SQL语句
    目录前言 基础组件 工作流程 初步使用 详细流程 获取 MapperProxy 对象 缓存执行方法 构造参数 获取需要执行的 SQL 对象 执行 SQL 语句 总结 前言 Myba...
    99+
    2022-11-12
  • Java MyBatis是如何执行一条SQL语句的
    目录背景阅读环境阅读过程加载XML的过程创建Mapper获得一个Mapper执行一个Mapper的方法结论背景 在前两天的一次面试中,面试官问了一个和标题一样的问题,由于一直认为My...
    99+
    2022-11-13
  • Java MyBatis是怎么执行一条SQL语句的
    今天小编给大家分享一下Java MyBatis是怎么执行一条SQL语句的的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下...
    99+
    2023-07-02
  • 一条SQL语句执行过程
    目录一、MySQL 体系架构- 连接池组件- 缓存组件- 分析器- 优化器- 执行器二、写操作执行过程三、读操作执行过程四、SQL执行顺序一、MySQL 体系架构 - 连接池组件 ...
    99+
    2022-11-13
  • 一条SQL查询语句是如何执行的?
    导读 Mysql在中小型企业中是个香饽饽,目前主流的数据库之一,几乎没有一个后端开发者不会使用的,但是作为一个老司机,仅仅会用真的不够。 今天陈某透过一个简单的查询语句来讲述在Mysql内部的执行过程。 selec&...
    99+
    2019-06-13
    一条SQL查询语句是如何执行的?
  • 一条SQL更新语句是如何执行的
    文章首发于公众号「蝉沐风」,认真写好每一篇文章,欢迎大家关注交流 这是图解MySQL的第2篇文章,这篇文章会通过一条SQL更新语句的执行流程让大家清楚地明白: 什么是InnoDB页?缓存页又是什么?为什么这么设计? 什么是表空间?不...
    99+
    2014-08-03
    一条SQL更新语句是如何执行的
  • 怎么执行一条SQL更新语句
    这篇文章给大家分享的是有关怎么执行一条SQL更新语句的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、前言前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模...
    99+
    2022-10-18
  • 一条SQL语句在MySQL中是如何执行的
    今天就跟大家聊聊有关一条SQL语句在MySQL中是如何执行的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。前言最近开始在学习mysql相关知识,自己...
    99+
    2022-10-19
  • MySQL中一条SQL查询语句是如何执行的
    目录前言1. 处理连接1.1 客户端和服务端的通信方式1.1.1 TCP/IP协议1.1.2 UNIX域套接字1.1.3 命名管道和共享内存1.2 权限验证1.3 查看MySQL连接...
    99+
    2022-11-13
  • 一条SQL语句在MySQL中怎么执行的
    小编给大家分享一下一条SQL语句在MySQL中怎么执行的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一 MySQL 基础架构分...
    99+
    2022-10-18
  • 一条SQL语句执行过程时怎样的
    本篇内容主要讲解“一条SQL语句执行过程时怎样的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“一条SQL语句执行过程时怎样的”吧!一、MySQL 体系架构- 连接池组件负责与客户端的通信,是半双...
    99+
    2023-06-29
  • 一条SQL更新语句的执行过程解析
    目录一、执行过程二、日志模块1、物理日志redo logredo log的使用场景redolog配置2、逻辑日志binlog两阶段提交binlog使用场景前言: 上一篇文章讲解了SQ...
    99+
    2022-11-13
  • 分析mysql中一条SQL查询语句是如何执行的
    目录一、MySQL 逻辑架构概览二、连接器(Connector)三、查询缓存(Query Cache)四、解析器(Parser)五、优化器(Optimizer)六、执行器七、小结一、MySQL 逻辑架构概览 MySQ...
    99+
    2022-05-30
    mysql 查询 查询 执行过程
  • 一条SQL更新语句的执行过程是什么
    这篇“一条SQL更新语句的执行过程是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“一条SQL更新语句的执行过程是什么”文...
    99+
    2023-06-30
  • 如何优化SQL语句的心得浅谈
    (1)选择最有效率的表名顺序(只在基于规则的优化器中有效):Oracle的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 driving tab...
    99+
    2022-11-15
    优化 SQL语句
  • 02.日志系统:一条SQL更新语句是如何执行的?
       我们还是从一个表的一条更新语句说起,我们创建下面一张表: create table T(ID int primary key, c int);    如果要将ID=2这一行c的值加1,SQL可以这么写: update T set...
    99+
    2015-02-19
    02.日志系统:一条SQL更新语句是如何执行的?
  • 01 | 基础架构:一条SQL查询语句是如何执行的?
    ...
    99+
    2019-08-14
    01 | 基础架构:一条SQL查询语句是如何执行的?
  • 02 | 日志系统:一条SQL更新语句是如何执行的?
    极客时间专栏《MySQL实战45讲》的阅读笔记。 ...
    99+
    2015-04-30
    02 | 日志系统:一条SQL更新语句是如何执行的?
  • MySQL中一条update语句是如何执行的
    目录前言前期准备SQL语句的执行过程Update语句分析redo log(重做日志)binlog(归档日志)‍♀️总结前言 上一篇文章说完MySQL的事务和锁了,这次来详细介...
    99+
    2022-11-13
  • mdb中如何执行sql语句
    在MDB(Microsoft Access数据库)中执行SQL语句,可以通过以下步骤完成:1. 打开Microsoft Access...
    99+
    2023-10-12
    mdb sql
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作