当前位置: 首页 > news >正文

MyBatis 操作数据库

目录

什么是MyBatis?

注释

@Mapper注释的介绍和使用

@Select注释的介绍和使用

@SpringBootTest注释的介绍和使用

@Test注释的介绍的使用

MyBatis入门​

1)准备工作

<1>创建工程​

<2>数据准备​

2)配置数据库连接字符串

3)写持久层代码​

4)单元测试

MyBatis的基础操作​

打印日志

参数传递​

1)传递一个参数

2)传递多个参数

增(Insert)​

删(Delete)​

改(Update)​

查(Select)​

1)起别名​

2)结果映射

3)开启驼峰命名(推荐)​

MyBatis XML配置文件

配置连接字符串和MyBatis​

写持久层代码​

添加 mapper 接口​

添加 UserInfoXMLMapper.xml​

增删改查操作​

1)增(Insert)

2)删(Delete)

3)改(Update)

4)查(select)

(1)起别名

(2)结果映射

(3)开启驼峰命名

其他查询操作​

多表查询​

#{} 和 ${} ​【重点】

#{} 和${} 使用​

#{} 和${} 区别

1. 性能更高

2. 更安全(防止SQL注入)​

排序功能​

like 查询​

内置函数 concat() 

动态sql

标签​

标签

标签​

标签

标签​


什么是MyBatis?

MyBatis是一款优秀的持久层框架,用于简化JDBC的开发。​

持久层:指的就是持久化操作的层, 通常指数据访问层(dao), 是用来操作数据库的

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


注释

在本篇博客中出现的所有注释小编都提到这个章节进行介绍.但是在之前的博客中进行介绍的注释,这里就不做过多的介绍。

@Mapper注释的介绍和使用

@Mapper
public interface UserInfoMapper {@Select("select * from user_info")List<UserInfo> getUserInfoAll();
}

告诉 Spring 框架这是一个与数据库交互Mapper 接口,Spring 会自动生成该接口的代理对象,在运行时通过代理对象执行 SQL 映射语句,实现数据库的增删改查操作。


@Select注释的介绍和使用

@Mapper
public interface UserInfoMapper {@Select("select * from user_info")List<UserInfo> getUserInfoAll();
}

通过在 Mapper 接口的方法上添加 @Select 注解,并在注解中编写 SQL 查询语句,MyBatis 框架在运行时会根据该注解对应的方法调用,执行相应的 SQL 查询,并将查询结果按照配置或默认规则进行映射处理,返回给调用方。例如,可用于从数据库表中查询特定条件的数据记录,如获取用户信息、订单信息等。


@SpringBootTest注释的介绍和使用

启动一个完整的 Spring 应用程序上下文,加载所有配置的 Bean、自动配置以及配置文件。基于此,能够对应用程序的各个组件(如 Controller、Service、Repository 等 )进行全面测试,验证它们是否能正常协同工作 。

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoAll() {System.out.println(userInfoMapper.getUserInfoAll());}
}

@Test注释的介绍的使用

标记在测试类的方法上,使该方法成为一个可单独执行的测试单元。通过运行这些被 @Test 注解标记的方法,可以验证代码中特定功能或逻辑的正确性。例如,验证一个计算方法的输出结果是否符合预期,或者验证数据库操作是否成功等。

代码展示:

同上一个代码一样。



MyBatis入门​

Mybatis操作数据库的步骤:​
1. 准备工作(创建springboot工程、数据库表准备、实体类)​
2. 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)​
3. 编写SQL语句(注解/XML)​
4. 测试

1)准备工作

<1>创建工程​

这里的创建工程就是创建spring boot项目,与前面小编创建项目的不同点在于多引入两个依赖,如下图所示。分别是MyBatis Framework、Mysql Drive

这里小编要多一句嘴,对于这个依赖的引入是非常容易出错的,尤其是对于一些在校的大学生这个依赖的引入非常容易出现问题,即为网络的问题。

不要使用校园网!不要使用校园卡的热点!切换成自己最常用的那张卡的热点!不然真的捣鼓一整天就弄不出来!

<2>数据准备​

(1)创建用户表, 并创建对应的实体类User​

如上图所示,小编已经在MySQL中创建好了对应的数据库、数据表、以及表的内容...

sql代码如下示:

-- 删除数据库(如果存在)
DROP DATABASE IF EXISTS mybatis_test;
-- 创建数据库
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;-- 使用数据库
USE mybatis_test;-- 删除表(如果存在)
DROP TABLE IF EXISTS user_info;
-- 创建用户表
CREATE TABLE `user_info` (`id` INT(11) NOT NULL AUTO_INCREMENT,`username` VARCHAR(127) NOT NULL,`password` VARCHAR(127) NOT NULL,`age` TINYINT(4) NOT NULL,`gender` TINYINT(4) DEFAULT '0' COMMENT '1-男 2-女 0-默认',`phone` VARCHAR(15) DEFAULT NULL,`delete_flag` TINYINT(4) DEFAULT 0 COMMENT '0-正常, 1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;-- 插入用户信息
INSERT INTO user_info (username, `password`, age, gender, phone)
VALUES ('admin', 'admin', 18, 1, '18612340001');
INSERT INTO user_info (username, `password`, age, gender, phone)
VALUES ('zhangsan', 'zhangsan', 18, 1, '18612340002');
INSERT INTO user_info (username, `password`, age, gender, phone)
VALUES ('lisi', 'lisi', 18, 1, '18612340003');
INSERT INTO user_info (username, `password`, age, gender, phone)
VALUES ('wangwu', 'wangwu', 18, 1, '18612340004');

不想手敲代码的,直接cv小编的即可!

(2)创建对应的实体类 UserInfo​

在MySQL中把数据库准备好之后,就需要在我们的Mybatis中创建一个类来对应上MySQL中的数据的字段,MySQL中的数据字段和程序中对应的代码如下示:

MySQL中的数据字段:

程序中对应的代码:

package org.example.mybatis_test.model;import lombok.Data;import java.util.Date;
@Data
public class UserInfo {private int id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;}

这两个地方的字段示一一对应的关系。


2)配置数据库连接字符串

Mybatis中要连接数据库,需要数据库相关参数配置​
• MySQL驱动类​
• 登录名
• 密码
• 数据库连接字符串

复制代码:

#数据库相关配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver

注意,这里大家不要盲目地复制,看清自己数据库地用户名、密码、地址、端口...但凡有一个与自己地数据库信息对应不上都会导致报错,所以大家不要盲目地跟随小编。小编还是那句话,每个同学的电脑配置环境配置都是不尽相同,所以要学会自己去变通以及解决问题。


3)写持久层代码​

大家先扫一眼小编的目录,是由哪些功能实现的。

(1)UserInfoMapper接口

