Mybatis系列之核心分析

news2024/11/27 5:39:39

文章目录

  • 一、Mybatis的前世
    • 1、简述:
    • 2、什么是JDBC:
    • 3、什么是驱动:
    • 4、JDBC的开发步骤:
      • 《1》注册和加载数据库驱动
      • 《2》获得数据库连接
      • 《3》获得语句执行对象,然后执行SQL语句,获取执行结果,最后释放资源
  • 二、Mybatis的核心组成
    • SqlSessionFactoryBuilder:
    • SqlSessionFactory:
    • SqlSession(会话):
    • SQL Mapper(映射器):
    • MappedStatement:
    • Executor:
  • 三、Mybatis的工作原理
    • 1、MyBatis内部核心流程
    • 2、Mybatis层次结构
  • 四、Mybatis的插件机制
    • 1. 自定义插件
      • 1.1 拦截器(Interceptor)
    • 1.2 创建Interceptor实现类
    • 1.3 MyBatis拦截器注册的三种方式
      • 1.XML注册
      • 2.配置类注册
      • 3.注解方式
    • 2. 插件实现原理
      • 2.1 初始化操作
      • 2.2 如何创建代理对象
        • 2.2.1 Executor
        • 2.2.2 StatementHandler
        • 2.2.3 ParameterHandler
        • 2.2.4 ResultSetHandler
      • 2.3 执行流程
      • 2.4 多拦截器
    • 3. PageHelper分析
      • 3.1 PageHelper的应用
        • 3.1.1添加依赖
        • 3.1.2 在项目添加配置
        • 3.1.3 分页操作
        • 3.1.3 分页操作
      • 3.2 实现原理剖析
    • 4.应用场景分析



一、Mybatis的前世

Mybatis的前世:JDBC数据库编程

1、简述:

Mybatis是一个优秀的持久层框架。它对JDBC的操作数据库的过程进行封装。JDBC是连接数据库和Java程序的桥梁,通过JDBC API可以方便地实现对各种主流数据库的操作。

2、什么是JDBC:

Java数据库连接Java Database Connectivity,简称JDBC,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC是一类接口,制定了统一访问各类关系数据库的标准接口。如果没有JDBC这个接口标准的存在,程序员面对各类数据库的操作将会变得十分复杂。

3、什么是驱动:

JDBC是接口,驱动是接口的实现类,没有驱动将无法完成数据库连接,从而不能操作数据库。每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都是由数据库生成厂商提供。
驱动程序可以保证两个设备进行通信,它需要满足一定通信数据格式,数据格式有设备提供商规定,设备提供商为设备提供驱动软件,通过驱动程序可以与设备进行通信。

4、JDBC的开发步骤:

我们需要访问数据库时,首先要加载数据库驱动,只需加载一次,然后在每次访问数据库时创建一个Connection实例,获取数据库连接;获取数据库连接后,执行对应SQL,最后完成数据库操作时释放与数据库之间的连接。具体开发步骤如下:

《1》注册和加载数据库驱动

此步骤的目的是告知JVM使用的是哪一个数据库的驱动。Java加载数据库驱动的方法是调用class类的静态方法forName(),语法格式如下:
Class.forName(String driverManager)
例如,加载MySQL数据库驱动如下:
在这里插入图片描述

如果加载成功,会将加载的驱动类注册给DriverManager;如果加载失败,会抛出ClassNotFoundException异常。需要注意的是,要在项目中导入mysql-connection-java的jar包,方法是在项目中建立lib目录,在其下放入jar包。

《2》获得数据库连接

此步骤需要使用JDBC中的类,完成对数据库的连接。加载完数据库驱动后,就可以建立数据库的连接了,需要使用DriverManager类的静态方法getConnection()方法来实现。代码如下:
在这里插入图片描述

《3》获得语句执行对象,然后执行SQL语句,获取执行结果,最后释放资源

通过连接对象获取对SQL语句的执行者对象,利用执行者对象,向数据库发送并执行sql语句,然后获取到数据库的执行后的结果

建立了连接之后,就可以使用Connection接口的createStatement()方法来获取Statement对象,也可以调用prepareStatement()方法获得PrepareStatement对象,通过executeUpdate()方法来执行SQL语句。

以插入为例,我们可以使用Statement接口中的executeUpdate()方法,如下:
在这里插入图片描述

