浅入浅出MyBatis(二)简单实现MyBatis底层机制

news2025/1/12 10:35:16

MyBatis底层机制示意图

在这里插入图片描述

mybatis-config.xml

  • mybatis-config.xml 是MyBatis全局配置文件,在项目中只能有一份。
  • 通过该配置文件可以得到SqlSessionFactory对象

SqlSessionFactory

  • 通过SqlSessionFactory可以得到SqlSession,拿到SqlSession就可以操作数据库了。

SqlSession

SqlSession底层是Excutor执行器。

Excutor

  • Excutor是接口,定义了很多方法。
  • 有两个重要的实现类,基本执行器 BaseExcutor缓存执行器 CacheExcutor
    在这里插入图片描述

Mapped Statement

  • 对sql语句的参数及执行结果进行封装。

手写MyBatis框架

1. 思路分析

在这里插入图片描述

  • 传统方式,通过Connection连接获取PrepareStatement对象,就可以执行sql了
  • 使用MyBatis框架思想,则在执行sql之前,从mapper.xml中拿到sql和参数,结果类型。然后封装sql语句。执行sql之后,把得到结果封装到对应的类型之中。(动态代理实现)

2. 搭建Maven项目

配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>mybatis_edu</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--解析xml-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!--简化java bean开发-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
        </dependency>
    </dependencies>

</project>

阶段一 :读取配置文件,获取连接

在这里插入图片描述

(1)配置文件mybatis-config.xml简化版
<?xml version="1.0" encoding="UTF-8" ?>
<database>
 <!--配置数据库连接信息-->
 <property name="driver" value="com.mysql.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
 <property name="username" value="root"/>
 <property name="password" value="root1234"/>
</database>
(2)ConfigurationEdu,模拟Configuration
package com.mybatis.sqlsession;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 读取xml,建立连接
 */
public class ConfigurationEdu {
    // 类加载器 ,作用:可以通过类加载器加载配置文件得到对应的流
    private ClassLoader classLoader = ClassLoader.getSystemClassLoader();

    // 读取配置文件并处理
    public Connection build(String resource) {
        Connection connection = null;
        try {
            // 加载配置文件,获取对应inputStream流
            InputStream inputStream = classLoader.getResourceAsStream("mybatis-config.xml");

            // 解析xml dom4j
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            // 获取配置文件根元素
            Element root = document.getRootElement();
            connection = evalElement(root);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        return connection;
    }

    private Connection evalElement(Element node){
        String driver = "",url = "",username = "",password = "";
        Connection connection = null;
        if (!"database".equals(node.getName())){
            throw new RuntimeException("root 节点为 <database>");
        }
        // 遍历node节点下的字节点,获取属性值
        for (Object item : node.elements()){
            // e 就是 property
            Element e = (Element)item;
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            // 判断是否得到 name 和 value
            if( null == name || null == value){
                throw new RuntimeException("未设置name和 value值");
            }

            switch (name){
                case "url" :
                    url = value;
                    break;
                case "username":
                    username = value;
                    break;
                case "password" :
                    password = value;
                    break;
                case "driver":
                    driver = value;
                    break;
                default:
                    throw new RuntimeException("未匹配到属性值。");
            }
        }

        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return connection;
    }
}
(3)测试

测试一下,看看能不能获取到连接

在这里插入图片描述

阶段二:创建执行器Excutor

(1)通过Lombok创建Monster
package com.mybatis.entity;

import lombok.Data;

import java.util.Date;

@Data
public class Monster {
    private Integer id;
    private Integer age;
    private String name;
    private String email;
    private Date birthday;
    private double salary;
}
(2)Executor接口
package com.mybatis.sqlsession;

/**
 * 执行器接口 定义操作数据库的通用方法
 */
public interface Executor {

    <T> T query(String sql, Object... params);
}
(3)BaseExecutor
package com.mybatis.sqlsession;

import com.mybatis.entity.Monster;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 编写执行器
 *      输入sql语句,完成数据库操作
 */
public class BaseExecutor implements Executor{
	
    private ConfigurationEdu configuration = new ConfigurationEdu();