@Mapper
public interface UserInfoMapper {@Select("select * from user_info")List<UserInfo> getUserInfoAll();
}

如上代码,"select * from user_info"这个语句与我们MySQL中查询整张表的格式是一样的【这里要注意,这个表的名字一定要和MySQL数据库中存储的表头名字是一样的,不然就会报错!】,下一句的语句就是创建一个List去存储我们从这张表中获取的全部数据,通过getUserInfoAll()这个方法。

如上述这个代码就是持久层的代码,我们一般把这个代码统一放在Mapper这个包下,方便企业中进行管理。【这个包下代码都是添加了@Mapper注解的】

(2)UserService

package org.example.mybatis_test.Service;import org.example.mybatis_test.Mapper.UserInfoMapper;
import org.example.mybatis_test.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;
@Service
public class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public List<UserInfo> getUserAll() {return userInfoMapper.getUserInfoAll();}
}

(3)UserController

package org.example.mybatis_test.Controller;import org.example.mybatis_test.Service.UserService;
import org.example.mybatis_test.model.UserInfo;
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;
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/getUserAll")public List<UserInfo> getUserAll() {System.out.println(userService.getUserAll());return userService.getUserAll();}
}

上述代码已经基本实现了我们从MySQL中获取user_info这个表全部内容的需求了。接下来我们在测试类中测试一下这个代码是否能够顺利访问到MySQL数据库中的代码。

代码展示:

package org.example.mybatis_test;import org.example.mybatis_test.Mapper.UserInfoMapper;
import org.example.mybatis_test.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
class MybatisTestApplicationTests {@Autowired   //注入进来private UserInfoMapper userInfoMapper;@Testvoid contextLoads() {List<UserInfo> userInfos = userInfoMapper.getUserInfoAll();System.out.println(userInfos);}
}

结果展示:

如上图所示,我们在运行测试代码之后呢,也是顺利在控制台获取到了MySQL数据库中,user_info表中的全部信息。

那在浏览器中呢?接下来我们通过Controller中的路由进行一个访问,验证一下我们这个代码在http的请求下能否顺利访问到MySQL数据库中指定路径中的信息。

(1)先把我们项目先启动起来

如上图所示,我们的项目顺利启动!

(2)在浏览器中向后端发起http请求

如上图所示,我们也成功在浏览器中通过http请求获取到MySQL数据库中的信息了。


那是不是所有的测试都只能在同一个测试类中进行,能不能有一种简便方法直接对某一方法直接生成对应的测试类。那当然有了,接下来我们介绍一下单元测试

4)单元测试

UserInfoMapper举例:

(1)在UserInfoMapper点击鼠标左键

(2)选择Test

(3)选择对应的方法

(4)在目录中找到对应的方法然后进行测试

(5)填写自己的测试需求

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoAll() {System.out.println(userInfoMapper.getUserInfoAll());}
}

结果展示:

如上图所示,测试的结果与我们使用Spring boot架构自身提供的那个Test类进行测试一样。


MyBatis的基础操作​

上面我们学习了Mybatis的查询操作, 接下来我们学习MyBatis的增, 删, 改操作​
在学习这些操作之前, 我们先来学习MyBatis日志打印​。

打印日志

在Mybatis当中我们可以借助日志, 查看到sql语句的执行执行传递的参数以及执行结果​
在配置文件中进行配置即可

下面这些内容是放在配置文件yml中的。

mybatis:configuration:# 配置打印MyBatis日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

运行结果:

①: 查询语句​
②: 传递参数及类型​
③: SQL执行结果​


参数传递​

在进行参数传递之前,我们要明确我们的MySQL数据表中有哪些参数【哪些字段】,我们才能够通过这个参数去进行查询。通过参数的传递我们可以找到我们想要的数据,比如我们只想查看user_info表名字zhangsan的人的所有信息,那我们就要通过名字进行定位。

1)传递一个参数

如上图我们看到这个delete_fla这一列中只有叫lisi的这个人是1,那咱们就通过下述代码来查找delete_flag1的人,预期的结果应该是只出现id为1,3,4的人。

代码示例:

@Mapper
public interface UserInfoMapper {/*** 参数的传递【一个】*/@Select("select * from user_info where delete_flag = #{deleteFlag}")List<UserInfo> getUserInfoAllByDeleteFlg(Integer deleteFlag);
}

单元测试的方式进行测试:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoAllByDeleteFlg() {System.out.println(userInfoMapper.getUserInfoAllByDeleteFlg(0));}}

结果展示:

结果如上图所示,我们确实是顺利的找到这三个人。

但是,此时我们要想一个问题,我们在Mapper代码中可得,其中

这两个名字是一样的,若名字不一样会不会出错呢?我们接下来就开始试验一下,寻找答案

由上述的结果,我们看出得到的结果是一样的。那我们是不是可以笃定这个参数一不一致都不会影响我们的结果呢?还是说只是在特殊情况下才可以这么做呢?我们留下一个疑问,继续往前走。

2)传递多个参数

上面是传递一个参数的情况,要是传递两个参数得到的又是一些什么信息呢?我们依旧是先看一下这个MySQL数据库的这个user_info这个表中有写什么信息先。

如上图,假设我们要查询gander为1,且delete_flag为0的信息,只能查到两条。

代码展示:

@Mapper
public interface UserInfoMapper {/*** 参数的传递【多个】*/@Select("select * from user_info where delete_flag = #{deleteFlag} and gender = #{gender}")List<UserInfo> getUserInfoAllByDeleteFlg2(Integer deleteFlag,Integer gender);
}

测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoAllByDeleteFlg2() {System.out.println(userInfoMapper.getUserInfoAllByDeleteFlg2(0,1));}}

结果展示:

如上图所示,又符合我们刚开始的预期,返回的是id为1、2的两条信息。

但是我们把问题又抛到上面遗留的那个问题,是否参数都要一样才可以从数据库中获取信息呢?心动不如行动,我们测试一下便能知晓答案。

改动后的代码:

@Mapper
public interface UserInfoMapper {/*** 参数的传递【多个】*/@Select("select * from user_info where delete_flag = #{deleteFlag} and gender = #{gender}")List<UserInfo> getUserInfoAllByDeleteFlg2(Integer flag,Integer gender);
}

测试结果:

唉!报错了!那这就值得我们总结一下咯。经过多测实验,小编发现当传递的参数只有一个的时候,参数的名字对不对应都可以获取到预期的结果。但是一旦参数超过一个的时候,参数的名称必须对应才可以找到数据库中对应的数据!


增(Insert)​

代码展示:

1)方法代码:

@Mapper
public interface UserInfoMapper {/*** 1.增*/@Options(useGeneratedKeys = true,keyProperty = "id")@Insert("insert into user_info (username,password,age,gender) values (#{username},#{password},#{age},#{gender})")Integer insertUserInfo(UserInfo userInfo);
}