还可以使用PreparedStatement接口中的executeUpdate()方法,如下:
在这里插入图片描述


二、Mybatis的核心组成

SqlSessionFactoryBuilder:

根据配置或者代码生成SqlSessionFactory,采用的是分步构建的Builder模式

SqlSessionFactory:

SqlSessionFactory 是 MyBatis 的核心组件之一,它是应用程序与 MyBatis 数据库之间的一个交互对象。SqlSessionFactory 依据配置文件以及 Java API 的方式生成 SqlSession 对象,SqlSession 对象为执行 SQL 命令提供了相关接口。SqlSessionFactory 是 SqlSession 的工厂类,SqlSessionFactory 采用工厂模式设计,实现了 MyBatis 应用程序与数据库之间的解耦。

SqlSession(会话):

SqlSession 是 MyBatis 的核心组件之一,它是 Session(会话)级别的缓存,用于与数据库进行交互。SqlSession 对象提供了一系列操作数据库的 API,包括查询、插入、更新和删除数据等操作。SqlSession 作为 MyBatis 应用程序与数据库之间沟通的桥梁,SqlSession 可以被应用程序的各个层访问。

SQL Mapper(映射器):

Mapper 是 MyBatis 中抽象出来的一个概念,表示一类 DAO 类的接口。每个 Mapper 接口中定义了对应 SQL 操作的方法。Mapper 接口中的方法会被 MyBatis 解析成 MappedStatement 对象,与该 SQL 语句对应。Mapper 接口的定义使得应用程序开发者可以在无需编写具体的 SQL 语句的情况下,对数据库进行操作。

MappedStatement:

MappedStatement 是 MyBatis 用于存储 SQL 语句、入参、出参等相关信息的核心组件。在 MyBatis 中,Mapper 接口中的每个方法都会被解析成一个 MappedStatement 对象。MappedStatement 对象是一个有状态(stateful)对象,包含了 SQL 语句的语法、入参映射、结果映射等相关信息。

Executor:

Executor 是 MyBatis 中的核心组件之一,它主要负责查询语句的执行和结果的返回。Executor 的实现类有三种:SimpleExecutor、ReuseExecutor、BatchExecutor,分别对应于简单执行器、重复执行器和批处理执行器。Executor 提供了追踪和缓存查询结果的功能,能够提高执行效率。

以上这些组件相互配合,实现了 MyBatis 框架的核心功能,为开发者提供了便捷、高效的数据库操作方式,避免了手写 SQL 语句和 JDBC 操作的繁琐。


三、Mybatis的工作原理

1、MyBatis内部核心流程

在这里插入图片描述
图中流程就是MyBatis内部核心流程,每一步流程的详细说明如下文所述:

