Spring - 8 ( 10000 字 Spring 入门级教程 )
一: MyBatis
1.1 引入 MyBatis
我们学习 MySQL 数据库时,已经学习了 JDBC 来操作数据库, 但是 JDBC 操作太复杂了.
我们先来回顾⼀下 JDBC 的操作流程:
- 创建数据库连接池 DataSource
- 通过 DataSource 获取数据库连接 Connection
- 编写要执行带 ? 占位符的 SQL 语句
- 通过 Connection 及 SQL 创建操作命令对象 Statement
- 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
- 使用 Statement 执行 SQL 语句
- 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
- 处理结果集
- 释放资源
对于 JDBC 来说,整个操作非常的繁琐,我们不但要拼接每⼀个参数,而且还要按照模板代码方式,⼀步步的操作数据库,并且在每次操作完,还要手动关闭连接等,而所有的这些操作步骤都需要在每个方法中重复书写. 那有没有⼀种方法,可以更单、更方便的操作数据库呢?
这就是我们要学习 MyBatis 的真正原因,它可以帮助我们更方便、更快速的操作数据
库.
1.2 什么是 MyBatis
MyBatis 是⼀款优秀的持久层框架,⽤于简化 JDBC 的开发。
持久层:指的就是持久化操作的层, 通常指数据访问层(dao), 一般用来操作数据库.
二: MyBatis 入门
Mybatis操作数据库的步骤:
- 准备工作 (创建 springboot 工程、数据库表准备、实体类)
- 引入 Mybatis 的相关依赖,配置 Mybatis (数据库连接信息)
- 编写 SQL 语句 (注解 / XML)
- 测试
2.1 创建工程
- 创建 springboot 工程,并导入 mybatis 的起步依赖、mysql 的驱动包
Mybatis 是⼀个持久层框架, 具体的数据存储和数据操作还是在 MySQL 中操作的, 所以需要添加 MySQL 驱动
- 项目工程创建完成后自动在 pom.xml ⽂件中,导入 Mybatis 依赖和 MySQL 驱动依赖
<!--Mybatis 依赖包-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version>
</dependency><!--mysql驱动包-->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
2.2 数据准备
- 创建用户表, 并创建对应的实体类 User
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使用数据库
USE mybatis_test;
-- 创建表[用户表]
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (`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(),PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
-- 添加用户信息
INSERT INTO mybatis_test.userinfo (username, `password`, age, gender, phone)
VALUES ('admin', 'admin', 18, 1, '18612340001');
INSERT INTO mybatis_test.userinfo (username, `password`, age, gender, phone)
VALUES ('zhangsan', 'zhangsan', 18, 1, '18612340002');
INSERT INTO mybatis_test.userinfo (username, `password`, age, gender, phone)
VALUES ('lisi', 'lisi', 18, 1, '18612340003');
INSERT INTO mybatis_test.userinfo (username, `password`, age, gender, phone)
VALUES ('wangwu', 'wangwu', 18, 1, '18612340004');
- 创建对应的实体类 UserInfo
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {private Integer 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.3 配置数据库连接字符串
Mybatis 中要连接数据库,需要数据库相关参数配置:
- MySQL驱动类
- 登录名
- 密码
- 数据库连接字符串
- application.yml 文件
# 数据库连接配置
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
- application.properties 文件
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false#连接数据库的⽤⼾名
spring.datasource.username=root#连接数据库的密码
spring.datasource.password=root
2.4 写持久层代码
在项目中, 创建持久层接口 UserInfoMapper
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface UserInfoMapper {//查询所有⽤⼾@Select("select username, `password`, age, gender, phone from userinfo")public List<UserInfo> queryAllUser();
}
@Mapper
注解:这个注解标识了这个接口是一个 MyBatis 的 Mapper 接口,它告诉MyBatis框架要为这个接口创建一个实现类,并且这个实现类会自映射SQL语句的方法。@Select
注解:这个注解标识了 queryAllUser() 方法使用的查询语句,当需要执行数据库操作时,应用程序会调用 UserInfoMapper 接口中的 queryAllUser() 方法。
注意:Mybatis 的持久层接口规范⼀般都叫 XxxMapper
2.5 单元测试
2.5.1 使用 test 测试类
在创建出来的 SpringBoot 工程中,在 src 下的 test 目录下,已经自动帮我们创建好了测试类 ,我们可以直接使用这个测试类来进行测试.
import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.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 DemoApplicationTests {@Autowiredprivate UserInfoMapper userInfoMapper;@Test void contextLoads() {List<UserInfo> userInfoList = userInfoMapper.queryAllUser();System.out.println(userInfoList);}
}
- @SpringBootTest 用于指示该类是一个 Spring Boot 应用程序的集成测试类。这个注解告诉测试运行器去加载整个 Spring 应用程序上下文,包括所有的 bean,并为测试提供一个实例化的应程序环境。
- @Test 注解用于标识一个测试方法。当运行测试类时,它会搜索类中带有 @Test 注解的方法,并执行这些方法。
运行结果如下:
返回结果中, 可以看到, 只有 SQL 语句中查询的列对应的属性才有赋值
2.5.2 使用 Idea 自动生成测试类
除此之外, 也可以使用 Idea 自动生成测试类
- 在需要测试的Mapper接口中, 右键 -> Generate -> Test
2. 选择要测试的方法, 点击 OK
- 书写测试代码
import com.example.demo.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 UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid queryAllUser() {List<UserInfo> userInfoList = userInfoMapper.queryAllUser();System.out.println(userInfoList);}
}
记得加 @SpringBootTest 注解, 加载 Spring 行环境
运行结果:
三: MyBatis 的基础操作
上面我们学习了 Mybatis 的查询操作, 接下来我们学习 MyBatis 的增, 删, 改操作,在学习这些操作之前, 我们先来学习 MyBatis 日志打印
3.1 打印日志
在 Mybatis 当中我们可以借助日志, 查看到 sql 语句的执行语句、执行传递参数以及执行结果
- yml 配置
mybatis:configuration: # 配置打印 MyBatis⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- properties 配置
#指定mybatis输出⽇志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
重新运行程序, 可以看到 SQL 执行内容, 以及传递参数和执行结果
- ①: 查询语句
- ②: 传递参数及类型
- ③: SQL执行结果
3.2 参数传递
@Select("select username, `password`, age, gender, phone from userinfo where id= 4 ")
UserInfo queryById();
这条语句只能查找 id=4 的数据,有时候我们需要 id 的数值是动态的,我们该怎么做呢
解决方案:
- 在 queryById 方法中添加⼀个参数 (id),将方法中的参数,传给 SQL 语句
- 然后使用 #{} 的方式获取方法中的参数
@Select("select username, `password`, age, gender, phone from userinfo where id= #{id} ")
UserInfo queryById(Integer id);
如果 mapper 接口方法形参只有⼀个普通类型的参数,#{…} 里面属性名可以随便写,但如果有多个参数的话,建议和参数名保持⼀致
- 测试用例
@Test
void queryById() {UserInfo userInfo = userInfoMapper.queryById(4);System.out.println(userInfo);
}
- 运行结果
我们也可以通过 @Param , 设置参数的别名, 如果使用 @Param 设置别名, #{…} 里面的属性名必须和 @Param 设置的⼀样
@Select("select username, `password`, age, gender, phone from userinfo where id= #{userid} ")
UserInfo queryById(@Param("userid") Integer id);
3.3 增 (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);
- 测试代码:
@Test
void insert() {UserInfo userInfo = new UserInfo();userInfo.setUsername("zhaoliu");userInfo.setPassword("zhaoliu");userInfo.setGender(2);userInfo.setAge(21);userInfo.setPhone("18612340005");userInfoMapper.insert(userInfo);
}
- 如果设置了 @Param 属性, #{…} 需要使用参数.属性来获取
@Insert("insert into userinfo (username, `password`, age, gender, phone) values (#{userinfo.username},#{userinfo.password},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})")
Integer insert(@Param("userinfo") UserInfo userInfo);
3.3.1 返回主键
Insert 语句默认返回的是受影响的行数,但有些情况下我们需要获取到新插入数据的 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);
- @Options: 这是 Myatis 中的一个注解,用于配置一些选项以影响 SQL 语句的执行方式。
- useGeneratedKeys = true 这个选项告诉 MyBatis 使用数据库自动生成的主键。
- Property = “id”: 这个选项指定了用来接收生成的主键值的属性名。这个例子中,自动生成的主键值将会被赋给 UserInfo 对象中名为 id 的属性。
测试数据:
@Test
void insert() {UserInfo userInfo = new UserInfo();userInfo.setUsername("zhaoliu");userInfo.setPassword("zhaoliu");userInfo.setGender(2);userInfo.setAge(21);userInfo.setPhone("18612340005");Integer count = userInfoMapper.insert(userInfo);System.out.println("添加数据条数:" +count +", 数据ID:" + userInfo.getId());
}
运行结果:
注意: 设置 useGeneratedKeys=true 之后, 方法返回值依然是受影响的行数, 自增 id 会设置在上述 keyProperty 指定的属性中.
3.4 删(Delete)
SQL 语句:
delete from userinfo where id=6
把 SQL 中的常量替换为动态的参数:
@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);
3.5 改(Update)
SQL 语句:
update userinfo set username="zhaoliu" where id=5
把 SQL 中的常量替换为动态的参数:
@Update("update userinfo set username=#{username} where id=#{id}")
void update(UserInfo userInfo);
3.6 查(Select)
我们在上面查询时发现, 有几个字段是没有赋值的, 只有Java对象属性和数据库字段⼀模⼀样时, 才会进行赋值,接下来我们多查询⼀些数据
@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
List<UserInfo> queryAllUser();
- 查询结果:
从运行结果上可以看到, 我们 SQL 语句中, 查询了 delete_flag, create_time, update_time, 但是这几个属性却没有赋值.
- 原因分析:
当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性,但如果名字不一样就无法进行赋值了。
解决办法:
- 起别名
- 结果映射
- 开启驼峰命名
注意:MyBatis 会根据方法的返回结果进行赋值.
- 方法用对象 UserInfo 接收返回结果, MySQL 查询出来数据为⼀条, 就会自动赋值给对象.
- 方法用 List< UserInfo > 接收返回结果, MySQL 查询出来数据为⼀条或多条时, 也会自动赋值给 List.
- 但如果 MySQL 查询返回多条, 但是方法使用 UserInfo 接收, MyBatis 执行就会报错.
3.6.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();
SQL 语句太长时, 使用加号 + 进行字符串拼接
3.6.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> queryAllUser();
如果其他 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> queryAllUser();@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 注解来复用其他定义的 ResultMap
3.6.3 开启驼峰命名 (推荐)
通常数据库列使用蛇形命名法进行命名,而 Java 属性⼀般遵循驼峰命名法约定,为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。
mybatis:configuration:map-underscore-to-camel-case: true #配置驼峰⾃动转换
Java 代码不做任何处理
@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();
添加上述配置, 运行代码:
字段全部进行正确赋值.
相关文章:

Spring - 8 ( 10000 字 Spring 入门级教程 )
一: MyBatis 1.1 引入 MyBatis 我们学习 MySQL 数据库时,已经学习了 JDBC 来操作数据库, 但是 JDBC 操作太复杂了. 我们先来回顾⼀下 JDBC 的操作流程: 创建数据库连接池 DataSource通过 DataSource 获取数据库连接 Connection编写要执行带 ? 占位符…...

鸿蒙内核源码分析(忍者ninja篇) | 都忍者了能不快吗
ninja | 忍者 ninja是一个叫 Evan Martin的谷歌工程师开源的一个自定义的构建系统,最早是用于 chrome的构建,Martin给它取名 ninja(忍者)的原因是因为它strikes quickly(快速出击).这是忍者的特点,可惜Martin不了解中国文化,不然叫小李飞刀更合适些.究竟有多块呢? 用Martin自…...

Linux——守护进程化(独立于用户会话的进程)
目录 前言 一、进程组ID与会话ID 二、setsid() 创建新会话 三、daemon 守护进程 前言 在之前,我们学习过socket编程中的udp通信与tcp通信,但是当时我们服务器启动的时候,都是以前台进程的方式启动的,这样很不优雅,…...

安卓开发--按键跳转页面,按键按下变色
前面已经介绍了一个空白按键工程的建立以及响应方式,可以参考这里:安卓开发–新建工程,新建虚拟手机,按键事件响应。 安卓开发是页面跳转是基础!!!所以本篇博客介绍利用按键实现页面跳转&#…...
Ps基础学习笔记
Ps基础学习笔记 Adobe Photoshop(简称Ps)是一款非常流行的图像处理软件,被广泛应用于图像编辑、修饰和设计等领域。作为一名初学者,了解Ps的基础知识是非常重要的,本文将介绍Ps的基本操作和常用工具,帮助你…...
spring开发问题总结(持续更新)
开始 最近在做项目的时候,总遇到一些大小不一,奇形怪状的问题。 现在终于有时间来总结一下遇到的问题,以备复习之用。 以下提到的问题经过简化,不代表任何项目代码或问题。 问题1:未完成任务状态搜索结果有误&#x…...

Android 状态栏WiFi图标的显示逻辑
1. 状态栏信号图标 1.1 WIFI信号显示 WIFI信号在状态栏的显示如下图所示 当WiFi状态为关闭时,状态栏不会有任何显示。当WiFi状态打开时,会如上图所示,左侧表示有可用WiFi,右侧表示当前WiFi打开但未连接。 当WiFi状态连接时&#x…...
更改 DeepXDE 的后端
DeepXDE 库为科学计算和工程优化等领域提供了深度学习方法,是一个非常有用的工具。其中一个重要的功能是它允许用户自定义后端。在本文中,我们将指导如何更改 DeepXDE 的后端,并且验证更改是否成功。 更改 DeepXDE 的后端 DeepXDE 支持多种…...

SpringBoot之Zuul服务
概述 Spring Cloud Netflix zuul组件是微服务架构中的网关组件,Zuul作为统一网关,是所有访问该平台的请求入口,核心功能是路由和过滤。 目前公司业务就是基于Zuul搭建的网关服务,且提供的服务包括转发请求(路由)、黑名单IP访问拦截、URL资源访问时的权限拦截、统一访问日志记…...

Go-变量
可以理解为一个昵称 以后这个昵称就代指这些信息 var sg string "czy" 声明赋值 package mainimport "fmt"func main() {var sg string "陈政洋"fmt.Println(sg)var age int 73fmt.Println(age)var flag bool truefmt.Println(flag) } …...

【CTF-Crypto】RSA-选择明密文攻击 一文通
RSA:选择明密文攻击 关于选择明/密文攻击,其实这一般是打一套组合拳的,在网上找到了利用的思路,感觉下面这个题目是真正将这个问题实现了,所以还是非常棒的一道题,下面先了解一下该知识点:(来自…...
Pytorch基础:torch.expand() 和 torch.repeat()
在torch中,如果要改变某一个tensor的维度,可以利用view、expand、repeat、transpose和permute等方法,这里对这些方法的一些容易混淆的地方做个总结。 expand和repeat函数是pytorch中常用于进行张量数据复制和维度扩展的函数,但其…...
如何正确安装Scrapy 2.6.1并解决常见的Python环境问题
在配置Python环境和安装包时,常常会遇到版本冲突和路径问题,特别是当系统中存在多个Python版本时。本文将指导你如何在CentOS系统中正确使用pip3安装Scrapy 2.6.1,并解决一些常见的环境问题。 步骤1: 确认和升级 pip3 确认 pip3 的版本&…...

阵痛中的乳业产业,何时才能成为下一个啤酒产业?
说起饮品,近年来中国啤酒业中各大品牌齐齐聚焦高端化的趋势绝对值得一提。然而,与之相反,国内乳业却是仍未进入高端化阶段,甚至陷入了周期底部中。 图源:中国圣牧财报 增收降利 牧企承受巨大的供需缺口压力 从产业链…...

关于模型参数融合的思考
模型参数融合通常指的是在训练过程中或训练完成后将不同模型的参数以某种方式结合起来,以期望得到更好的性能。这种融合可以在不同的层面上进行,例如在神经网络的不同层之间,或者是在完全不同的模型之间。模型参数融合的目的是结合不同模型的…...
Windows MySQL本地服务器设置并导入数据库和数据
文章目录 小结问题及解决导出数据库Windows MySQL本地服务器设置导入数据库和数据 参考 小结 最近需要在本地Windows环境中设置MySQL服务器,并导入数据库和数据,记录过程。 问题及解决 导出数据库 首先需要导出数据库: C:\mysql-8.0.37-…...

豪投巨资,澳大利亚在追逐海市蜃楼吗?
澳大利亚政府正在积极投资于量子计算领域。继2021年向量子技术投资逾1亿澳元后,2023年5月,该国发布了首个国家量子战略,详细阐述了如何把握量子技术的未来及保持全球领先地位。 澳大利亚的国家量子战略概述 原文链接: https://ww…...

面试集中营—Redis架构篇
一、Redis到底是多线程还是单线程 1、redis6.0版本之前的单线程,是指网络请求I/O与数据的读写是由一个线程完成的; 2、redis6.0版本升级成了多线程,指的是在网络请求I/O阶段应用的多线程技术;而键值对的读写还是由单线程完成的。所…...
05_kafka-整合springboot
文章目录 kafka 整合 springboot pom.xml <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.5.RELEASE</version> </parent> <dependencies>&…...
论UML在学情精准测评系统中的应用
摘要简介 项目背景: 随着教育改革的不断深入,对学生学情的精准测评成为教育教学工作中的重要环节。为了解决传统学情测评方式主观性强、效率低、反馈不及时等问题,我们团队受教育主管部门委托,承担了中小学学情精准测评系统&…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...

Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
【题解-洛谷】P10480 可达性统计
题目:P10480 可达性统计 题目描述 给定一张 N N N 个点 M M M 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。 输入格式 第一行两个整数 N , M N,M N,M,接下来 M M M 行每行两个整数 x , y x,y x,y,表示从 …...
linux设备重启后时间与网络时间不同步怎么解决?
linux设备重启后时间与网络时间不同步怎么解决? 设备只要一重启,时间又错了/偏了,明明刚刚对时还是对的! 这在物联网、嵌入式开发环境特别常见,尤其是开发板、树莓派、rk3588 这类设备。 解决方法: 加硬件…...

Spring是如何实现无代理对象的循环依赖
无代理对象的循环依赖 什么是循环依赖解决方案实现方式测试验证 引入代理对象的影响创建代理对象问题分析 源码见:mini-spring 什么是循环依赖 循环依赖是指在对象创建过程中,两个或多个对象相互依赖,导致创建过程陷入死循环。以下通过一个简…...