2)测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid insertUserInfo() {UserInfo userInfo = new UserInfo();userInfo.setUsername("dayun");userInfo.setPassword("123456");userInfo.setAge(18);userInfo.setGender(1);Integer result = userInfoMapper.insertUserInfo(userInfo);System.out.println("影响行数: "+result+","+"id: "+userInfo.getId());}
}

结果展示:

如上图所示,成功在数据库中添加了这条数据!测试成功!


删(Delete)​

代码展示:

1)方法代码:

@Mapper
public interface UserInfoMapper {/*** 2.删*/@Delete("delete from user_info where id = #{id}")Integer delete(Integer id);
}

2)测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid delete() {Integer result = userInfoMapper.delete(19);System.out.println("影响行数: "+result);}
}

上述代码,是要删除id为19的数据【即我们刚刚插入的位置】。

结果展示:

由结果展示,刚刚我们添加到数据库的数据被删除掉了!测试成功!


改(Update)​

代码展示:

1)方法代码:

@Mapper
public interface UserInfoMapper {/*** 3.更新操作*/@Update("update user_info set password = #{password},age = #{age},gender = #{gender} where id = #{id}")Integer updateUserInfo(UserInfo userInfo);
}

2)测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid updateUserInfo() {UserInfo userInfo = new UserInfo();userInfo.setId(1);userInfo.setPassword("1122334455");userInfo.setAge(88);userInfo.setGender(1);Integer result = userInfoMapper.updateUserInfo(userInfo);System.out.println("影响行数: "+result);}}
}

如上述代码,是要把id为1的数据更新!

结果展示:

如上述三张图所示,id为1的数据已经更新了!测试成功!


查(Select)​

1)起别名​

代码展示:

1)方法代码:

@Mapper
public interface UserInfoMapper {// 1.起别名@Select("select id,username,password,age,gender,phone,delete_flag as " +"deleteFlag,create_time as createTime,update_time as updateTime from user_info")List<UserInfo> getUserInfoByUsername();
}

2)测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoByUsername() {System.out.println(userInfoMapper.getUserInfoByUsername());}}

结果展示:

如上图所示,数据库中的所有内容都展示出来了!测试成功!【包括UserInfo属性与数据库中字段不同的部分也已经显示出来了!】,即映射上了!

2)结果映射

代码展示:

1)方法代码:

@Mapper
public interface UserInfoMapper {// 2.结果映射【结果能不能复用呢?】@Results(id = "resultMap",value = {@Result(column = "delete_flag",property = "deleteFlag"),@Result(column = "create_time",property = "createTime"),@Result(column = "update_time",property = "updateTime")})@Select("select * from user_info")List<UserInfo> getUserInfoByResult();
}

如上述代码所示,是利用注解@Result的Map映射

2)测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoByResult() {System.out.println(userInfoMapper.getUserInfoByResult());}
}

结果展示:

3)开启驼峰命名(推荐)​

代码展示:

1)方法代码:

@Mapper
public interface UserInfoMapper {// 3.驼峰自动转换的方式@Select("select * from user_info")List<UserInfo> getUserInfoByChange();
}
mybatis:configuration:map-underscore-to-camel-case: true

上述这个代码是一个配置文件,要更新我们的配置文件才能使用这个功能!

2)测试代码:

package org.example.mybatis_test.Mapper;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoByChange() {System.out.println(userInfoMapper.getUserInfoByChange());}
}

结果展示:


MyBatis XML配置文件

Mybatis的开发有两种方式:​
        1. 注解
        2. XML

上面学习了注解的方式, 接下来我们学习XML的方式​
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能. 如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中. ​
MyBatis XML的方式需要以下两步:​
        1. 配置数据库连接字符串和MyBatis​
        2. 写持久层代码


配置连接字符串和MyBatis​

#数据库相关配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: yqy,25602Xdriver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:mybatis/**Mapper.xml

写持久层代码​

1)方法定义 Interface

2)方法实现: XXX.xml​

如上图所示,这个.xml文件的路径要按照配置文件中的路径!


添加 mapper 接口​

添加 UserInfoXMLMapper.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="org.example.mybatis.mapper.UserInfoXmlMapper"></mapper>

这个文件也一定要根据自己的路径来配!【即mapper标签下的,namespace属性】


增删改查操作​

1)增(Insert)

代码展示:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper    
public interface UserInfoXmlMapper {/*** 1.增*/Integer insert(UserInfo userInfo);
}
    <insert id="insert">insert into user_info (username,password,age,gender)values (#{username},#{password},#{age},#{gender})</insert>
package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; /*** 1.增*/@Testvoid insert() {UserInfo userInfo = new UserInfo();userInfo.setUsername("云惠强");userInfo.setPassword("8999999998");userInfo.setAge(45);userInfo.setGender(2);Integer result = userInfoXmlMapper.insert(userInfo);System.out.println("插入的数量: "+result);}
}

结果展示:

如上图所示,数据库中出现了新的数据!测试成功!

2)删(Delete)

代码展示:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper    
public interface UserInfoXmlMapper {/*** 2.删除*/Integer delete(Integer id);
}
    <delete id="delete">delete from user_info where id = #{id}</delete>
package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid delete() {Integer result = userInfoXmlMapper.delete(13);}
}

结果展示:

如上图所示,我们的代码是要删除id为13的数据!id为13的数据成功删除!测试成功!

3)改(Update)

代码展示:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper    
public interface UserInfoXmlMapper {/*** 3.改【更新】*/Integer update(UserInfo userInfo);
}
/*** 3.改【更新】*/Integer update(UserInfo userInfo);
package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid update() {UserInfo userInfo = new UserInfo();userInfo.setId(11);userInfo.setPassword("5201314");userInfo.setAge(21);userInfo.setGender(1);Integer result = userInfoXmlMapper.update(userInfo);System.out.println("影响行数: "+result);}
}

结果展示:

如图所示,

4)查(select)

(1)起别名

这里起别名的方式和注解起别名的方式一致。

(2)结果映射

代码展示:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper    
public interface UserInfoXmlMapper {/*** 3.改【更新】*/Integer update(UserInfo userInfo);
}
    <resultMap id="BaseMap" type="org.example.mybatis.model.UserInfo"><id column="id" property="id"></id><result column="delete_flag" property="deleteFlag"></result><result column="create_time" property="createTime"></result><result column="update_time" property="updateTime"></result></resultMap><select id="selectAllUser2" resultMap="BaseMap">select * from user_info</select>
package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid selectAllUser2() {System.out.println(userInfoXmlMapper.selectAllUser2());}
}

结果展示:

(3)开启驼峰命名