(1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。

(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

(3)构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。

(4)创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。

(5)Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。

(6)MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。

(7)输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。

(8)输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。

2、Mybatis层次结构

在这里插入图片描述
SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能

Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护

StatementHandler :封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。

ParameterHandler: 负责对用户传递的参数转换成JDBC Statement 所需要的参数,

ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

TypeHandler :负责java数据类型和jdbc数据类型之间的映射和转换

MappedStatement: MappedStatement维护了一条<select|update|delete|insert>节点的封装,

SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回

BoundSql :表示动态生成的SQL语句以及相应的参数信息

Configuration:MyBatis所有的配置信息都维持在Configuration对象之中。


四、Mybatis的插件机制

插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来扩展或者改变原有的功能,MyBatis中也提供的有插件,虽然叫插件,但是实际上是通过拦截器(Interceptor)实现的,在MyBatis的插件模块中涉及到责任链模式和JDK动态代理,这两种设计模式的技术知识也是大家要提前掌握的。

将从以下4点介绍:

  1. 自定义插件
    1.1 认识拦截器
    1.2 创建Interceptor实现类
    1.3 MyBatis拦截器注册的三种方式
  2. 插件实现原理
    2.1 初始化操作
    2.2 如何创建代理对象
    2.3 执行流程
    2.4 多拦截器
  3. PageHelper分析
    1.1 PageHelper的应用
    1.2 配置拦截器
  4. 应用场景分析

1. 自定义插件

1.1 拦截器(Interceptor)

Mybatis拦截器设计的初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。通过Mybatis拦截器我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。所以Mybatis拦截器的使用范围是非常广泛的。

Mybatis里面的核心对象还是比较多,如下:
在这里插入图片描述
Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截
Executor、ParameterHandler、StatementHandler、ResultSetHandler 四个对象里面的方法。

  • Executor
    Mybatis中所有的Mapper语句的执行都是通过Executor进行的。Executor是Mybatis的核心接口。从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。Executor里面常用拦截方法如下所示。
public interface Executor {

   ...

    /**
     * 执行update/insert/delete
     */
    int update(MappedStatement ms, Object parameter) throws SQLException;

    /**
     * 执行查询,先在缓存里面查找
     */
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

    /**
     * 执行查询
     */
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

    /**
     * 执行查询,查询结果放在Cursor里面
     */
    <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

    ...


}
  • ParameterHandler
    ParameterHandler用来设置参数规则,当StatementHandler使用prepare()方法后,接下来就是使用它来设置参数。所以如果有对参数做自定义逻辑处理的时候,可以通过拦截ParameterHandler来实现。ParameterHandler里面可以拦截的方法解释如下:
public interface ParameterHandler {

 ...

 /**
  * 设置参数规则的时候调用 -- PreparedStatement
  */
 void setParameters(PreparedStatement ps) throws SQLException;

 ...


}
  • StatementHandler
    StatementHandler负责处理Mybatis与JDBC之间Statement的交互。
public interface StatementHandler {

    ...

    /**
     * 从连接中获取一个Statement
     */
    Statement prepare(Connection connection, Integer transactionTimeout)  throws SQLException;

    /**
     * 设置statement执行里所需的参数
     */
    void parameterize(Statement statement) throws SQLException;

    /**
     * 批量
     */
    void batch(Statement statement)  throws SQLException;

    /**
     * 更新:update/insert/delete语句
     */
    int update(Statement statement) throws SQLException;

    /**
     * 执行查询
     */
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;

    <E> Cursor<E> queryCursor(Statement statement) throws SQLException;

    ...

}

一般只拦截StatementHandler里面的prepare方法。
在Mybatis里面RoutingStatementHandler是SimpleStatementHandler(对应Statement)、PreparedStatementHandler(对应PreparedStatement)、CallableStatementHandler(对应CallableStatement)的路由类,所有需要拦截StatementHandler里面的方法的时候,对RoutingStatementHandler做拦截处理就可以了,如下的写法可以过滤掉一些不必要的拦截类。

@Intercepts({
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
})
public class TableShardInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof RoutingStatementHandler) {
            // TODO: 做自己的逻辑
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
        return (target instanceof RoutingStatementHandler) ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
  • ResultSetHandler
    ResultSetHandler用于对查询到的结果做处理。所以如果你有需求需要对返回结果做特殊处理的情况下可以去拦截ResultSetHandler的处理。ResultSetHandler里面常用拦截方法如下:
public interface ResultSetHandler {

    /**
     * 将Statement执行后产生的结果集(可能有多个结果集)映射为结果列表
     */
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

    /**
     * 处理存储过程执行后的输出参数
     */
    void handleOutputParameters(CallableStatement cs) throws SQLException;

}

1.2 创建Interceptor实现类

我们创建的拦截器必须要实现Interceptor接口,Interceptor接口的定义为

在这里插入图片描述

在MyBatis中Interceptor允许拦截的内容是:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

我们创建一个拦截Executor中的query和close的方法:
在这里插入图片描述
在这里插入图片描述

1.3 MyBatis拦截器注册的三种方式

1.XML注册

xml注册是最基本的方式,是通过在Mybatis配置文件中plugins元素来进行注册的。一个plugin对应着一个拦截器,在plugin元素可以指定property子元素,在注册定义拦截器时把对应拦截器的所有property通过Interceptor的setProperties方法注入给拦截器。因此拦截器注册xml方式如下:
在这里插入图片描述

2.配置类注册

配置类注册是指通过Mybatis的配置类中声明注册拦截器,配置类注册也可以通过Properties类给Interceptor的setProperties方法注入参数。具体参考如下:
在这里插入图片描述

3.注解方式

通过@Component注解方式是最简单的方式,在不需要转递自定义参数时可以使用,方便快捷。
在这里插入图片描述

2. 插件实现原理

2.1 初始化操作

在这里插入图片描述
该方法用来解析全局配置文件中的plugins标签,然后对应的创建Interceptor对象,并且封装对应的属性信息。最后调用了Configuration对象中的方法。 configuration.addInterceptor(interceptorInstance)
在这里插入图片描述
通过这个代码我们发现我们自定义的拦截器最终是保存在了InterceptorChain这个对象中。而InterceptorChain的定义为
在这里插入图片描述

2.2 如何创建代理对象

在解析的时候创建了对应的Interceptor对象,并保存在了InterceptorChain中,那么这个拦截器是如何和对应的目标对象进行关联的呢?
首先拦截器可以拦截的对象是 Executor,ParameterHandler,ResultSetHandler,StatementHandler.那么我们来看下这四个对象在创建的时候又什么要注意的.

2.2.1 Executor

在装饰完二级缓存后会通过pluginAll来创建Executor的代理对象。
在这里插入图片描述
进入plugin方法中,然后进入到MyBatis给我们提供的Plugin工具类的实现 wrap方法中。
在这里插入图片描述
在这里插入图片描述

2.2.2 StatementHandler

 @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();
      // 注意,已经来到SQL处理的关键对象 StatementHandler >>
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 获取一个 Statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行查询
      return handler.query(stmt, resultHandler);
    } finally {
      // 用完就关闭
      closeStatement(stmt);
    }
  }

