Mybatis 初识

news2024/12/24 9:17:50

目录

1. MyBatis入门

1.1 MyBatis的定义

1.2 MyBatis的核心

MyBatis的核心

JDBC 的操作回顾

1.3 MyBatis的执行流程

MyBatis基本工作原理

2. MyBatis的使用

2.1 MyBatis环境搭建

2.1.1 创建数据库和表

2.1.2 添加MyBatis框架支持

老项目添加MyBatis

新项目添加MyBatis

2.1.3 设置MyBatis配置信息

设置数据库连接的相关信息

MyBatis xml保存路径和xml命名规范

2.2 MyBatis 业务代码 (以查询为例)

2.2.1 MyBatis的开发模式

2.2.2 添加实体类

2.2.3 数据持久层实现

Interface

.xml

2.2.4 服务层Service

2.2.5 控制器层 Controller

2.2.6 程序测试

浏览器测试

Postman 测试


1. MyBatis入门

1.1 MyBatis的定义

MyBatis 是一款优秀的 持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 去除了几乎所有的JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的XML 或注解来配置和映射原始类型、接口和 Java POJO (Plain Old Java Obiects,普通老式 Java 对象)为数据库中的记录。

简单来说 MyBatis 是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具

MyBatis官网

持久化: 将内存中的数据长久的保存到磁盘中.
MyBatis是半自动化的框架: 要实现一个业务, 所有SQL需要自己实现.
存储过程: 将多个SQL语句连成一个方法.
高级映射: 将数据库中的多个表映射到一个对象中.

1.2 MyBatis的核心

对于后端开发来说,程序是由以下两个重要的部分组成的:

  1. 后端程序
  2. 数据库

而这两个重要的组成部分要通讯,就要依靠数据库连接工具MyBatis(之前则使用JDBC) 那么MyBatis是为了简化JDBC操作而诞生的.

MyBatis的核心

MyBatis的核心分为两部分

  • 配置MyBatis 开发环境;
  • 使用MyBatis 模式和语法操作数据库.

JDBC 的操作回顾

  1. 创建数据库连接池 DataSource
  2. 通过 DataSource 获取数据库连接 Connection
  3. 编写要执行带?占位符的 SQL 语句
  4. 通过 Connection 及 SQL 创建操作命令对象 Statement
  5. 替换占位符: 指定要替换的数据库字段类型,占位符索引及要替换的值
  6. 使用 Statement 执行 SQL 语句
  7. 查询操作: 返回结果集 ResultSet,更新操作:返回更新的数量
  8. 处理结果集
  9. 释放资源

下面的一个完整案例,展示了通过 JDBC 的 API 向数据库中添加一条记录,修改一条记录,查询一条记录的操作。

-- 创建数据库
create database if not exists `library` default character set utf8mb4;
-- 使用数据库
use library;
-- 创建表
create table if not exists `soft_bookrack` (
    `book_name` varchar(32) NOT NULL,
    `book_author` varchar(32) NOT NULL,
    `book_isbn` varchar(32) NOT NULL primary key
);

以下是 JDBC 操作的具体实现代码:

package com.example.demo;

import lombok.Getter;
import lombok.Setter;

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

public class SimpleJdbcOperation {
    private final DataSource dataSource;

    public SimpleJdbcOperation(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 添加一本书
     */
    public void addBook() {
        Connection connection = null;
        PreparedStatement stmt = null;
        try {
            //获取数据库连接
            connection = dataSource.getConnection();
            //创建语句
            stmt = connection.prepareStatement(
                    "insert into soft_bookrack (book_name, book_author, book_isbn) values( ?,?,?);");
            //参数绑定
            stmt.setString(1, "Spring in Action");
            stmt.setString(2, "Craig Walls");
            stmt.setString(3, "9787115417305");
            //执行语句
            stmt.execute();
        } catch (SQLException e) {
            //处理异常信息
        } finally {
            //清理资源
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                //
            }
        }
    }