这个方式也与注解的方式一致,只要在yml文件中进行配置相关文件即可!


其他查询操作​

多表查询​

现在我们的数据库中有两张表,如下图所示:

代码展示:

package org.example.mybatis.model;
import lombok.Data;
import java.util.Date;
@Data
public class ArticleInfo {private Integer id;private String title;private String content;private Integer uid;private Integer deleteFlag;private Date createTime;private Date updateTime;private String username;private Integer gender;
}
package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.mybatis.model.ArticleInfo;
@Mapper
public interface ArticleInfoMapper {ArticleInfo selectByPrimaryKey(Integer id);ArticleInfo selectArticleAndUserById(Integer id);
}
<?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="org.example.mybatis.mapper.ArticleInfoMapper"><select id="selectByPrimaryKey" resultType="org.example.mybatis.model.ArticleInfo">select * from articleinfo where id = #{id}</select><select id="selectArticleAndUserById" resultType="org.example.mybatis.model.ArticleInfo">select ta.*,tb.username,tb.gender from articleinfo taleft join user_info tb on ta.uid = tb.id where ta.id = #{id}</select></mapper>
package org.example.mybatis.mapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class ArticleInfoMapperTest {@Autowiredprivate ArticleInfoMapper articleInfoMapper;@Testvoid selectByPrimaryKey() {System.out.println(articleInfoMapper.selectByPrimaryKey(1));}@Testvoid selectArticleAndUserById() {System.out.println(articleInfoMapper.selectArticleAndUserById(1));}
}

结果展示:


#{} 和 ${} ​【重点】

MyBatis 参数赋值有两种方式, 咱们前面使用了 #{} 进行赋值, 接下来我们看下二者的区别​

#{} 和${} 使用​

代码展示:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** #和 $ 的区别*/// 为什么使用#而不使用$符号呢?使用一个例子来进行说明@Select("select * from user_info where username = #{username}")UserInfo getUserName(String username);@Select("select * from user_info where username = ${username}")UserInfo getUserName2(String username);
}

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; // # :预编译sql@Testvoid getUserName() {System.out.println(userInfoMapper.getUserName("wangwu"));}// $【出错】 :即时sql@Testvoid getUserName2() {System.out.println(userInfoMapper.getUserName2("wangwu"));}
}

结果展示:

结果一【${}】:

结果二【#{}】:

如上图的两个结果,当我们使用${}的时候报错了,但是使用#{}没有报错,这两个不都是传参数吗?为什么一个会编译通过,一个却出现了sql语句错误的信息?这个问题留到后面讲二者去别的时候再解答。同时小编又发现了,当我们传的参数是Integer类型的时候,两者都可以拿到正确的结果,这又是为什么呢?


#{} 和${} 区别

#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别.​

1. 性能更高

绝大多数情况下, 某一条 SQL 语句可能会被反复调用执行, 或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同, update 的 set 子句值不同, insert 的 values 值不同). 如果每次都需要经过上面的语法解析, SQL优化、SQL编译等,则效率就明显不行了. 

预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同), 省去了解析优化等过程, 以此来提高效率​

2. 更安全(防止SQL注入)​

SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。

下面举个例子更便于理解:

如上图所示,我们可以看到这张表里面的所有数据,当我们使用where语句,只想找到username为"admin"的这个人的信息的时候,我们加上了一个or语句却查到了所有人的信息,这是一个相当危险的操作!!!

这个sql注入的情况最经常发生在用户登录的情况!


排序功能​

对于这个排序功能,我们使用分别使用#{} 和${}来试验一下,看会不会报错?是只有其中一个行,还是两个都行?还是两个都不行?还是在某一些条件下行,在某一些条件下不信?实践会告诉我们答案。

代码示例:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** 排序之使用 # 和 $ 的区别*/@Select("select * from user_info order by id #{order}")List<UserInfo> queryUserListByOrder(String order);@Select("select * from user_info order by id ${order}")List<UserInfo> queryUserListByOrder2(String order);
}

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; // 报错[#]@Testvoid queryUserListByOrder() {System.out.println(userInfoMapper.queryUserListByOrder("asc"));}// 没报错[$]@Testvoid queryUserListByOrder2() {System.out.println(userInfoMapper.queryUserListByOrder2("desc"));}
}

结果展示:

结果一【${}】:

结果二【#{}】:

如上述的两个结果可知,当使用#{}发生了报错,而使用${}的时候却能运行成功。这这这不对啊,我们刚刚才说#{}性能更强啊,怎么会报错呢?让我们一起来分析一下这个报错信息,这里出现了' 'asc' ' 出现了两个单引号,导致识别不出来了。主要原因是因为,#{}当识别到这个类型是一个String类型的时候就会自动加上一对引号,导致最后sql语句的错误!


like 查询​

那我们又要探讨一下这个like语句了到底是使用#{}还是${},因为在sql语句中,是使用类似于下图

我们想让这个引号加在'% %'百分号的两边,而不是参数的两边,而#{}只能加载参数的两边,${}又不加引号。有想法我们就大胆去尝试,实验出真知!我们要实践才能知道结果

代码展示:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** like查询问题*/@Select("select * from user_info where username like '%${username}%'")List<UserInfo> queryUserListByLike(String username);
}

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid queryUserListByLike() {userInfoMapper.queryUserListByLike("yun");}
}

结果展示:

结果展示1【${}】:

结果展示2【#{}】:

结果如上展示,${}虽然能正确展示结果,但是毕竟还是有注入的风险。那有没有什么办法能够解决这个问题的呢?那当然了,我们程序员最擅长的事情就是偷懒了!


内置函数 concat() 

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** 既然like查询使用$符号,就要考虑到注入得问题* 那怎么解决呢?* 使用CONCAT函数?*/@Select("select * from user_info where username like CONCAT('%',#{username},'%')")List<UserInfo> queryUserListByLike2(String username);
}

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid queryUserListByLike2() {userInfoMapper.queryUserListByLike2("yun");}
}

结果展示:

如上图所示,能正确得得到结果!测试成功!方法生效!


动态sql

动态 SQL 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接​。

动态 SQL 是指在程序运行时根据条件或参数的不同生成和执行 SQL 语句的技术,与在编译时就确定好的静态 SQL 相对。其主要思想是依据不同需求和条件,灵活生成可变的 SQL 语句,涵盖查询、插入、更新、删除等操作 。

<if>标签​

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** <if test = "">标签</if>* @param userInfo* @return*/Integer insertByCondition(UserInfo userInfo);
}

xml代码:

    <select id="insertByCondition" >insert into user_info(username,password,age<if test="gender != null">, gender</if>) values (#{username},#{password},#{age}<if test="gender != null">, #{gender}</if>)</select>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid insertByCondition() {UserInfo userInfo = new UserInfo();userInfo.setUsername("云鹏");userInfo.setPassword("33445566");userInfo.setAge(18);System.out.println(userInfoXmlMapper.insertByCondition(userInfo));}
}