在进入newStatementHandler方法


  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 植入插件逻辑(返回代理对象)
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); --2.2.1 Executor 中一致
    return statementHandler;
  }

2.2.3 ParameterHandler

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在newParameterHandler的步骤我们可以发现代理对象的创建
在这里插入图片描述

2.2.4 ResultSetHandler

在上面的newResultSetHandler()方法中,也可以看到ResultSetHander的代理对象
在这里插入图片描述

2.3 执行流程

以Executor的query方法为例,当查询请求到来的时候,Executor的代理对象是如何处理拦截请求的呢?我们来看下。当请求到了executor.query方法的时候 实际是进入了 CachingExecutor 的query,这时候会被拦截
在这里插入图片描述
实际是进入了 CachingExecutor 的query,这时候会被拦截,然后会执行Plugin的invoke方法
在这里插入图片描述
然后进入interceptor.intercept 会进入我们自定义的 FirstInterceptor对象中
在这里插入图片描述

2.4 多拦截器

如果我们有多个自定义的拦截器,那么他的执行流程是怎么样的呢?比如我们创建了两个 Interceptor 都是用来拦截 Executor 的query方法,一个是用来执行逻辑A 一个是用来执行逻辑B的。

单个拦截器的执行流程
在这里插入图片描述

1、如果说对象被代理了多次,这里会继续调用下一个插件的逻辑,再走一次Plugin的invoke()方法。这里我们需要关注一下有多个插件的时候的运行顺序。
2、配置的顺序和执行的顺序是相反的。InterceptorChain的List是按照插件从上往下的顺序解析、添加的。
3、而创建代理的时候也是按照list的顺序代理。执行的时候当然是从最后代理的对象开始。
在这里插入图片描述
这个我们可以通过实际的案例来得到验证,最后来总结下Interceptor的相关对象的作用
在这里插入图片描述

3. PageHelper分析

3.1 PageHelper的应用

3.1.1添加依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
   	<version>4.1.6</version>
</dependency>

或者整合springboot

<!--  pagehelper分页插件  -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

3.1.2 在项目添加配置

在这里插入图片描述
在这里插入图片描述

3.1.3 分页操作

在这里插入图片描述
查看日志我们能够发现查询出来的只有5条记录
在这里插入图片描述

3.1.3 分页操作

通过MyBatis的分页插件的使用,我们发现我们仅仅是在 执行操作之前设置了一句
PageHelper.startPage(1,5);
并没有做其他操作,也就是没有改变任何其他的业务代码。这就是它的优点,那么我再来看下他的实现原理

3.2 实现原理剖析

在PageHelper中,肯定有提供Interceptor的实现类,通过源码我们可以发现是PageInterceptor,而且我们也可以看到在该方法头部添加的注解,声明了该拦截器拦截的是Executor的query方法