    @Override
    public <T> T query(String sql, Object... params) {
        Connection connection = getConnection();
        PreparedStatement pre = null;
        ResultSet resultSet = null;
        Monster monster = null;
        try {
            pre = connection.prepareStatement(sql);
            if (params != null || params.length > 0){
                for (int i = 0; i < params.length; i++) {
                    pre.setObject(i+1,params[i]);
                }
            }
            resultSet = pre.executeQuery();
            // 封装结果集,底层用反射,在这里先简化封装
            monster = new Monster();
            while(resultSet.next()){
                monster.setId(resultSet.getInt("id"));
                monster.setAge(resultSet.getInt("age"));
                monster.setBirthday(resultSet.getDate("birthday"));
                monster.setName(resultSet.getString("name"));
                monster.setEmail(resultSet.getString("email"));
                monster.setSalary(resultSet.getDouble("salary"));
            }
            return (T) monster;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (resultSet != null){
                    resultSet.close();
                }
                if (pre != null){
                    pre.close();
                }
                if (connection != null){
                    connection.close();
                }
            }catch (Exception e){
                throw new RuntimeException("资源关闭失败。");
            }

        }
    }

    private Connection getConnection(){
        Connection connection = configuration.build("mybatis-config.xml");
        return connection;
    }
}
(4) Junit测试

在这里插入图片描述

阶段三:编写SqlSession

在这里插入图片描述

(1)编写SqlSession
package com.mybatis.sqlsession;

public class SqlSession {
    private Executor executor = new BaseExecutor();
    private ConfigurationEdu configuration = new ConfigurationEdu();

    public <T> T selectOne(String sql,Object... object){
        return executor.query(sql,object);
    }
}
(2) Junit测试

在这里插入图片描述

阶段四 : 编写MonsterMapper接口与MonsterMapper.xml

(1)MonsterMapper接口
package com.mybatis.mapper;

import com.mybatis.entity.Monster;

/**
 * 声明对monster表的CRUD
 */
public interface MonsterMapper {
    Monster getMonsterById(Integer id);
}
(2)MonsterMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.mybatis.mapper.MonsterMapper">
	<select id="getMonsterById" resultType="com.mybatis.entity.Monster">
        select * from `monster` where id = ?
	</select>
</mapper>

阶段五 : 开发Function和MapperBean

在这里插入图片描述

传统方式 编写完接口之后,会编写实现类,调用实现类完成功能
MyBatis是通过XML配置文件,XML配置文件不可能像Java代码一样运行,因此需要一个类,封装Mapper接口中的方法,并去XML中找到对应的SQL保存到该对象中。

package com.mybatis.config;

/**
 * 记录Mapper.xml中对应的方法
 */
public class Function {

    private String sqlType; // insert、delete、update、select
    private String funName; // 方法名
    private String sql; // 执行sql
    private Object resultType; // 结果类型
    private Object parameterType; // 参数类型

    public Function() {
    }

    public String getSqlType() {
        return sqlType;
    }

    public void setSqlType(String sqlType) {
        this.sqlType = sqlType;
    }

    public String getFunName() {
        return funName;
    }

    public void setFunName(String funName) {
        this.funName = funName;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public Object getResultType() {
        return resultType;
    }

    public void setResultType(Object resultType) {
        this.resultType = resultType;
    }

    public Object getParameterType() {
        return parameterType;
    }

    public void setParameterType(Object parameterType) {
        this.parameterType = parameterType;
    }
}
package com.mybatis.config;

import java.util.List;

/**
 * 封装Mapper接口信息
 */
public class MapperBean {
    private String interfaceName; // 保存接口全路径名
    private List<Function> functions; // 接口中的所有方法

    public MapperBean() {
    }

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public List<Function> getFunctions() {
        return functions;
    }

    public void setFunctions(List<Function> functions) {
        this.functions = functions;
    }
}

阶段六 : 在Configuration中加载mapper.xml,创建MapperBean对象

(1)Configuration
package com.mybatis.sqlsession;

import com.mybatis.config.Function;
import com.mybatis.config.MapperBean;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 读取xml,建立连接
 */
public class Configuration {
    // 类加载器 ,作用:可以通过类加载器加载配置文件得到对应的流
    private ClassLoader classLoader = ClassLoader.getSystemClassLoader();

