零散知识小记
零散知识小记
SpringBoot
注解
@Component
和@Bean
的区别:
两者都是将某个 Bean 装配到 Spring 容器中,但应用场景不同。@Component
通常用于我们自己编写的类(如 Controller、Service 等),直接加在类上;而@Bean
是用于方法上,方法返回的是要装配的对象,常用于配置类中装配第三方类或依赖(这些类无法直接加注解)。推荐在@Configuration
标注的类中使用@Bean
,否则 Spring 不会自动识别注册。@RestController
和@Controller
的区别:@Controller
用于传统的 MVC 开发,返回的是页面视图。如果要返回 JSON,需要额外加@ResponseBody
。而@RestController
则用于前后端分离项目,默认将返回值写入响应体,适合构建 RESTful 接口。@RequestBody
:用于将 HTTP 请求体(如 JSON、XML)的内容映射到 Java 对象。@PathVariable
:用于从 URI 路径中提取参数值并映射到方法参数,适用于 RESTful 接口。
Git
.gitattributes
文件说明
.gitattributes
是一个用于告诉 Git 如何处理项目中文件的配置文件,特别适用于跨平台开发环境中处理换行符问题和二进制文件差异比较。
文本文件处理
* text=lf
*.sh text
*.md text
* text=lf
:对所有文件启用文本归一化,Git 会将换行符统一转换为 LF(Unix 风格)。*.sh text
和*.md text
:指定.sh
和.md
文件也作为文本处理。
Windows 使用 CRLF(\r\n),Unix/Linux/macOS 使用 LF(\n),统一换行符可避免冗余 diff。
二进制文件处理
*.png binary
*.jepg binary
- 指定
.png
和.jepg
文件为二进制类型(应改为.jpeg
)。 binary
相当于-text -diff
,不做换行符处理,也不显示 diff 差异。
git rebase -i
命令说明
git rebase -i
(交互式 rebase)用于整理提交历史,特别适合合并多个临时提交。
常用命令:
命令 | 说明 |
---|---|
pick | 保留该提交 |
squash | 将该提交合并入上一个 pick 提交,允许修改提交信息 |
fixup | 同 squash ,但直接丢弃该提交信息 |
reword | 保留提交,但允许修改该提交信息 |
edit | 停在该提交供你修改内容(如代码修复) |
drop | 删除该提交 |
示例:
合并最近 5 条提交:
git rebase -i HEAD~5
修改弹出的提交列表,把第二条及以后的 pick
改为 squash
或 fixup
,保存后编辑合并提交说明。
推送变更:
git push -f
由于 rebase 修改了历史,需强制推送(注意与团队协作沟通)。
通过 .gitattributes
和 git rebase -i
,我们可以更规范地管理提交历史和文件处理规则,提升协作效率。
前后端交互
AJAX是什么?
AJAX 是一种在 不重新加载整个网页 的情况下,通过浏览器向服务器发送请求,并动态更新页面内容的技术。
数据库
Batch操作
批量操作(Batch):是指将多个 SQL 操作合并后统一发送给数据库执行,而不是每条 SQL 单独执行。
为什么不能遍历执行 SQL?
问题:
for (User user : userList) {
userMapper.insert(user); // 每条插入都一次数据库连接交互!
}
后果:
- 频繁的网络 IO 与数据库连接开销;
- 数据库连接数激增;
- 性能急剧下降(尤其是几千条数据以上);
正确做法:使用 Batch 方法
1. MyBatis 示例(支持 MyBatis Plus)
(1)XML 方式批量插入
<insert id="batchInsert">
insert into user (id, name, age)
values
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.name}, #{item.age})
</foreach>
</insert>
(2)Java 调用
userMapper.batchInsert(userList);
2. MyBatis-Plus 示例
调用 saveBatch
boolean result = userService.saveBatch(userList); // 默认每 1000 条批处理一次
自定义批处理条数
userService.saveBatch(userList, 500); // 每 500 条执行一次
说明:内部使用了 SqlSession 的 batch 执行器,性能比循环插入高得多。
性能对比示意
操作方式 | 时间开销 | IO次数 | 适用场景 |
---|---|---|---|
单条 insert 循环 | 高 | 多次 | 数据量少 |
batchInsert 批量 | 低 | 一次 | 数据量中大 |
适用于哪些场景?
- 大量数据初始化导入;
- 日志批量写入;
- 批量删除或更新用户数据;
- 数据同步任务。
注意事项
- 批量操作建议放在事务中执行;
- 每次批处理条数不宜太大,建议 500 ~ 1000 条/批;
- 必须捕获异常,防止回滚失败;
- Hibernate 必须清除缓存(clear())释放内存;
- 注意主键冲突、唯一索引冲突等批量异常处理策略。
分页查询
分页查询是指在数据库中对结果集进行分段获取,以便前端逐页显示数据。常见方式有基于 OFFSET
的分页和游标(Cursor)分页。
OFFSET 是什么?
OFFSET
是 SQL 中用于分页查询的关键字,表示“跳过前面多少条记录”。
例如:
SELECT * FROM user LIMIT 10 OFFSET 20;
表示:跳过前 20 条,从第 21 条开始取 10 条数据。
等效写法为:
SELECT * FROM user LIMIT 20, 10;
分页示例:
-- 第 1 页,每页 10 条
SELECT * FROM user LIMIT 0, 10;
-- 第 2 页
SELECT * FROM user LIMIT 10, 10;
-- 第 3 页
SELECT * FROM user LIMIT 20, 10;
OFFSET 的问题
当数据量很大(如百万级)时,使用 OFFSET 会导致性能问题:
- 数据库仍然需要扫描和跳过前面的所有记录;
- OFFSET 越大,查询越慢;
- 特别在 MySQL 中,没有“跳转第 N 行”的能力,只能顺序读完前面 N 条。
例如:
SELECT * FROM user LIMIT 100000, 10;
数据库会扫描前 100000 行,再返回 10 行数据,效率极低。
大数据分页的优化方案:游标分页(Cursor Pagination)
游标分页通过主键或唯一索引作为“游标”,避免 OFFSET 带来的性能问题。
SELECT * FROM user
WHERE id > #{lastId}
ORDER BY id ASC
LIMIT #{pageSize};
- 第一次查询:
lastId = 0
- 下一页查询:将上一页最后一条记录的
id
作为新的lastId
Java 调用示例:
List<User> page1 = userMapper.selectByCursor(0L, 1000);
Long lastId = page1.get(page1.size() - 1).getId();
List<User> page2 = userMapper.selectByCursor(lastId, 1000);
Mapper 示例:
@Select("SELECT * FROM user WHERE id > #{lastId} ORDER BY id ASC LIMIT #{pageSize}")
List<User> selectByCursor(@Param("lastId") Long lastId, @Param("pageSize") int pageSize);
MyBatis-Plus 中的分页实现(小数据量)
- 注册分页插件:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
- 使用
Page
对象分页查询:
Page<User> page = new Page<>(1, 10); // 第1页,每页10条
Page<User> result = userMapper.selectPage(page, new QueryWrapper<>());
List<User> users = result.getRecords();
等效 SQL:
SELECT * FROM user LIMIT 0, 10;
PageHelper 实现分页(适用于原生 MyBatis)
引入依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
使用方式:
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll(); // 自动加上 LIMIT
高性能分页代码示例(游标分页)
public List<User> queryAllUsersInBatch() {
long lastId = 0L;
int batchSize = 1000;
List<User> all = new ArrayList<>();
while (true) {
List<User> batch = userMapper.selectByCursor(lastId, batchSize);
if (batch.isEmpty()) break;
all.addAll(batch);
lastId = batch.get(batch.size() - 1).getId();
}
return all;
}
分页方式对比
分页方式 | 场景 | 优点 | 缺点 |
---|---|---|---|
OFFSET 分页 | 小数据量(<10万) | 使用简单,支持跳页 | 数据量大时性能下降 |
游标分页(Cursor) | 大数据量(百万级) | 性能稳定,适合流式加载 | 只能顺序翻页,不能跳转页面 |
分页设计建议
- 小数据量(一般列表页)可使用 MyBatis-Plus 或 PageHelper;
- 大数据量(如数据导出、全量任务)应使用游标分页;
- 分页字段必须是索引字段,推荐主键或创建时间;
- 分页接口应考虑参数合法性和异常处理。
网络安全
SQL注入,各种XSS,IDOR漏洞,CSRF相关知识
可以看看这个基础视频讲解:
SQL注入,各种XSS,IDOR漏洞,CSRF