在这里插入图片描述
然后当我们要执行查询操作的时候,我们知道 Executor.query() 方法的执行本质上是执行 Executor的代理对象的方法。先来看下Plugin中的invoke方法
在这里插入图片描述
interceptor.intercept(new Invocation(target, method, args));方法的执行会进入到 PageInterceptor的intercept方法中.

4.应用场景分析

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1008900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

问道管理:机器人产业迎催化 黄金价格或将突破前高

昨日&#xff0c;沪指盘中震动下探&#xff0c;一度跌近1%逼近3100点&#xff0c;尾盘逐步止跌&#xff1b;深成指、创业板指均跌超1%。截至收盘&#xff0c;沪指跌0.45%报3123.07点&#xff0c;深成指跌1.14%报10255.87点&#xff0c;创业板指跌1.14%报2027.73点&#xff0c;科…

SpringBoot2.0(mybatis-plus初始使用)

目录 一&#xff0c;介绍二&#xff0c;SpringBoot2.x整合MybatisPlus Lombok2.1&#xff0c;添加依赖 pom2.2&#xff0c;配置数据库信息 application.properties2.3&#xff0c;工程结构初始化 三&#xff0c;创建接口返回统一对象四&#xff0c;创建bean五&#xff0c;创建…

iTOP-STM32MP157开发板Ubuntu镜像的烧写

由于 Ubuntu 镜像的烧写和之前的 QT 系统存在区别,QT 系统所使用的内核可以不用区分屏幕&#xff0c;而ubuntu 系统不同。所以我们在烧写镜像的时候需要修改对应的内核镜像&#xff0c;我们以烧写 ubuntu18 无桌面版本的镜像为例&#xff0c;镜像存放路径为“iTOP-STM32MP157 开…

Windows10/11强制删除多余的本地连接、以太网

如图进入到网络适配器准备删除多余的网络&#xff0c;发现无法删除&#xff0c;删除按钮是被禁用的。 解决办法 此电脑》右键》管理 找到对应连接下面的名称 设备管理器》网络适配器》Hyper-V Virtual Ethernet Adapter>右键》卸载设备 谨慎操作&#xff0c;卸载错的话…

基于视觉重定位的室内AR导航APP的大创项目思路(2):改进的项目思路——建图和定位分离

文章目录 一、建图二、定位首先是第一种方法&#xff1a;几何方法其次是第二种方法&#xff1a;图像检索方法最后是第三种方法&#xff1a;深度学习方法 前情提要&#xff1a; 是第一次做项目的小白&#xff0c;文章内的资料介绍如有错误&#xff0c;请多包含&#xff01; 一、…

递归学习——记忆化搜索

目录 ​编辑 一&#xff0c;概念和效果 二&#xff0c;题目 1.斐波那契数 1.题目 2.题目接口 3.解题思路 2.不同的路径 1.题目 2.题目接口 3.解题思路 3.最长增长子序列 1.题目 2.题目接口 3.解题思路 4.猜数字游戏II 1.题目 2.题目接口 3.解题思路 总结&a…

2.docker基础使用命令

请点击滑动滚轮&#xff1a;放大查看 PS:发现滚轮不能放大了&#xff0c;这傻B的csdn&#xff0c;越做越垃圾了。。。 来这个地址看吧&#xff1a;https://img-blog.csdnimg.cn/7a5eb5a1eca4484fa0faa73b398257bd.png&#xff0c;滑动滚轮放大 ​ 源文件&#xff1a; 点击下载…

快速幂 c++

一般大家写都是 int ans 1; for (int i 1; i < a; i )ans * x;时间复杂度 但是这对于我们还不够&#xff0c;我们要 首先我们得知道一个数学知识 那么求 就有以下递归式 a 能被2整除 a 不能被2整除 (这里a/2是整除) 所以每次都调用 不就是么 最后补充一个东西…

【Java从入门到精通】这也许就是Java火热的原因吧!

前言&#xff1a;Java是一种高级的、面向对象的、可跨平台的程序设计语言。Java根据技术类别可划分为以下几类&#xff1a;JavaSE&#xff08;Standard Edition&#xff0c;标准版&#xff09;&#xff1a;支持面向桌面、嵌入式和移动设备的应用程序开发&#xff1b;JavaEE&…

Apollo介绍和入门