    // 读取配置文件并处理
    public Connection build(String resource) {
        Connection connection = null;
        try {
            // 加载配置文件,获取对应inputStream流
            InputStream inputStream = classLoader.getResourceAsStream("mybatis-config.xml");

            // 解析xml dom4j
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            // 获取配置文件根元素
            Element root = document.getRootElement();
            connection = evalElement(root);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        return connection;
    }

    private Connection evalElement(Element node){
        String driver = "",url = "",username = "",password = "";
        Connection connection = null;
        if (!"database".equals(node.getName())){
            throw new RuntimeException("root 节点为 <database>");
        }
        // 遍历node节点下的字节点,获取属性值
        for (Object item : node.elements()){
            // e 就是 property
            Element e = (Element)item;
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            // 判断是否得到 name 和 value
            if( null == name || null == value){
                throw new RuntimeException("未设置name和 value值");
            }

            switch (name){
                case "url" :
                    url = value;
                    break;
                case "username":
                    username = value;
                    break;
                case "password" :
                    password = value;
                    break;
                case "driver":
                    driver = value;
                    break;
                default:
                    throw new RuntimeException("未匹配到属性值。");
            }
        }

        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return connection;
    }

    public MapperBean readMapper(String resource){
        InputStream inputStream = classLoader.getResourceAsStream(resource);
        SAXReader saxReader = new SAXReader();
        MapperBean mapperBean = null;
        try {
            Document document = saxReader.read(inputStream);
            Element root = document.getRootElement();
            System.out.println("root = " + root.getName());
            mapperBean = evalMapperElement(root);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
        return mapperBean;
    }

    private MapperBean evalMapperElement(Element node){
        if (!"mapper".equals(node.getName())){
            throw new RuntimeException("mapper 应为根节点");
        }
        MapperBean mapperBean = new MapperBean();
        Function function = null;
        String interfaceName = node.attributeValue("namespace");
        mapperBean.setInterfaceName(interfaceName);
        Iterator iterator = node.elementIterator();
        List<Function> functions = new ArrayList<>();
        while (iterator.hasNext()){
            function = new Function();
            Object next = iterator.next();
            Element e = (Element)next;
            function.setSqlType(e.getName().trim());
            function.setFunName(e.attributeValue("id").trim());
            //function.setParameterType(e.attributeValue("parameterType").trim());
            function.setSql(e.getText().trim());
            //function.setResultType(e.attributeValue("resultType").trim());
            // 反射生成resultType
            Object o = null;
            try {
                o = Class.forName(e.attributeValue("resultType").trim()).newInstance();
                function.setResultType(o);
            } catch (InstantiationException ex) {
                throw new RuntimeException(ex);
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (ClassNotFoundException ex) {
                throw new RuntimeException(ex);
            }
            functions.add(function);
        }
        mapperBean.setFunctions(functions);
        return mapperBean;
    }
}
(2)Junit测试

在这里插入图片描述

阶段七 : 实现动态代理执行Executor方法

(1)创建代理类
package com.mybatis.sqlsession;

import com.mybatis.config.Function;
import com.mybatis.config.MapperBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;

/**
 * MapperProxy: 动态代理生成Mapper对象,调用HspExecutor方法
 */
public class MapperProxy implements InvocationHandler {
    //属性
    private SqlSession sqlSession;
    private String mapperFile;
    private Configuration configuration;

    //构造器
    public MapperProxy(Configuration configuration,
                          SqlSession sqlSession,
                          Class clazz) {

        this.configuration = configuration;
        this.sqlSession = sqlSession;
        this.mapperFile = "com/mybatis/mapper/"+clazz.getSimpleName() + ".xml";
    }


    //当执行Mapper接口的代理对象方法时,会执行到invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        MapperBean mapperBean =
                configuration.readMapper(this.mapperFile);

        //判断是否是xml文件对应的接口
        if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName())) {
            return null;
        }

        //取出mapperBean的functions
        List<Function> functions = mapperBean.getFunctions();
        //判断当前mapperBean解析对应MappperXML后 , 有方法
        if (null != functions && 0 != functions.size()) {

            for (Function function : functions) {
                //当前要执行的方法和function.getFuncName()一样
                //说明我们可以从当前遍历的function对象中,取出相应的信息sql, 并执行方法
                if(method.getName().equals(function.getFunName())) {
                    //如果我们当前的function 要执行的sqlType是select
                    //我们就去执行selectOne
                    /**
                     *
                     * 老师说明:
                     * 1. 如果要执行的方法是select , 就对应执行selectOne
                     * 2. 因为老韩在HspSqlSession就写了一个 selectOne
                     * 3. 实际上HspSqlSession 应该对应不同的方法(多个方法)
                     * , 根据不同的匹配情况调用不同方法, 并且还需要进行参数解析处理, 还有比较复杂的字符串处理,拼接sql ,处理返回类型等等工作
                     * 4. 因为老韩主要是讲解mybatis 生成mapper动态代理对象, 调用方法的机制,所以我做了简化
                     */
                    if("select".equalsIgnoreCase(function.getSqlType())) {
                        return sqlSession.selectOne(function.getSql(),String.valueOf(args[0]));
                    }
                }
            }
        }

        return null;
    }


}
(2)SqlSession中添加getMapper方法
package com.mybatis.sqlsession;

import java.lang.reflect.Proxy;

public class SqlSession {
    private Executor executor = new BaseExecutor();
    private Configuration configuration = new Configuration();