    /**
     * 更新一 本书
     */
    public void updateBook() {
        Connection connection = null;
        PreparedStatement stmt = null;
        try {
            //获取数据库连接
            connection = dataSource.getConnection();
            //创建语句
            stmt = connection.prepareStatement("update soft_bookrack set book_author=? where book_isbn=?;");
            //参数绑定
            stmt.setString(1, "张卫滨");
            stmt.setString(2, "9787115417305");
            //执行语句
            stmt.execute();
        } catch (SQLException e) {
            //处理异常信息
        } finally {
            //清理资源
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                //
            }
        }
    }

    /**
     * 查询一本书
     */
    public void queryBook() {
        Connection connection = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Book book = null;
        try {
            //获取数据库连接
            connection = dataSource.getConnection();
            //创建语句
            stmt = connection.prepareStatement("select book_name, book_author, book_isbn from soft_bookrack where book_isbn =?");
            //参数绑定
            stmt.setString(1, "9787115417305");
            //执行 语句
            rs = stmt.executeQuery();
            if (rs.next()) {
                book = new Book();
                book.setName(rs.getString("book_name"));
                book.setAuthor(rs.getString("book_author"));
                book.setIsbn(rs.getString("book_isbn"));
            }
            System.out.println(book);
        } catch (SQLException e) {
            //处理异常信息
        } finally {
            //清理资源
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                //
            }
        }
    }

    @Setter
    @Getter
    public static class Book {
        private String name;
        private String author;
        private String isbn;
    }
}

从上述代码和操作流程可以看出,对于JDBC 来说,整个操作非常的繁琐,我们不但要拼接每一个参数,而且还要按照模板代码的方式,一步步的操作数据库,并且在每次操作完,还要手动关闭连接等而所有的这些操作步骤都需要在每个方法中重复书写。所以为了简化数据库的操作, 就有了MyBatis.


1.3 MyBatis的执行流程

上图描述了一个程序执行的标准流程, 那么其中的Mapper就是MyBatis的所在.
MyBatis包括Interface和.xml(实现接口), 那么Interface是方法的声明(接口), 但是没有方法的实现; 而方法的实现就在.xml中实现(SQL写在这里面). 但是在调用的时候调用的是Interface.
那么程序员操作的就是这两个组件, 这两个组件的操作也是基于JDBC的, 也就是说MyBatis是基于JDBC, 即 JDBC是给数据库上面封装了一层操作, 而MyBatis则是在JDBC基础上再加了一层, 能够更加的方便易用.

MyBatis基本工作原理

MyBatis 也是一个 ORM 框架,ORM(Object RelationalMapping),即对象关系映射。在面向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象的互相转换:

  1. 将输入数据 (即传入对象) +SQL 映射成原生 SQL
  2. 将结果集映射为返回对象,即输出对象

ORM 把数据库映射为对象:

  • 数据库表 (table) --> 类 (class)
  • 记录 (record,行数据)--> 对象 (object)
  • 字段 (field) --> 对象的属性 (attribute)

一般的 ORM 框架,会将数据库模型的每张表都映射为一个 Java 类。也就是说使用 MyBatis可以像操作对象一样来操作数据库中的表,可以实现对象和数据库表之间的转换。

2. MyBatis的使用

2.1 MyBatis环境搭建

2.1.1 创建数据库和表

首先我们要实现的功能是: 使用 MyBatis 的方式来读取用户表中的所有用户,我们使用个人博客的数据库和数据包,具体 SQL 如下。

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;

-- 使用数据数据
use mycnblog;

-- 创建表[用户表]
drop table if exists  userinfo;
create table userinfo(
    id int primary key auto_increment,
    username varchar(100) not null,
    password varchar(32) not null,
    photo varchar(500) default '',
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    `state` int default 1
) default charset 'utf8mb4';

-- 创建文章表
drop table if exists  articleinfo;
create table articleinfo(
    id int primary key auto_increment,
    title varchar(100) not null,
    content text not null,
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    uid int not null,
    rcount int not null default 1,
    `state` int default 1
)default charset 'utf8mb4';

-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
      vid int primary key,
      `title` varchar(250),
      `url` varchar(1000),
        createtime timestamp default current_timestamp,
        updatetime timestamp default current_timestamp,
      uid int
)default charset 'utf8mb4';

-- 添加一个用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

-- 文章添加测试数据
insert into articleinfo(title,content,uid)
    values('Java','Java正文',1);
    
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);

2.1.2 添加MyBatis框架支持

添加 MyBatis 框架支持分为两种情况:一种情况是对自己之前的 Spring 项目进行升级,另一种情况是创建一个全新的 MyBatis 和 Spring Boot 的项目,下面我们分别来演示这两种情况的具体实现。

老项目添加MyBatis

如果是在老项目中新增功能,添加框架支持:

在pom.xml下的<dependencies>标签内部Alt+Insert, 并点击Edit Starters(安装好这个插件),

点击OK, 即可在窗口中添加相关框架.

新项目添加MyBatis

如果是新项目创建Spring Boot 项目的时候添加引用就可以了,如下图所示:

注意: 添加MyBatis框架支持之后直接运行是会报错的, 所以要再设置MyBatis配置信息.

2.1.3 设置MyBatis配置信息

在application.properties配置文件中配置MyBatis相关信息. (也可以用yml, 这里省略)

设置数据库连接的相关信息

# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
# 注意! password 要写自己本机 mysql 的密码, 不是此处固定的111111
spring.datasource.password=111111
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

MyBatis xml保存路径和xml命名规范

# 设置 MyBatis
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml

下图为用户表的Mapper.xml, 即UserMapper.xml

2.2 MyBatis 业务代码 (以查询为例)

2.2.1 MyBatis的开发模式

配置完MyBatis的开发环境之后, 就要按照MyBatis的模式来开发我们的业务代码.

上图描述了MyBatis的开发模式, 针对于MyBatis来说, 它只有Interface和XXX.xml两个文件, Interface是给其他人调用的, 这个就是Java中普通的Interface接口, 然后通过注入的方式将这个接口注入上去, 那么问题是这个接口没有方法实现的, 前文提到, MyBatis在实现业务的时候是需要我们自己来写SQL代码的, 那么SQL就会写在XXX.xml中.
也就是说, 每一个功能都会对应两个组件, 一个是Interface给我们的服务层调用, 然后是针对Interface的代码实现.
那么在Java中只有普通的类可以实现接口, 重写接口的方法, 那xml怎么能够实现Interface中的方法?
这就是MyBatis的厉害之处, MyBatis通过代理的方式实现Interface中的方法, 也就是说, 对于我们用户来说, 我们感知的是Interface和XXX.xml, 到了MyBatis中运行时就会生成一个代理对象, 然后这个代理对象会将Interface里面的方法声明和XXX.xml里面的方法实现给它们组合成一个代理对象的方法进行填充.
也就是说在其他层去调用MyBatis, 其实就是调用那个代理对象了, 那么代理对象就是一个普通类了. 那么普通类中就自然而然有方法, 有方法的实现.
所以说, 我们看到的虽然是这两个组件, 但是最终实现的时候, MyBatis框架会帮我们封装好进行操作.

MyBatis模式开发由两部分组成:

  1. Interface: 让其他层可以注入使用的接口
  2. MyBatis: xml -> 具体实现SQL【它是上面Interface的"实现"】

明确了MyBatis中的构成之后我们才能够去合理的写它的代码, 由UserMapper.xml的图可知, xml的命名格式必须得是*Mapper.xml, 并且必须得放在/mybatis文件夹下. (注意: 配置了什么路径就得将xml放在什么文件夹下, 文件夹名要和路径相同)

2.2.2 添加实体类

先添加用户的实体类, 注意属性名和字段名尽量和SQL中的表一致, 这样MyBatis就会自动实现映射和关联(如果名字不一样就不会关联了):

package com.example.demo.entity;

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class UserEntity {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private LocalDateTime createtime;
    private LocalDateTime updatetime;
    private Integer state;
}

2.2.3 数据持久层实现

Interface

以用户的查询功能为例, 在mapper下建立UserMapper, 注意这个类不需要有实现, 因为SQL是写在xml中, 它只需要有方法的声明让别人去调用即可.

package com.example.demo.mapper;

import com.example.demo.entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper // 注意添加此注解
public interface UserMapper {
    List<UserEntity> getAll();
}

注意, 创建这个Interface之后的第一件事情就是加@Mapper注解, 因为这不是一个普通的类, 这个类是MyBatis的一个接口, 这个接口是需要和xml去对应起来的, 所以要把这个对象托管给MyBatis, 即加@Mapper注解.

以上就是第一个Interface的实现, 接下来就要实现*Mapper.xml, 它需要在resources.mybatis中创建

.xml


mybatis 的固定 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="com.example.demo.mapper.UserMapper">
   
</mapper>

添加 UserMapper.xmlUserMapper.xml 查询所有用户的具体实现 SQL:

<?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="com.example.demo.mapper.UserMapper">
    <select id="getAll" resultType="com.example.demo.entity.UserEntity">
        select * from userinfo
    </select>