文章目录 Apollo介绍配置中心介绍apollo介绍主流配置中心功能特性对比 Apollo简介 入门简单的执行流程Apollo具体的执行流程Apollo对象执行流程分步执行流程 核心概念应用&#xff0c;环境&#xff0c;集群&#xff0c;命名空间企业部署方案灰度发布全量发布 配置发布的原理发送…

MyBatis基础之概念简介

文章目录 基本概念1. 关于 MyBatis2. MyBatis 的体系结构3. 使用 XML 构建 SqlSessionFactory4. SqlSession5. 默认的别名6. 补充 [注意] 放前面前 很多人可能在使用 MyBatis-plus 进行代码开发&#xff0c;MyBatis的这部分内容是用来更好的讲述之后的内容。 基本概念 1. 关于…

无涯教程-JavaScript - ISODD函数

描述 如果数字为奇数,则ISODD函数返回TRUE,如果数字为偶数,则返回FALSE。 语法 ISODD (number) 争论 Argument描述Required/OptionalNumber 要测试的值或表达式。 如果number不是整数,则将其截断。 Required Notes 您可以在执行计算之前使用此功能测试单元格的内容。 如果…

微服务 第一章 Java线程池技术应用

系列文章目录 第一章 Java线程池技术应用 文章目录 系列文章目录[TOC](文章目录) 前言1、Java创建线程方式回顾1.1、继承Thread类(只运行一次)1.1.1、改造成主线程常驻&#xff0c;每秒开启新线程运行1.1.2、匿名内部类1.1.3、缺点1.1.4、扩展知识&#xff1a;Java内部类1.1.4…

Python 内置函数详解 (2) 逻辑运算

近期在外旅游,本篇是出发编辑的,准备定时发布用,不完整,旅游回来后再补充。 Python 内置函数 Python3.11共有75个内置函数,其来历和分类请参考:Python 新版本有75个内置函数,你不会不知道吧_Hann Yang的博客-CSDN博客https://blog.csdn.net/boysoft2002/article/detai…

SOLIDWORKS PDM—数据库的备份计划

SOLIDWORKS产品数据管理 (PDM) 解决方案可帮助您控制设计数据&#xff0c;并且从本质上改进您的团队就产品开发进行管理和协作的方式。使用 SOLIDWORKS PDM Professional&#xff0c;您的团队能够&#xff1a;1. 安全地存储和索引设计数据以实现快速检索&#xff1b;2. 打消关于…

Linux——进程间通信(管道及共享内存)

目录 0. 前言 1. 进程通信的目的 2. 进程通信发展及分类 3. 进程通信匿名管道 3.1 什么是管道&#xff1f; 3.2 匿名管道系统调用 3.3 fork后子进程继承&#xff08;基于内存级&#xff09; 3.4 站在文件描述符角度-深度理解管道 3.5 站在内核角度-管道本质 3.6 父子…

电动车彻底取代燃油车?瑞士限制,中国每天烧7辆,现实不乐观

随着新能源汽车在国内汽车市场的销量占比突破三成&#xff0c;业界对于电动汽车取代燃油车相当乐观&#xff0c;然而电动汽车存在的不少问题却不容忽视&#xff0c;这正逐渐成为电动汽车普及的巨大障碍。 电动汽车如今面临的问题不少&#xff0c;最让消费者吐槽的是充电问题&am…

python自学

自学第一步 第一个简单的基础&#xff0c;向世界说你好 启动python 开始 print是打印输出的意思&#xff0c;就是输出引号内的内容。 标点符号必须要是英文的&#xff0c;因为他只认识英文的标点符号。 exit&#xff08;&#xff09;推出python。 我们创建一个文本文档&…

删除数组中的重复项——双指针

双指针法&#xff0c;说是双指针其实就是在一个数组中定义两个数组下标变量&#xff0c;通过两个下标的移动和赋值来实现 代码&#xff1a; int removeDuplicates(int* nums, int numsSize){int left1; int right1; while(left<numsSize) {if(nums[left]!nums[left-1]){num…

耐蚀合金连续油管最新版 学习记录

声明 本文是学习GB-T 42858-2023 耐蚀合金连续油管. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了耐蚀合金连续油管的订货、材料、制造、检验试验、标记等。 本文件适用于油气井用耐蚀合金连续油管(以下简称"油管")…