MyBatis 用法详解

文章目录

  • 一、普通 SQL
    • 1.1 注解实现:
      • 1.1.1 参数传递:
      • 1.1.2 增(@Insert):
      • 1.1.3 删(@Delete):
      • 1.1.4 改(@Update):
      • 1.1.5 查(@Select):
        • 1.1.5.1 起别名:
        • 1.1.5.2 结果映射:
        • 1.1.5.3 开启驼峰命名(推荐):
    • 1.2 MyBatis XML 配置文件实现:
      • 1.2.1 添加 Mapper.xml
      • 1.2.2 增:
      • 1.2.3 删:
      • 1.2.4 改:
      • 1.2.5 查:
  • 二、#{} 和 ${} 的区别:
  • 三、动态 SQL:
    • 3.1 `<if> `标签:
    • 3.2 `<trim>`标签:
    • 3.3 `<where>`标签:
    • 3.4 `<set>`标签:
    • 3.5 `<foreach>`标签:
    • 3.6 `<include>`标签:

使用 MyBatis 需要导入依赖(MyBatis 和 MySQL(数据库都行)),和配置 yml 文件。导入对应的依赖比较简单,所以下面只给出 yml 文件的配置。

yml 文件配置如下。

下面用中文填写(除了注释)的都是要根据自己的情况来填写的。

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/对应数据库名称?characterEncoding=utf8&useSSL=falseusername: rootpassword: 对应数据库的密码(如果是纯数字记得加上单引号)driver-class-name: com.mysql.cj.jdbc.Drivermvc:favicon:enable: falseprofiles:  #多平台配置active: dev
# 设置 Mybatis 的 xml 保存路径
mybatis:mapper-locations: classpath:mapper/*Mapper.xmlconfiguration: # 配置打印 MyBatis 执行的 SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true  #自动驼峰转换

MyBatis 有两种实现方式。(1)注解(2)xml 文件实现。

一、普通 SQL

1.1 注解实现:

注解实现,其实就是在对应的注解里面写上 SQL 语句即可。

1.1.1 参数传递:

需求:查找 id=4 的用户,对应的 SQL 就是:select * from userinfo where id=4。

@Select("select username, `password`, age, gender, phone from userinfo where id = 4")
UserInfo queryById();

但是这样的话,只能查找 id=4 的数据,所以 SQL 语句中的 id 值不能写成固定数值,需要变为动态的数值。

解决方法:在 queryById 方法中添加一个参数(id),将方法中的参数,传给 SQL 语句。

使用#{}的方式获取方法中的参数。注意:#{}里面的名称需要和方法参数名称一致(如果方法参数是一个对象,那么#{}里面的值,要和对象的属性名称一样。)

@Select("select username, `password`, age, gender, phone from userinfo where id= #{id} ")
UserInfo queryById(Integer id);

如果 mapper 接口方法形参只有一个普通类型的参数,#{…}里面的属性名可以随便写,如:#{id}、# {value}。建议和参数名保持一致。

添加测试用例:

@Test
void queryById() {UserInfo userInfo = userInfoMapper.queryById(4);System.out.println(userInfo);
}

运行结果:

image-20241007150941216

也可以通过 @Param ,设置参数的别名,如果使用 @Param 设置别名,#{…}里面的属性名必须和 @Param 设置的一样。

@Select("select username, `password`, age, gender, phone from userinfo where id = #{userid} ")
UserInfo queryById(@Param("userid") Integer id);

如果参数是对象,设置了 @Param 属性,#{…} 需要使用 别名.属性 来获取。

//插入数据
@Insert("insert into userinfo(username,`password`,age,gender,phone) " +"values(#{gobeyye.username},#{gobeyye.password}" +",#{gobeyye.age},#{gobeyye.gender},#{gobeyye.phone})")
Integer insertUserInfo1(@Param("gobeyye") UserInfo userInfo);

1.1.2 增(@Insert):

SQL 语句:

insert into userinfo (username, `password`, age, gender, phone) values 
("zhaoliu","zhaoliu",19,1,"18700001234")

把 SQL 中的常量替换为动态的参数:

Mapper 接口的方法:

@Insert("insert into userinfo (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})")
Integer insert(UserInfo userInfo);

直接使用 UserInfo 对象的属性名来获取参数。

测试代码:

 @Testvoid insertUesrInfo1() {UserInfo userInfo = new UserInfo();userInfo.setUsername("zhaoliu");userInfo.setPassword("zhaoliu");userInfo.setAge(19);userInfo.setGender(1);userInfo.setPhone("18700001234");userInfoMapper.insertUesrInfo1(userInfo);}

运行结果:下面这个运行结果,需要导入日志配置。

image-20241007153531754

返回主键:

如果想要拿到自增 id(主键一般默认是 id),需要在 Mapper 接口的方法上添加一个 Options 的注解。

@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into userinfo (username, age, gender, phone) values (#{userinfo.username},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})")
Integer insert(@Param("userinfo") UserInfo userInfo);
  • useGeneratedKeys:这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。

  • keyProperty:指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素,设置它的值,默认值:未设置(unset)。

注意:设置 useGeneratedKeys = true 之后,方法返回值依然是受影响的行数,自增 id 会设置在上述 keyProperty 指定的属性中。

1.1.3 删(@Delete):

SQL 语句:

问号是占位符的意思,方便下面写代码,不是 SQL 语法支持的。

delete from userinfo where id = ?

Mapper 接口的方法:

@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);

1.1.4 改(@Update):

SQL 语句:

update userinfo set username="zhaoliu" where id=5

Mapper 接口的方法:

@Update("update userinfo set username=#{username} where id=#{id}")
void update(UserInfo userInfo);

1.1.5 查(@Select):

SQL 语句:

@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo")

Mapper 接口的方法:

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
List<UserInfo> queryAllUser();

查询结果:

image-20241007160052250

从运行结果上可以看到,我们 SQL 语句中,查询了 delete_flag,create_time,update_time 但是这几个属性却没有赋值。

原因分析:

当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性。

观察下图:

image-20241007160512525

我们可以发现,这是因为字段名和属性名称不一致导致的(命名规则不同导致的)。

解决方法:

  1. 起别名

  2. 结果映射

  3. 开启驼峰命名

1.1.5.1 起别名:

在 SQL 语句中,给列名起别名,保持别名和实体类属性名一样。

@Select("select id, username, `password`, age, gender, phone," +" delete_flag as deleteFlag, create_time as createTime, update_time as updateTime from userinfo")
public List<UserInfo> queryAllUser();
1.1.5.2 结果映射:
@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
@Results({@Result(column = "delete_flag", property = "deleteFlag"),@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUser2();

如果其他 SQL,也希望可以复用这个映射关系,可以给这个 Results 定义一个名称。

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
@Results(id="resultMap",value = {@Result(column = "delete_flag", property = "deleteFlag"),@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUser2();@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time " +"from userinfo where id= #{userid} ")
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);

使用 id 属性给该 Results 定义别名,使用 @ResultMap 注解来复用 Results。

1.1.5.3 开启驼峰命名(推荐):

通常数据库列使用蛇形命名法进行命名(下划线分割各个单词),而 Java 属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true(设置 application.properties 或者 application.yml 文件)。

mybatis:configuration:
#    日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#    自动驼峰转换map-underscore-to-camel-case: true

这时 Java 代码不做任何处理。字段就能全部正确赋值。

1.2 MyBatis XML 配置文件实现:

1.2.1 添加 Mapper.xml

首先根据 yml 文件中配置的 mapper 路径创建对应的 xml 文件。

image-20241007165055159

创建完成后,向里面填入 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.UserInfoXMlMapper"><select id="queryAllUser" resultType="com.example.demo.model.UserInfo">select username,`password`, age, gender, phone from userinfo</select>
</mapper>

以下是对以上标签的说明:

  • <mapper>标签:需要指定 namespace 属性,标识命名空间,值为 mapper 接口的全限定名,包括全包名.类名。
  • <select>查询标签:是用来执行数据库的查询操作的。select 中的属性解释如下:
  1. id :是和 Interface(接口)中定义的方法名称一样的,表示对接口的具体实现方法。
  2. resultType:返回的数据类型。
image-20241007170155891

其实 xml 增删改查和注解实现增删改查,其实差不多,无非就是一个使用注解,另一个使用标签。

1.2.2 增:

UserInfoMapper 接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml 实现:

<insert id="insertUser">insert into userinfo (username, `password`, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone})
</insert>

测试代码:

@Test
void insertUser() {UserInfo userInfo = new UserInfo();userInfo.setUsername("gobeyye");userInfo.setPassword("123456");userInfo.setAge(18);userInfo.setGender(1);userInfo.setPhone("12345678");userInfoXMLMapper.insertUser(userInfo);
}

效果如下:

image-20241008094127079

  • 返回自增 id:

接口定义不变,Mapper.xml 实现设置 useGeneratedKeys 和 keyProperty 属性。代码如下:

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">insert into userinfo (username, `password`, age, gender, phone)values (#{username}, #{password}, #{age},#{gender},#{phone})
</insert>

1.2.3 删:

UserInfoMapper 接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml 实现:

<delete id="deleteUser">delete from userinfo where id = #{id}
</delete>

1.2.4 改:

UserInfoMapper 接口:

Integer updateUser(UserInfo userInfo);

UserInfoMapper.xml 实现:

<update id="updateUser">update userinfo set username=#{username} where id=#{id}
</update>

1.2.5 查:

UserInfoMapper 接口:

List<UserInfo> queryAllUser();

UserInfoMapper.xml 实现:

<select id="queryAllUser" resultType="com.example.demo.model.UserInfo">select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time from userinfo
</select>

运行结果:

image-20241008095014817

结果显示:deleteFlag,createTime,updateTime 也没有进行赋值。

解决办法和注解类似:

  1. 起别名

  2. 结果映射

  3. 开启驼峰命名

其中 1 和 3 方法和注解一样,下面重点讲如何使用 xml 写结果映射。

Mapper.xml:

<resultMap id="BaseMap" type="com.gobeyye.mybatis.moder.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="queryAllUser" resultMap="BaseMap">select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time from userinfo
</select>
image-20241008100239710

多表查询和单表查询类似,只是 SQL 不同而已,不再多说。

二、#{} 和 ${} 的区别:

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

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

了解 SQL 语句的执行流程,有助于理解下面的区别。

SQL 语句的执行流程:

  1. SQL 语法校验和解析

  2. SQL 优化

  3. SQL 执行

  4. #{} 性能更高:

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

image-20241008101914520

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

  1. #{} 更安全(防止 SQL 注入,最重要的一点):

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

#{} 内部的值,只会被识别成参数,而 ${} 内部的值,既可以识别成参数也可以被识别成 SQL 语句。

  1. 特殊场景只能使用 ${}:

在排序功能中,我们可以选择升序或者降序。

Mapper 实现:

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);

部分结果如下:

image-20241008103313736

如果将 ${} 换成 #{},就会出现如下结果,并报错:

image-20241008103424177

这是因为 #{} 会根据参数类型判断,是否拼接引号''。如果参数类型为 String,就会加上引号。

当使用 #{sort} 查询时,asc 前后自动给加了引号,导致 sql 错误。

除此之外,还有表名作为参数时,也只能使用 ${},但是一定要注意 SQL 注入问题。

综上,能使用 #{} 的场景就使用 #{},实在不行再使用 ${},但是一定要注意 SQL 注入问题。

三、动态 SQL:

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

可以参考官方文档:https://mybatis.net.cn/dynamic-sql.html

由于动态 SQL 会复杂一点,所以一般将动态 SQL 写在 xml 文件中(使用注解也行)。下面就都使用 xml 文件的形式,注解就不再演示。

3.1 <if> 标签:

接口定义:

Integer insertUserByCondition(UserInfo userInfo);

Mapper.xml 实现:

<insert id="insertUserByCondition">INSERT INTO userinfo (username, `password`, age,<if test="gender != null">gender,</if>phone)VALUES (#{username},#{password}, #{age},<if test="gender != null">#{gender},</if>#{phone})
</insert>

测试代码如下:

@Test
void insertUserByCondition() {UserInfo userInfo = new UserInfo();userInfo.setUsername("gobeyye");userInfo.setPassword("123123");userInfo.setAge(18);userInfo.setPhone("999999");userInfoXMLMapper.insertUserByCondition(userInfo);
}

效果如下:可以看到 gender 由于属性没有值,所以该字段被去除了。

image-20241008105642300

这里有的友友可能会有疑问:把全部字段都加上,不进行赋值的字段,参数传为 null 不就行了?

答:不行,因为 MySQL 中,有的字段如果没有赋值,会有默认值(可以自己设定),如果传值为 null,就会把默认值覆盖掉,这是我们不希望看到的,所以只能使用 if 标签。

3.2 <trim>标签:

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

<trim>标签中有如下属性:

  • prefix:表示整个语句块,以 prefix 的值作为前缀。

  • suffix:表示整个语句块,以 suffix 的值作为后缀。

  • prefixOverrides:表示整个语句块要去除掉的前缀。

  • suffixOverrides:表示整个语句块要去除掉的后缀。

调整 Mapper.xml 的插入语句为:

<insert id="insertUserByCondition">insert into userinfo<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><if test="phone != null">phone,</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><if test="phone != null">#{phone}</if></trim>
</insert>

在上面的这段代码中,就可以使用 <trim>标签起到解决,多余后缀,的问题。

3.3 <where>标签:

where 标签解决的是,如果所有条件都加上了 if 标签,且都没有进行传值,这时 where 就会多出来,这时 SQL 语法就出错了,使用 where 标签就可以很好的解决上面的问题。

<where>只会在子元素有内容的情况下才插入 where 字句,而且会自动去除字句开头的 AND 或 OR。

<select id="queryByCondition" resultType="com.example.demo.model.UserInfo">select id, username, age, gender, phone, delete_flag, create_time,update_timefrom userinfo<where><if test="age != null">and age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="deleteFlag != null">and delete_flag = #{deleteFlag}</if></where>
</select>

3.4 <set>标签:

<set> :动态的在 SQL 语句中插入 set 关键字,并会删掉额外的逗号。(用于 update 语句中)。

Mapper.xml:

<update id="updateUserByCondition">update userinfo<set><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="deleteFlag != null">delete_flag = #{deleteFlag},</if></set>where id = #{id}
</update>

3.5 <foreach>标签:

对集合进行遍历时可以使用该标签。标签有如下属性:

  • collection:绑定方法参数中的集合,如 List,Set,Map 或数组对象。

  • item:遍历时的每一个对象。

  • open:语句块开头的字符串。

  • close:语句块结束的字符串。

  • separator:每次遍历之间间隔的字符串。

使用演示:

接口方法:

void deleteByIds(List<Integer> ids);

Mapper.xml:

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

3.6 <include>标签:

在 xml 映射文件中配置的 SQL,有时可能会存在很多重复的片段,此时就会存在很多冗余的代码。

image-20241008121124855

我们可以对重复的代码片段进行抽取,将其通过标签封装到一个 SQL 片段,然后再通过标签进行引用。

  • <sql>:定义可重用的 SQL 片段。
  • <include>:通过属性 refid,指定包含的 SQL 片段。

例如 SQL片段:

<sql id="allColumn">id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>

通过 include 标签进行引用。操作如下:

<select id="queryById" resultType="com.example.demo.model.UserInfo">select<include refid="allColumn"></include>from userinfo where id= #{id}
</select>

结语:
其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话,还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

贴吧软件怎么切换ip

在网络使用中&#xff0c;有时我们需要切换IP地址来满足特定的需求&#xff0c;比如需要切换贴吧软件IP以进行不同的操作。本文将介绍几种贴吧切换IP地址的方法&#xff0c;帮助用户更好地管理自己的网络身份和访问权限。 1、更换网络环境‌ 通过连接到不同的Wi-Fi网络或使用移…

微服务之间的相互调用的几种常见实现方式对比

目录 微服务之间的相互调用的几种实现方式 一、HTTP HTTP/RESTful API调用工作原理 二、RPC 设计理念与实现方式 协议与传输层 RPC远程调用工作原理 应用场景与性能考量 特点 三、Feign 设计理念与实现方式 协议与传输层 Feign调用的基本流程 Feign调用的工作原理…

钢铁行业3大改造方向 智能仪器亦起到重要作用!

钢铁企业新的改造方向主要包括超低排放改造、能效改造和数字化转型。‌这些政策旨在提升钢铁行业的环保水平、能效和智能化水平。其中智能仪器的加入&#xff0c;为钢铁企业数字化智能化自动化改造带来新的活力。 具体来说&#xff0c;到2027年&#xff0c;钢铁行业将实现以下目…

ubuntu-24.04.1 系统安装

使用VMware虚拟机上进行实现 官网下载地址&#xff1a; https://cn.ubuntu.com/download https://releases.ubuntu.com 操作系统手册&#xff1a; https://ubuntu.com/server/docs/ &#xff08;里面包含安装文档&#xff09; 安装指南&#xff08;详细&#xff09;&#xff1a…

华为云应用侧Android Studio开发

本文将介绍如何使用AndroidStudio开发APP完成与接入华为云IoTDA设备的对接&#xff0c;包括属性参数获以及取命令下发。 一、鉴权认证 应用侧需要通过IAM服务鉴权&#xff0c;获取token&#xff0c;华为账号创建 IAM 用户&#xff0c; 可以为创建的用户分配权限 认证鉴权_设…

PHP智慧餐饮新风尚点餐系统

智慧餐饮新风尚点餐系统 —— 美食与科技的完美碰撞 &#x1f37d;️ 开篇&#xff1a;智慧餐饮的崛起 在快节奏的现代生活中&#xff0c;智慧餐饮正逐渐成为我们日常的一部分。随着科技的飞速发展&#xff0c;餐饮行业也在不断创新&#xff0c;力求为顾客提供更加便捷、高效…

深信服上网行为管理AC无法注销在线用户

下图用户认证成功后无法注销 很多入网的用户都是使用的这个账号 针对单个IP强制注销也不生效 解决步骤&#xff1a; 接入管理-用户管理-用户绑定管理-用户绑定 删除绑定免认证的配置 删除后所有用户会强制注销掉&#xff0c;重新登录即可 可添加主页联系方式帮忙远程解决问…

codeforces- 973-div2----补题

1、求最小时间 思路&#xff1a;简单的模拟 木桶效应 #include<iostream> #include<algorithm> using namespace std; typedef long long ll; int dx[] { 0,1,0,-1 }; int dy[] { 1,0,-1,0 }; const ll N 2e5 5; const ll mod 1e9 7; ll a[N]; void solve…

免费又好用的保护网站WAF,基于语义引擎的waf雷池社区版推荐

为什么传统规则防护失效了&#xff1f;&#x1f914; 目前&#xff0c;大多数 Web 应用防火墙&#xff08;WAF&#xff09;依赖规则匹配来识别和阻断攻击流量。然而&#xff0c;随着 Web 攻击的低成本、复杂多样的手段和频繁爆发的高危漏洞&#xff0c;管理人员不得不频繁调整防…

pyQT5+vscode python开发环境搭建

1、下载安装python https://www.python.org/ftp/python/3.9.13/python-3.9.13-amd64.exe 注意&#xff1a;高版本python的pyQT5可能有兼容性问题,我之前装的python3.11时pyuic就不工作&#xff0c;就降级为3.9 2、安装pip 及 pyQT python -m ensurepip --default-pip pip i…

蓝桥杯【物联网】零基础到国奖之路:十六. 扩展模块之矩阵按键

蓝桥杯【物联网】零基础到国奖之路:十六. 扩展模块之矩阵按键 第一节 硬件解读第二节 CubeMX配置第三节 MDK代码 第一节 硬件解读 扩展模块和ADC模块是一摸一样的&#xff0c;插在主板上。 引脚对应关系&#xff1a; PB6-ROW1 PB7-ROW2 PB1-COLUMN1 PB0-COLUMN2 PA8-COLUMN3 …

骨传导耳机哪个牌子好?2024年度五大高分骨传导机型推荐!

骨传导耳机哪个牌子好&#xff1f;作为专业健身教练&#xff0c;我平日在训练的时候会使用骨传导耳机来听歌&#xff0c;不过&#xff0c;随着骨传导耳机热度逐渐提高&#xff0c;如今市场上骨传导耳机品牌繁多&#xff0c;类型各异&#xff0c;它们的质量差距也很大。很多网红…

【Java】多线程代码案例

多线程代码案例 单例模式初步了解饿汉模式懒汉模式线程安全问题分析存在的问题 生产者消费者模型初识生产者消费者模型初识阻塞队列生产者消费者模型的意义BlockingQueue阻塞队列模拟实现 定时器初识计时器初识Timer类初识 schedule() 方法简易定时器的实现思路讲解代码书写 线…

耳机座接口会被TYPE-C取代吗?

耳机座接口&#xff0c;即传统的3.5mm耳机插孔&#xff0c;一直以来都是音频设备的标准配置。然而&#xff0c;随着科技的发展和用户需求的变化&#xff0c;TYPE-C接口逐渐崭露头角&#xff0c;成为许多设备的主流选择。这一趋势引发了一个重要问题&#xff1a;耳机座接口会被T…

Collection 集合框架

Collection 集合框架 各类集合 Set TreeSet 基于红黑树实现&#xff0c;支持有序性操作&#xff0c;例如根据一个范围查找元素的操作。但是查找效率不如 HashSet&#xff0c;HashSet 查找的时间复杂度为 O(1)&#xff0c;TreeSet 则为 O(logN)。 HashSet 基于哈希表实现&…

php常用的注释符号

如果没有安装vscode和小皮&#xff0c;请点击下方链接安装&#xff1a; Vscode、小皮面板安装-CSDN博客 在学习php过程中&#xff0c;肯定少不了注释&#xff0c;也可以理解为备注的信息&#xff0c;来提醒自己这段代码有什么用&#xff0c;是什么意思等&#xff0c;接下来就介…

【Redis】网络模型(day10)

在本篇文章中&#xff0c;主要是对五种网络模型进行一个简单的介绍&#xff0c;然后对Redis4.0和6.0的网络模型进行一个概述。 用户空间和内核空间 在Linux系统上&#xff0c;分为用户空间、内核空间和硬件设备。硬件设备主要包括CPU、内存、网卡等物体&#xff0c;内核应用去…

QT开发--QT基础

第0章 QT工具介绍 0.1 编译工具 uic&#xff0c;rcc&#xff0c;moc&#xff0c;qmake 都是 qt 的工具 uic 主要是 编译 .ui文件 -> ui_xxx.h //.ui文件 .h rcc 主要是 编译 资源文件.qrc文件 -> xxx.rcc …

SpringBoot3.3 优雅启停定时任务

定时任务是非常常见的功能,在一个复杂的应用程序中,如何优雅地管理这些定时任务的启动与停止尤为重要。 Spring Boot 提供了强大的任务调度支持,通过@Scheduled注解可以轻松地创建定时任务,并且可以通过配置来灵活地管理这些任务的执行环境。在本文中,我们将深入探讨如何…

如何设置 GitLab 密码过期时间?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 60天专业…