</mapper>

以下是对以上标签的说明

  • <mapper>标签: namespace属性是 指定当前xml实现的类是哪个Interface接口. 值为"包名.类名"
  • <select>查询标签: 是用来执行数据库的查询操作的:
    • id: 是和Interface(接口) 中定义的方法名称一样的,表示对接口的具体实现方法。
    • resultType: 是返回的数据类型,也就是开头我们定义的实体类。

2.2.4 服务层Service

添加 Service服务层, 实现代码如下:

package com.example.demo.service;

import com.example.demo.entity.UserEntity;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public List<UserEntity> getAll(){
        return userMapper.getAll();
    }
}

2.2.5 控制器层 Controller

控制器层的实现代码如下:

package com.example.demo.controller;

import com.example.demo.entity.UserEntity;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/user")
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getall")
    public List<UserEntity> getAll(){
        return userService.getAll();
    }
}

2.2.6 程序测试

浏览器测试

以上代码写完,整个 MyBatis 的查询功能就实现完了,接下来我们启动项目, 先使用浏览器输入路径的方式测试一下.

可以看到已经成功实现了查询功能, 查到了admin. 那么我们可以往数据库中再添加一条数据, 就可以查到第二条数据.

INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
(2, 'zhangsan', 'zhangsan', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

插入第二条数据之后刷新页面, 可以看到数据成功的插入到数据库中了.

Postman 测试

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

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

相关文章

第一份工作要怎么找呀

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

LeetCode150道面试经典题--赎金信(简单)

1.题目 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 2.示例 3.思路 统计字…

JVM 中一次完整的 GC 流程和对象如何晋升到老年代?

前言 今天来分享一道比较好的面试题&#xff0c;JVM 中一次完整的 GC 流程是怎样的以及对象如何晋升到老年代&#xff1f;对于这个问题&#xff0c;我们一起看看考察点和比较好的回答吧。 考察点 Java 虚拟机能够替我们回收内存空间&#xff0c;清除垃圾对象&#xff0c;因此…

《嵌入式 - 嵌入式大杂烩》SVC和PendSV异常详解

1 操作模式 在讨论PendSV和SVC异常前,需要先了解Cortex-M的模式和两个特权等级。 Figure 1 1 操作模式和特权等级 两种模式为handler模式和线程(thread)模式,这两种模式是为了区别正在执行代码的类型;handler模式为异常处理例程的代码;线程模式为普通应用程序的代码。 两…

【云原生】Kubernetes 概述

Kubernetes 概述 1.Kubernetes 简介 Kubernetes 是一个可移植的、可扩展的、用于管理容器化工作负载和服务的开源平台&#xff0c;它简化&#xff08;促进&#xff09;了声明式配置和自动化。它有一个庞大的、快速增长的生态系统。Kubernetes 的服务、支持和工具随处可见。 K…

计算机视觉中的Transformer

几十年来&#xff0c;理论物理学家一直在努力提出一个宏大的统一理论。通过统一&#xff0c;指的是将被认为是完全不同的两个或多个想法结合起来&#xff0c;将它们的不同方面证明为同一基础现象。一个例子是在19世纪之前&#xff0c;电和磁被看作是无关的现象&#xff0c;但电…

HCIP-linux和kvm

1、linux linux安装教程参考&#xff0c;https://blog.51cto.com/cloudcs/5245337 yum源配置 本地yum源配置&#xff1a; 8版本配置&#xff1a;将光盘iso挂载到某个目录&#xff0c;/dev/cdrom是/dev/sr0软链接&#xff0c;# mount /dev/cdrom /mnt&#xff0c;# ls /mnt Ap…

项目知识点记录

1.使用druid连接池 使用properties配置文件&#xff1a; driverClassName com.mysql.cj.jdbc.Driver url jdbc:mysql://localhost:3306/book?useSSLtrue&setUnicodetrue&charsetEncodingUTF-8&serverTimezoneGMT%2B8 username root password 123456 #初始化链接数…

Python-OpenCV中的图像处理-霍夫变换

Python-OpenCV中的图像处理-霍夫变换 霍夫变换霍夫直线变换霍夫圆环变换 霍夫变换 霍夫(Hough)变换在检测各种形状的技术中非常流行&#xff0c;如果要检测的形状可以用数学表达式描述&#xff0c;就可以是使用霍夫变换检测它。即使要检测的形状存在一点破坏或者扭曲也是可以使…

《人月神话》:我所遇见的“焦油坑”

最近在读《人月神话》这本书。 发现大部分task延期和研发关系不是很大&#xff0c;技术为业务服务&#xff0c;但是偿还不了业务债。 焦油坑的概念源自于挖掘坑井时的一种不幸状况。当挖掘坑井时&#xff0c;首先会进入表土层&#xff0c;进展颇快。但接下来遇到了沥青或泥浆…

Highcharts引入

Highcharts是和jQuery一起使用的&#xff0c;所以需要下载好jQuery jQuery下载方式&#xff1a;访问&#xff1a;http://cdn.staticfile.org/jquery/2.1.4/jquery.min.js&#xff0c;然后全选复制到自己新建的txt文档中&#xff0c;最后把扩展名改为js。 Highcharts下载方式&…

红帽8.5 ansible 安装和部署 |(简单版)

安装 配置yum仓库&#xff1a; vim /etc/yun.repo.d/aliyun.repo [AppStream] nameApp baseurlhttps://mirrors.aliyun.com/centos/8-stream/AppStream/x86_64/os gpgcheck0[BaseOS] namebase baseurlhttps://mirrors.aliyun.com/centos/8-stream/BaseOS/x86_64/os gpgcheck…

安装PaddleOCR-2.7.0版本-笔记

安装PaddleOCR-2.7.0版本-笔记 先安装conda和python版本 本机安装的conda 22.9.0 python2.9.12 paddle2.4.2 paddlepaddle-gpu2.4.2 cuda10.2 安装完后&#xff0c;测试生成的结果如下所示&#xff1a; 一、第一步先激活环境 conda activate base conda activate base二、第…

线程介绍,线程与进程区别,如何使用多线程,Thread类,Runnable接口,补充知识(方法重载,方法重写)

引言&#xff1a;搞进程是为了什么呢&#xff1f; 满足并发编程&#xff0c;这样的需求&#xff0c;cpu多个核心&#xff0c;应用程序做出对应调整&#xff0c;让代码可以把多个核心充分利用起来&#xff5e;&#xff0c;当需要支持多个任务的时候——进程就十分关键了&#x…

8月9日上课内容 nginx反向代理与负载均衡

负载均衡工作当中用的很多的&#xff0c;也是面试会问的很重要的一个点 负载均衡&#xff1a;通过反向代理来实现&#xff08;nginx只有反向代理才能做负载均衡&#xff09; 正向代理的配置方法&#xff08;用的较少&#xff09; 反向代理的方式&#xff1a;四层代理与七层代…

Golang 函数定义及使用

文章目录 一、函数定义格式二、函数定义及使用 一、函数定义格式 //func: 函数定义关键字 //function_name&#xff1a;函数名称 //parameter_List: 函数参数列表&#xff0c;多个时使用逗号拆分 //return_types&#xff1a;函数返回类型&#xff0c;返回多个值时使用逗号拆分…

2023河南萌新联赛第(五)场:郑州轻工业大学-F 布鲁特佛斯

2023河南萌新联赛第&#xff08;五&#xff09;场&#xff1a;郑州轻工业大学-F 布鲁特佛斯 https://ac.nowcoder.com/acm/contest/62977/F 文章目录 2023河南萌新联赛第&#xff08;五&#xff09;场&#xff1a;郑州轻工业大学-F 布鲁特佛斯题意解题思路代码 题意 给定一个…

前后端分离------后端创建笔记(02)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

《甲午》观后感——GPT-3.5所写

《甲午》是一部令人深思的纪录片&#xff0c;通过生动的画面和真实的故事&#xff0c;向观众展示了中国历史上的一段重要时期。观看这部纪录片&#xff0c;我深受触动&#xff0c;对历史的认识也得到了深化。 首先&#xff0c;这部纪录片通过精心搜集的历史资料和珍贵的影像资料…

SLF4J日志绑定原理分析

前言 最近做项目&#xff0c;实际上也知道日志冲突的事&#xff0c;不过涉及MDC NDC数据传递&#xff0c;当日志框架冲突后&#xff0c;MDC和NDC就失效了&#xff0c;这里就涉及slf4j-api的MDC的绑定的过程&#xff0c;顺便分析了日志冲突实际生效的原因&#xff0c;理解SLF4J…