如上述代码可知,我们使用if标签,让gender这个字段变得动态起来。如果有参数传入则接受,如果没有参数传入则为空。

结果展示:

如上图所示,id为5的user的gender字段为默认的0;


但是if标签其实有bug,就是逗号的问题,那我们就需要使用另外一个功能较大的标签来进行解决

<trim>标签

之前的插入用户功能,只是有一个 gender 字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。标签中有如下属性:

• prefix:表示整个语句块,以prefix的值作为前缀​
• suffix:表示整个语句块,以suffix的值作为后缀​
• prefixOverrides:表示整个语句块要去除掉的前缀​
• suffixOverrides:表示整个语句块要去除掉的后缀​

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** <trim>标签</trim>* 是否能解决逗号问题*/Integer insertByCondition2(UserInfo userInfo);
}

xml代码:

    <select id="insertByCondition2">insert into user_info<trim prefix="(" suffix=")" suffixOverrides="," ><if test="username!= null">username,</if><if test="password != null">password,</if><if test="age != null">age,</if><if test="gender != null">gender</if></trim>values<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="age != null">#{age},</if><if test="gender != null">#{gender}</if></trim></select>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid insertByCondition2() {UserInfo userInfo = new UserInfo();userInfo.setGender(2);System.out.println(userInfoXmlMapper.insertByCondition2(userInfo));}
}

结果展示:

结果如上图所示,id为6的user,只有一个gender的信息!测试成功,方法失效!


<where>标签​

在日常生活中,我们需要一些筛选的功能。比如小编在逛拼多多的时候,小编就喜欢筛选免邮费的!【毕竟小编是一个穷鬼!】

1)报错版本:

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** <where>标签*/// 报错List<UserInfo> queryUserByCondition(UserInfo userInfo);
}

xml代码:

    <select id="queryUserByCondition" resultType="org.example.mybatis.model.UserInfo">select * from user_infowhere<if test="age != null">age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="deleteFlag != null">and delete_flag = #{deleteFlag}</if></select>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid queryUserByCondition() {UserInfo userInfo = new UserInfo();userInfo.setGender(1);System.out.println(userInfoXmlMapper.queryUserByCondition(userInfo));}
}

结果展示:

结果如上图,报错了已经!这里报错的主要原是因为这里只传了gender这个参数,导致前面的and无法被sql语句识别到!所以进行了报错,因此解决方法是将多余的and去除!这时候我们第一时间想到了我们刚刚去多余的逗号使用的<trim>标签。

2)<trim>标签版本

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {// 正确【<trim>标签】List<UserInfo> queryUserByCondition2(UserInfo userInfo);
}

xml代码:

    <select id="queryUserByCondition2" resultType="org.example.mybatis.model.UserInfo">select * from user_info<trim prefix="where" prefixOverrides="and"><if test="age != null">age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="deleteFlag != null">and delete_flag = #{deleteFlag}</if></trim></select>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid queryUserByCondition2() {UserInfo userInfo = new UserInfo();userInfo.setGender(1);System.out.println(userInfoXmlMapper.queryUserByCondition2(userInfo));}
}

结果展示:

如上图所示,我们查找了表中所有gender为1的user信息!

3)最简洁的版本:

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {// 更简洁的标签【<where标签>】List<UserInfo> queryUserByCondition3(UserInfo userInfo);
}

xml代码:

    <select id="queryUserByCondition3" resultType="org.example.mybatis.model.UserInfo">select * from user_info<where><if test="age != null">age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="deleteFlag != null">and delete_flag = #{deleteFlag}</if></where></select>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid queryUserByCondition3() {UserInfo userInfo = new UserInfo();userInfo.setGender(1);System.out.println(userInfoXmlMapper.queryUserByCondition2(userInfo));}
}

结果展示:

如上图的控制台信息可知,我们也是顺利通过<where>标签获取了表中符合标准的信息!


<set>标签

需求: 根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容.​
接口定义: 根据传入的用户 id 属性,修改其他不为 null 的属性​

1)<trim>标签版本

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** 更新的动态sql,传值才更新,不传值不更新*/// <trim>标签List<UserInfo> updateByCondition(UserInfo userInfo);
}

xml代码:

    <update id="updateByCondition">update user_info<trim prefix="set" suffixOverrides=","><if test="password != null">password = #{password},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender}</if></trim>where id = #{id}</update>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid updateByCondition() {UserInfo userInfo = new UserInfo();userInfo.setId(1);userInfo.setPassword("55666778899");userInfo.setAge(33);userInfo.setGender(2);Integer result = userInfoXmlMapper.update(userInfo);System.out.println("影响行数: "+result);}
}

结果展示:

如上述结果展示,id为1的user已经更新!

2)简洁版本【<set>标签】

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** 更新的动态sql,传值才更新,不传值不更新*/// 简洁版本<set>List<UserInfo> updateByCondition2(UserInfo userInfo);
}

xml代码:

<update id="updateByCondition2"><set><if test="password != null">password = #{password},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender}</if></set>where id = #{id}</update>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid updateByCondition2() {UserInfo userInfo = new UserInfo();userInfo.setId(4);userInfo.setPassword("88990033");userInfo.setAge(36);userInfo.setGender(1);Integer result = userInfoXmlMapper.update(userInfo);System.out.println("影响行数: "+result);}
}

结果展示:

如上述结果所示,id为4的use的部分信息也得到了更新!


<foreach>标签​

对集合进行遍历时可以使用该标签。标签有如下属性:
• collection:绑定方法参数中的集合,如 List,Set,Map或数组对象​
• item:遍历时的每一个对象​
• open:语句块开头的字符串​
• close:语句块结束的字符串​
• separator:每次遍历之间间隔的字符串​
需求: 根据多个userid, 删除用户数据​

mapper代码:

package org.example.mybatis.mapper;
import org.apache.ibatis.annotations.*;
import org.example.mybatis.model.UserInfo;
import java.util.List;
@Mapper
public interface UserInfoMapper {/*** <foreach><foreach/>* 对集合进行遍历【批量插入、批量删除】*/// 1.批量删除Integer batchDelete(List<Integer> ids);
}

xml代码:

    <delete id="batchDelete">delete from user_infowhere id in<foreach collection="ids" open="(" close=")" item="id" separator=",">#{id}</foreach></delete>

测试代码:

package org.example.mybatis.mapper;
/**
这里的包我就不展示了,需要再导入
*/
@SpringBootTest
class UserInfoXmlMapperTest {@Autowiredprivate UserInfoXmlMapper userInfoXmlMapper; @Testvoid batchDelete() {List<Integer> ids = Arrays.asList(new Integer[]{4,5,6});userInfoXmlMapper.batchDelete(ids);}

结果展示:

结果如图所示,id为4,5,6的user信息全部被删除了!


相关文章:

MyBatis 操作数据库

目录 什么是MyBatis? 注释 Mapper注释的介绍和使用 Select注释的介绍和使用 SpringBootTest注释的介绍和使用 Test注释的介绍的使用 MyBatis入门​ 1&#xff09;准备工作 <1>创建工程​ <2>数据准备​ 2&#xff09;配置数据库连接字符串 3&#xff…...

蓉光:科技与自然的千年交响

故事背景 故事发生在中国四川成都&#xff0c;这座千年古城在近未来完成蜕变&#xff0c;青城山的云雾与锦江的碧波间&#xff0c;智能建筑如雨后春笋般生长。全城建筑采用太阳能皮肤&#xff0c;街道流淌着数字化的都江堰水系&#xff0c;杜甫草堂的飞檐与机械芙蓉树共舞&…...

[C语言]gets和fgets函数区别及详解

一、gets 每当讨论 gets 函数时&#xff0c;大家不由自主地就会想起 1988 年的“互联网蠕虫”&#xff0c;它在 UNIX 操作系统的 finger 后台程序中使用一个 gets 调用作为它的攻击方式之一。很显然&#xff0c;对蠕虫病毒的实现来说&#xff0c; gets 函数的功劳不可小视。不…...

【场景应用3】audio_classification:音频分类的微调

1 引言 本笔记展示了如何对多语种预训练的语音模型进行微调,以实现自动语音识别(Automatic Speech Recognition)。 本笔记旨在使用SUPERB数据集中的关键词检测子集,并且可以使用任何来自模型库(Model Hub)的语音模型检查点,只要该模型有一个包含序列分类头(Sequence …...

【前端】【难点】前端富文本开发的核心难点总结与思路优化

前端富文本开发的核心难点总结 富文本编辑器在前端开发中广泛应用于内容管理系统、文章发布、评论区等场景。其开发与集成存在较多复杂性&#xff0c;涵盖内容结构管理、交互体验、跨平台兼容性等方面&#xff0c;以下逐项分析。 二、富文本开发的具体难点分析 &#xff08;一…...

如何优雅使用 ReentrantLock 进行加解锁:避免常见坑点,提高代码可维护性

引言&#xff1a;锁的基本概念和问题 在多线程编程中&#xff0c;为了确保多个线程在访问共享资源时不会发生冲突&#xff0c;我们通常需要使用 锁 来同步对资源的访问。Java 提供了不同的锁机制&#xff0c;其中 ReentrantLock 是一种最常用且功能强大的锁&#xff0c;它属于…...

帕金森患者行动迟缓,日常生活怎么破局?

帕金森病&#xff0c;是一种常见于中老年人的神经退行性疾病&#xff0c;正悄然改变着无数患者的生活轨迹。它初期症状隐匿&#xff0c;常以手抖为信号&#xff0c;起初可能只是在安静状态下&#xff0c;手部出现轻微且有节律的震颤&#xff0c;随着时间推移&#xff0c;震颤逐…...

7-openwrt-one通过web页面配置访客网络、无线中继等功能

前几个章节一直在介绍编译、分区之类的,都还没正常开始使用这个路由器的wifi。默认wifi是没有启动的,前面还是通过手动修改uci配置启动的,这个章节介绍下官方web页面的使用。特别是访客网络、无线中继 1、开启wifi,配置wifi基本信息 我们使用有线连接路由器,通过192.168.…...

塑造现代互联网的力量:Berkeley在网络领域的影响与贡献

引言 “Berkeley” 这个名字在计算机网络和互联网领域中具有举足轻重的地位&#xff0c;许多关键的技术、协议和工具都与其紧密相关。它与 加利福尼亚大学伯克利分校&#xff08;UC Berkeley&#xff09; 密切相关&#xff0c;该校在计算机科学与网络研究中做出了许多开创性的…...

大数据学习(105)-Hbase

&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一…...

c# 系列pdf转图片 各种处理3--net3.1到net8 PDFtoImage

最近一直在做pdf渲染图片的问题&#xff0c;nuget PDFtoImage 支持3.1到net8 &#xff0c;直接上代码 private static void DownloadFileAsync(string url, string localPath){using (HttpClient client new HttpClient()){client.DefaultRequestHeaders.Add("User-Agen…...

宁德时代25年春招笔试演绎数字推理SHL测评题库

宁德时代校招测评包含演绎推理数字推理两部分&#xff0c;请单击以下链接进行测评&#xff0c;详细操作指引请参见如下指引&#xff0c;请在测试前了解&#xff0c;大约用时60分钟。正式测评有两个部分:数字推理18分钟演绎推理18分钟&#xff0c;数字推理共10题&#xff0c;演绎…...

C# 看门狗策略实现

using System; using System.Threading;public class Watchdog {private Timer _timer;private volatile bool _isTaskAlive;private readonly object _lock new object();private const int CheckInterval 5000; // 5秒检测一次private const int TimeoutThreshold 10000; …...

聊透多线程编程-线程池-5.C# 线程池(ThreadPool)详解

1. 线程池的基本概念 线程池的作用 由于每创建一个线程都需要该线程分配一定的内存空间&#xff0c;因此创建大量线程会导致内存使用量迅速增加&#xff0c;并可能导致性能问题。线程池的主要目的是减少线程创建和销毁的开销&#xff0c;从而提高程序性能。线程池维护了…...

清华DeepSeek教程又双叒叕更新了!(共7份PDF下载)

清华团队的DeepSeek教程又双叒叕更新了&#xff0c;目前共计有7份DeepSeek的教程&#xff0c;分别是《DeepSeek从入门到精通》、《DeepSeek赋能职场》、《普通人如何抓住DeepSeek红利》、《DeepSeekDeepResearch&#xff1a;让科研像聊天一样简单》、《DeepSeek与AI幻觉》、《A…...

免费在线文档工具,在线PDF添加空白页,免费在任意位置插入空白页,多样化的文件处理

小白工具&#xff08;https://www.xiaobaitool.net/files/pdf-add-page/ &#xff09;是一款免费的在线文档工具&#xff0c;专注于为用户提供便捷的 PDF 空白页添加服务。 功能特点&#xff1a;该工具支持在 PDF 文件的任意位置插入单页或多页空白页&#xff0c;能满足用户不同…...

MATLAB在哪些特定领域比Python更有优势?

文章目录 前言科学研究与工程计算数值计算信号处理控制系统设计 教育领域易于学习和上手教学资源丰富 快速原型开发集成开发环境便捷 前言 MATLAB 在以下特定领域比 Python 更具优势&#xff1a; 科学研究与工程计算 数值计算 高效矩阵运算&#xff1a;MATLAB 以矩阵为基本数…...

CAN协议

CAN简介 TJA1050(高速CAN收发器) 5V供电 界定符用来隔开各个数据 这个时候就要用到采样了 谁先谁后&#xff1f;&#xff1f;仲裁机制 发送邮箱用来放帧的 正常模式&#xff1a;正常收发 静默模式&#xff1a;只收不发 环回模式&#xff1a;不读&#xff0c;自己收 环回静…...

MFC案例:用鼠标移动窗口图像的实验

当使用基于对话框的MFC项目窗口显示图像时&#xff0c;如窗口的尺寸小于图像的尺寸&#xff0c;在不做缩放的情况下按照原图尺寸在窗口显示&#xff0c;那么只能看到图像的局部&#xff0c;这时我们希望可以通过鼠标移动图像进而显示其它部分。今天就进行这个实验&#xff0c;编…...

Linux基础IO(五)之用户缓冲区

文章目录 缓冲区FILE初步实现缓冲区 缓冲区 FILE 因为IO相关函数与系统调用接口对应&#xff0c;并且库函数封装系统调用&#xff0c; 所以本质上&#xff0c;访问文件都是通过fd访问的。 所以C库当中的FILE结构体内部&#xff0c;必定封装了fd。 编写代码and查看现象&…...

【Scrapy】Scrapy教程12——中间件

中间件这部分算是一个高阶的Scrapy内容,即便不了解这部分也可以使用Scrapy,但是一些特殊情况使用中间件就比较方便处理了,比如修改请求和响应等。 通过之前的工作原理图中,我们了解到Scrapy中有两个中间件,分别是下载器中间件和爬虫中间件,本节将一一讲解如何激活、编写自…...

C++学习之ORACLE①

目录 1.ORACLE数据库简介 2..ORACLE数据库安装 3..ORACLE体系结构 4..ORACLE基本概念 5..ORACLE基本元素 6..ORACLE数据库启动和关闭 7.SQLPLUS登录ORACLE数据库相关操作 8.SQLPLUS的基本操作 9.oracle中上课使用的方案 10.SQL语言分类 11.SQL中的select语句语法和注…...

vue---按钮防抖和节流----项目问题

一般来说前端都需要做按钮防抖避免一个时间被重复触发&#xff0c;首先可能会出现bug&#xff0c;消耗服务器性能&#xff0c;用户体验也不是很好。 1.防抖 解决方法&#xff1a;main.js文件自定义指令 Vue.directive("preventReClick", {inserted(el, binding) {…...

【LunarVim】解决which-key 自定义键位注册不成功问题

问题描述 LunarVim将which-key设置放在一个keymaps.lua中&#xff0c;然后config.lua调用reload “user.keymaps”&#xff0c;键位没用注册成功&#xff0c;而直接写在config.lua中&#xff0c;就注册成功 这暴露了LunarVim 插件和配置加载顺序的一些细节坑&#xff0c;下面解…...

湖北趣豆智能科技有限公司浅析XR技术对传统游戏的影响

在科技飞速发展的当下&#xff0c;XR&#xff08;扩展现实&#xff09;技术正以前所未有的态势重塑游戏行业格局。湖北趣豆智能科技有限公司凭借在XR技术与游乐设备融合领域的创新实践&#xff0c;对XR技术给传统游戏带来的影响有着深刻见解。 沉浸体验升级&#xff0c;重塑游戏…...

[操作系统] 进程间通信:system V共享内存

文章目录 内存共享示意图共享内存的管理代码shmget 函数原理先创建共享内存如何保证两个不同的进程拿到的是一块共享内存 shmctl 函数IPC_RMID参数 shmat函数无同步机制同步机制是什么模拟演示非同步效果如何提供保护机制 shmdt再谈描述共享内存的数据结构struct shmid_dsstruc…...

科技快讯 | 阿里云百炼MCP服务上线;英伟达官宣:CUDA 工具链将全面原生支持 Python

李飞飞团队最新AI报告&#xff1a;中美模型性能差距近乎持平 4月8日&#xff0c;斯坦福大学以人为本人工智能研究所发布《2025年人工智能指数报告》。报告显示&#xff0c;2023年AI性能显著提升&#xff0c;AI应用加速&#xff0c;投资增长&#xff0c;中美AI模型差距缩小。报告…...

踩雷,前端一直卡在获取token中

问题&#xff1a;一直卡在var token SecureStorage.Default.GetAsync("auth_token").Result; public VideoService(){_httpClient new HttpClient();var token SecureStorage.Default.GetAsync("auth_token");} 这是一个典型的同步等待异步操作导致的死…...

MATLAB双目标定

前言&#xff1a; 现在有许多双目摄像头在出厂时以及标定好&#xff0c;用户拿到手后可以直接使用&#xff0c;但也有些双目摄像头在出厂时并没有标定。因而这个时候就需要自己进行标定。本文主要介绍基于matlab工具箱的自动标定方式来对双目相机进行标定。 1、MATLAB工具箱标…...

4.10学习总结

完成两道算法题&#xff08;感觉对贪心的思路很难去想到如何解&#xff09; 完成stream流的学习&#xff0c;开始学习方法引用...

QML自定义组件

自定义组件是 QML 开发中的核心概念&#xff0c;它允许您创建可重用的 UI 元素和逻辑单元。以下是创建和使用自定义组件的完整方法。 1. 基本自定义组件创建 创建单独组件文件 (推荐方式) qml // MyButton.qml&#xff08;单独一个qml文件&#xff09;import QtQuick 2.15R…...

多模态大语言模型arxiv论文略读(十)

Towards End-to-End Embodied Decision Making via Multi-modal Large Language Model: Explorations with GPT4-Vision and Beyond ➡️ 论文标题&#xff1a;Towards End-to-End Embodied Decision Making via Multi-modal Large Language Model: Explorations with GPT4-Vi…...

关于 Spring Boot + Vue 前后端开发的打包、测试、监控、预先编译和容器部署 的详细说明,涵盖从开发到生产部署的全流程

以下是关于 Spring Boot Vue 前后端开发的打包、测试、监控、预先编译和容器部署 的详细说明&#xff0c;涵盖从开发到生产部署的全流程&#xff1a; 1. 打包 1.1 后端&#xff08;Spring Boot&#xff09; 打包方式 使用 Maven 或 Gradle 打包成可执行的 JAR/WAR 文件&…...

【Raqote】 1.1 路径填充ShaderMaskBlitter 结构体(blitter.rs)

ShaderMaskBlitter 结构体实现了 Blitter trait&#xff0c;用于带遮罩的着色器渲染。 结构体定义 pub struct ShaderMaskBlitter<a> {pub x: i32, // 目标区域的起始x坐标pub y: i32, // 目标区域的起始y坐标pub shader: &a dyn Shader, //…...

如何用 esProc 实现 Oracle 和 MySQL 的混合运算

逻辑数仓可以实现多源混算&#xff0c;但需要配置视图、预处理数据&#xff0c;结构太沉重。duckdb 是轻量级的方案&#xff0c;但没有内置 Oracle 的 connector&#xff0c;自己开发难度又太高。同为轻量级方案&#xff0c;esProc 支持 JDBC 公共接口&#xff0c;可以实现任何…...

zabbix和prometheus选择那个监控呢

文章目录 Zabbix 介绍概述架构组成特点适用场景 Prometheus 介绍概述架构组成特点适用场景 Zabbix vs Prometheus 对比架构与组件Zabbix 架构Prometheus 架构 监控要点与最佳实践告警与可视化ZabbixPrometheus Alertmanager Grafana 伸缩与高可用ZabbixPrometheus 运维成本与…...

SQL 查询中使用 IN 导致性能问题的解决方法

当 SQL 查询中使用 IN 子句导致查询长时间运行或挂起时&#xff0c;通常是由于以下几个原因造成的&#xff1a; 常见原因 IN 列表中的值过多 - 当 IN 子句包含大量值时&#xff08;如数千或更多&#xff09;&#xff0c;数据库需要处理大量比较操作 缺乏合适的索引 - 被查询的…...

UML-饮料自助销售系统(无法找零)序列图

一、题目&#xff1a; 在饮料自动销售系统中&#xff0c;顾客选择想要的饮料。系统提示需要投入的金额&#xff0c;顾客从机器的前端钱币口投入钱币&#xff0c;钱币到达钱币记录仪&#xff0c;记录仪更新自己的选择。正常时记录仪通知分配器分发饮料到机器前端&#xff0c;但可…...

Go语言中的runtime包是用来做什么的?

在Go语言中&#xff0c;runtime包提供了与Go运行时系统的交互接口。以下是runtime包的主要功能和用途&#xff1a; 1. 运行时信息 runtime包可以获取关于Go程序运行时的信息&#xff0c;包括&#xff1a; 内存使用情况&#xff1a;可以查看内存分配和使用的统计信息&#xf…...

【Linux】用C++实现UDP通信:详解socket编程流程

文章目录 协议&#xff08;Protocol&#xff09;协议的核心要素常见协议分类 UDP协议&#xff08;用户数据报协议&#xff09;1. 基本定义2. 核心特性 UDP协议实现通信服务器端Comm.hppInetAddr.hppUdpServer.hppUdpServer.cc 客户端 总结 协议&#xff08;Protocol&#xff09…...

代码随想录-06-二叉树-02.二叉树的递归遍历

二叉树的递归遍历 递归思路 确定递归函数的参数parameter和返回值确定终止条件确定单层递归逻辑 具体代码 CPP 前序遍历 vector<int> res; void traversal(TreeNode *root){if(!root)return;res.push_back(root->val);traversal(root->left);traversal(root-…...

一文详解ffmpeg环境搭建:Ubuntu系统ffmpeg配置nvidia硬件加速

在Ubuntu系统下安装FFmpeg有多种方式,其中最常用的是通过apt-get命令和源码编译安装。本文将分别介绍这两种方式,并提供安装过程。 一、apt-get安装 使用apt-get命令安装FFmpeg是最简单快捷的方式,只需要在终端中输入以下命令即可: # 更新软件包列表 sudo apt-get updat…...

(四)深入理解AVFoundation-播放:高度自定义视频播放器 UI

引言 在之前的博客中&#xff0c;我们已经介绍了如何实现一个简单的播放器&#xff0c;并通过监听资源和播放器的属性来提升播放体验。因此本篇博客将带你进一步自定义播放器 UI。通过构建自己的播放控制界面&#xff08;如播放/暂停按钮、进度条、全屏切换等&#xff09;&…...

sqli-labs靶场 less6

文章目录 sqli-labs靶场less 6 报错注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&#xf…...

数据库架构全解析:MyCat、MHA、ProxySQL 的原理、功能与实例

前言 &#xff1a; 在分布式数据库架构中&#xff0c;分库分表、高可用性&#xff08;HA&#xff09;和查询优化是核心需求。本文将深入解析三款主流工具&#xff1a;MyCat&#xff08;分布式数据库中间件&#xff09;、MHA&#xff08;MySQL高可用方案&#xff09;、ProxySQL…...

【hadoop】Hive数据仓库安装部署

一、MySQL的安装与配置 换源&#xff1a; 最下面附加部分 1、在master上直接使用yum命令在线安装MySQL数据库&#xff1a; sudo yum install mysql-server 途中会询问是否继续&#xff0c;输入Y并按回车。 2、启动MySQL服务&#xff1a; sudo service mysqld start 3、设…...

Unity Addressables资源生命周期自动化监控技术详解

一、Addressables资源生命周期管理痛点 1. 常见资源泄漏场景 泄漏类型典型表现检测难度隐式引用泄漏脚本持有AssetReference未释放高异步操作未处理AsyncOperationHandle未释放中循环依赖泄漏资源相互引用无法释放极高事件订阅泄漏未取消事件监听导致对象保留高 2. 传统管理…...

Linux网络编程——深入理解TCP的可靠性、滑动窗口、流量控制、拥塞控制

目录 一、前言 二、流量控制 三、TCP的滑动窗口 1、原理 2、机制 3、数据重发 Ⅰ、只是确认应答包(ACK)丢了 Ⅱ、发送数据包丢失 4、缓冲区结构 四、TCP的拥塞控制 1、慢启动 2、拥塞避免 3、快速重传 4、快速恢复 五、延迟应答 六、捎带应答 七、再谈TCP的面…...

Manifold-IJ 2022.1.21 版本解析:IntelliJ IDEA 的 Java 增强插件指南

Manifold-IJ-2022.1.21 可能是 IntelliJ IDEA 的一个插件或相关版本&#xff0c;特别是与 Manifold 这个增强 Java 开发体验的框架相关的组件。 很多时候没有网络环境&#xff0c;而又需要这个插件。 Manifold-IJ 2022.1.21下载&#xff1a;https://pan.quark.cn/s/ad907344c…...

linux内核

一 初识linux内核 1.1操作系统和内核简介 操作系统的精确定义并没有一个统一的标准&#xff0c;这里我认为操作系统是指整个系统负责完成最基本功能和系统管理的那些部分 这些部分包括内核&#xff0c;设备驱动程序&#xff0c;启动引导程序&#xff0c;基本的文件管理工具和…...