    public <T> T selectOne(String sql,Object... object){
        return executor.query(sql,object);
    }

    /**
     * 1. 返回mapper的动态代理对象
     * 2. 这里clazz 到时传入的是 MonsterMapper.class
     * 3. 返回的就是MonsterMapper接口代理对象
     * 4. 当执行接口方法时(通过代理对象调用), 根据动态代理机制会执行到HspMapperProxy-invoke
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> clazz) {

        //返回动态代理对象
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
                new MapperProxy(configuration,this,clazz));
    }
}
(3)Junit测试

在这里插入图片描述

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

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

相关文章

【C语言】简易版扫雷游戏(数组、函数的练习)

目录 一、分析和设计 1.1、扫雷游戏的功能分析 1.2、文件结构设计&#xff08;多文件的练习&#xff09; 1.3、数据结构的设计 二、代码 三、效果展示 三、优化 一、分析和设计 1.1、扫雷游戏的功能分析 以在线版的扫雷游戏为参考&#xff0c;分析它的功能&#xff1a;扫…

JAVA中的多线程详解

1.概念 进程(Process)&#xff1a; 进程是一个包含自身执行地址的程序&#xff0c;多线程使程序可以同时存在多个执行片段&#xff0c;这些执行片段根据不同的条件和环境同步或者异步工作&#xff0c;由于转换的数独很快&#xff0c;使人感觉上进程像是在同时运行。 现在的计…

Kafka知识总结(事务+数据存储+请求模型+常见场景)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 事务 事务Producer保证消息写入分区的原子性&#xff0c;即这批消…

nodejs与npm版本对应表

Node.js — Node.js 版本 (nodejs.org)

GB28181国标视频汇聚平台EasyCVR视频管理系统如何更改GIS地图的默认位置?

GB28181国标视频汇聚平台EasyCVR视频管理系统以其强大的拓展性、灵活的部署方式、高性能的视频能力和智能化的分析能力&#xff0c;为各行各业的视频监控需求提供了优秀的解决方案。通过简单的配置和操作流程&#xff0c;用户可以轻松地进行远程视频监控、存储和查看&#xff0…

【Qt开发】No matching signal for on_toolButton_clicked() 解决方案

【Qt开发】No matching signal for on_toolButton_clicked() 解决方案 文章目录 No matching signal for xxx 解决方案附录&#xff1a;C语言到C的入门知识点&#xff08;主要适用于C语言精通到Qt的C开发入门&#xff09;C语言与C的不同C中写C语言代码C语言到C的知识点Qt开发中…

【C51】8051 微控制器入门指南

目录 1. 理解 C51 编程环境1.1 了解 8051 微控制器架构1.2 设置开发环境 2. 编写 C51 嵌入式代码2.1 基础代码结构2.2 使用寄存器和 I/O 端口2.3 中断处理2.4 调试和测试 3. 高级特性和优化3.1 嵌套中断3.2 内存管理3.3 外设接口3.4 编译器优化 4. 示例项目4.1 LED 闪烁程序4.2…

vardaccico前端私有库

vardacico docker pull verdaccio/verdaccio:4 docker run -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio Docker | Verdaccio 拷贝docker中的配置到宿主机 进入docker内部 docker exec -it verdaccio /bin/sh 进入到指定目录 cd /verdaccio 开始拷贝到指定目…

BOM管理挑战:识别不同业务需求下的应对策略

BOM作为制造行业中的核心概念&#xff0c;其架构的复杂度直接影响到企业的运营效率、成本控制以及市场响应速度。道合顺接下来将介绍如何在管理目标与BOM架构复杂度之间找到平衡点&#xff0c;以满足不同业务需求&#xff0c;助力企业在激烈的市场竞争中脱颖而出。 一、理解BO…

window下编译UCL

window下编译UCL 一、环境安装二、编译error: ACC conformance test failed. Stop. 一、环境安装 安装minGW minGW中安装g 和gcc 安装msys-base 二、编译 启动msys.bat C:\MinGW\msys\1.0\msys.bat 切换到ucl源码目录 执行语句 ./configure CPPFLAGS"$CPPFLAGS -stdc…

昇思25天学习打卡营第19天|DCGAN生成漫画头像

DCGAN生成漫画头像总结 实验概述 本实验旨在利用深度卷积生成对抗网络&#xff08;DCGAN&#xff09;生成动漫头像&#xff0c;通过设置网络、优化器以及损失函数&#xff0c;使用MindSpore进行实现。 实验目的 学习和掌握DCGAN的基本原理和应用。熟悉使用MindSpore进行图像…

气象水文耦合模WRF-Hydro建模技术

原文链接&#xff1a;气象水文耦合模WRF-Hydro建模技术https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247610398&idx4&sn34b4bbed4c74dcbbb0ac19ef8dcdaaff&chksmfa8271f9cdf5f8ef34ea6f721736a2fbbf8be896744ab7e46caa571c52a30628f056b4bd6964&t…

AI如何助力UI设计师互联网学习?

嘿&#xff0c;咱 UI 设计师想用互联网学习&#xff0c;可真不容易&#xff01;资料筛选难&#xff0c;学习资源杂&#xff0c;真让人头疼。不过还好有 AI 工具能帮忙&#xff0c;提效率&#xff01; 这一年多来&#xff0c;我在 ai123.cn 这个平台上&#xff0c;可算是找到了…

羊大师:羊奶营养揭秘,健康关爱的另一优选选择

在琳琅满目的乳制品中&#xff0c;羊奶如同一颗璀璨的明珠&#xff0c;以其独特的营养价值和健康益处&#xff0c;逐渐走进千家万户&#xff0c;成为许多人健康关爱的新选择。那么&#xff0c;羊奶究竟蕴藏着怎样的营养奥秘&#xff0c;让它能够在众多饮品中脱颖而出呢&#xf…

【MySQL】:在Centos 7 环境下的安装

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家带来如何在Centos7环境下安装MySQL&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

关于C++11一些新特性的介绍(下)

文章目录 1. 可变参数模板1.1 可变参数模板介绍1.2 STL容器中的empalce相关接口函数 2. lambda表达式2.1 lambda诞生背景2.2 lambda表达式语法2.3 捕捉列表说明2.4 函数对象与lambda表达式 3. 包装器3.1 function包装器3.2 bind函数 1. 可变参数模板 1.1 可变参数模板介绍 C1…

我的创作纪念日(一)——Giser?Noder?不如“Computer”

目录 Giser&#xff1f;Noder&#xff1f;不如“Computer” 一、根源&#xff1a;保持学习习惯的刚需 二、机缘&#xff1a;processOn的另类替代 三、日常&#xff1a;对技术栈丰富的思考 四、成就&#xff1a;保持心态健康的活着 五、憧憬&#xff1a;能一直心态健康的活…

大模型学习(1)

初学者&#xff0c;仅做自己学习记录&#xff0c;如果对你有什么帮助&#xff0c;那更好了。 下面是论文《Attention Is All You Need》的经典transformer架构&#xff0c;在学习的过程中&#xff0c;有很多疑惑。 embedding层在做什么 Transformer的embedding层在做的是将输…

【C++进阶】AVL树详解

文章目录 1. AVL树的概念2. AVL树结点的定义3. AVL 树的插入3.1 关于平衡因子3.2 插入代码 4. AVL 树的旋转逻辑4.1 不需要旋转4.2 左旋4.3 右旋4.4 双旋4.4.1 先右后左单旋&#xff08;RL 旋转&#xff09;4.4.2 先左后右单旋&#xff08;LR 旋转&#xff09; 4.5 完整插入代码…