From 109b75d54bb4985b15a651506346f1c8c0863e0e Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Sat, 5 Jul 2025 18:51:00 +0800 Subject: [PATCH] =?UTF-8?q?Commit=20on=202025/07/05=20=E5=91=A8=E5=85=AD?= =?UTF-8?q?=2018:51:00.07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 后端学习/JavaWeb——后端.md | 102 ++++++++++++++++++++------------------ 后端学习/Java笔记本.md | 15 +++--- 后端学习/草稿.md | 35 +++++++------ 杂项/草稿.md | 5 ++ 5 files changed, 89 insertions(+), 70 deletions(-) create mode 100644 杂项/草稿.md diff --git a/.gitignore b/.gitignore index 41a26d7..4ca0a30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ output/ -自学/本地记录.md +后端学习/本地记录.md diff --git a/后端学习/JavaWeb——后端.md b/后端学习/JavaWeb——后端.md index 1bdae7f..b47fd50 100644 --- a/后端学习/JavaWeb——后端.md +++ b/后端学习/JavaWeb——后端.md @@ -1852,7 +1852,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons ## 全局异常处理 -**当前问题:**如果程序因不知名原因报错,响应回来的数据是一个JSON格式的数据,但这种JSON格式的数据不符合开发规范当中所提到的统一响应结果Result吗,导致前端不能解析出响应的JSON数据。 +**当前问题:**如果程序因不知名原因报错,响应回来的数据是一个JSON格式的数据,但这种JSON格式的数据不符合开发规范当中所提到的统一响应结果Result,导致前端不能解析出响应的JSON数据。 ![image-20230112130253486](https://pic.bitday.top/i/2025/04/08/soju68-0.png) @@ -1924,7 +1924,7 @@ int res=10/0; ## 事务 -### 问题分析: +### 场景与问题: ```java @Slf4j @@ -1951,69 +1951,77 @@ public class DeptServiceImpl implements DeptService { } ``` -即使程序运行抛出了异常,部门依然删除了,但是部门下的员工却没有删除,造成了数据的不一致。 +**问题**:出现异常后,部门已被删除,但员工记录仍然存在,造成数据不一致。 -因此,需要事务来控制这组操作,让这组操作同时成功或同时失败。 +**原因**:整组操作没有被事务包裹,无法做到“要么全部成功,要么全部失败”。 -### Transactional注解 +### `@Transactional` 注解 -`@Transactional`作用:就是在当前这个方法执行开始之前来开启事务,**方法执行完毕之后提交事务**。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作。一般会在**业务层(Service)**当中来控制事务。 +| 位置 | 作用 | +| ---------- | ---------------------------------------- | +| **方法级** | 仅当前方法受事务管理 | +| **类级** | 类中所有方法受事务管理 | +| **接口级** | 接口下 *所有实现类* 的全部方法受事务管理 | -`@Transactional`注解书写位置: +> 在实际开发中,**推荐只在 Service 层的方法或类上标注**,保持粒度清晰。 -- 方法:当前方法交给spring进行事务管理 -- 类:当前类中所有的方法都交由spring进行事务管理 -- 接口:接口下所有的实现类当中所有的方法都交给spring 进行事务管理 +#### 常用属性 +| 属性 | 说明 | 默认值 | +| ------------- | -------------------- | ---------------------- | +| `rollbackFor` | 指定哪些异常触发回滚 | 仅 `RuntimeException` | +| `propagation` | 指定事务传播行为 | `Propagation.REQUIRED` | - -`@Transactional`注解当中的两个常见的属性: - -1. 异常回滚的属性:rollbackFor -2. 事务传播行为:propagation - -默认情况下,只有出现**RuntimeException(运行时异常)**才会回滚事务。假如我们想让所有的异常都回滚,需要来配置@Transactional注解当中的**rollbackFor**属性,通过rollbackFor这个属性可以指定出现何种异常类型回滚事务。 +##### ① 回滚规则(`rollbackFor`) ```java -@Transactional(rollbackFor=Exception.class) - public void delete(Integer id){ - //根据部门id删除部门信息 - deptMapper.deleteById(id); - - //模拟:异常发生 - int num = id/0; +@Transactional(rollbackFor = Exception.class) // 捕获所有异常并回滚 +public void delete(Integer id) { ... } +``` - //删除部门下的所有员工信息 - empMapper.deleteByDeptId(id); +- **如果**只写 `@Transactional`,则 *仅* 运行时异常(`RuntimeException`)会触发回滚。 +- **如要**让 *检查时异常*(`Exception`)也能回滚,就需显式指定 `rollbackFor`。 + +##### ② 事务传播行为(`propagation`) + +| 传播行为 | **父事务已存在时** | **父事务不存在时** | 典型用途 / 说明 | +| ----------------------- | -------------------------------------------------- | ------------------------------------ | ------------------------------------------ | +| **`REQUIRED`** *(默认)* | **加入父事务**→ 共提交 / 回滚 | **创建新事务** | 日常业务写操作,保持一致性 | +| **`REQUIRES_NEW`** | **挂起父事务**→ **自己新建事务** | 自己新建事务 | 写日志、发送 MQ 等:外层失败也要单独成功 | +| **`SUPPORTS`** | 加入父事务 | **非事务方式执行** | 只读查询:有事务跟随一致性,没有就轻量查询 | +| **`NOT_SUPPORTED`** | **挂起父事务**→ 非事务方式执行 | 非事务方式执行 | 大批量/耗时操作,避免长事务锁表 | +| **`MANDATORY`** | 加入父事务 | **立即抛异常** | 防御性编程:强制要求调用方已开启事务 | +| **`NEVER`** | **立即抛异常** | 非事务方式执行 | 禁止在事务里跑的代码(如特殊 DDL) | +| **`NESTED`** | 同一物理事务,打 **SAVEPOINT**→ 子回滚只回到保存点 | 创建新事务(与 `REQUIRED` 效果相同) | 分段回滚;需 DB / JDBC 支持保存点 | + +需要“互不影响”时用 `REQUIRES_NEW`——强制新建事务: + +```java +@Transactional // 外层保存订单 +public void saveOrder(Order order){ + orderMapper.insert(order); + + // 总是单独提交日志 + logService.saveLog(...); + + // 后面出现异常 + if(order.getAmount() < 0){ + throw new IllegalArgumentException("非法金额"); } -``` +} - - -在`@Transactional`注解的后面指定一个属性**propagation**,通过 propagation 属性来指定事务的传播行为。 - -什么是事务的传播行为呢? - -- 就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。A方法运行的时候,首先会开启一个事务,在A方法当中又调用了B方法, B方法自身也具有事务,那么B方法在运行的时候,到底是加入到A方法的事务当中来,还是B方法在运行的时候新建一个事务? - -| **属性值** | **含义** | -| ------------ | ------------------------------------------------------------ | -| REQUIRED | 【默认值】有父事务则加入,**父子有异常则一起回滚**;无父事务则创建新事务 | -| REQUIRES_NEW | 需要新事务,无论有无,**总是创建新事务** | - -- REQUIRED :大部分情况下都是用该传播行为即可。 - -- REQUIRES_NEW :当我们**不希望事务之间相互影响**时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。 - -```java +//操作日志、审计表、MQ 消息等,不能因为业务失败而丢记录。 @Transactional(propagation = Propagation.REQUIRES_NEW) +public void saveLog(Log log) { ... } ``` -### Spring事务日志开关 +### 调试:Spring事务日志开关 + +在 `application.yml` 中添加: ```yml logging: @@ -2021,7 +2029,7 @@ logging: org.springframework.jdbc.support.JdbcTransactionManager: debug ``` -当你设置 `debug` 级别日志时,Spring 会打印出关于事务的详细信息,例如事务的开启、提交、回滚以及数据库操作。 +**效果**:控制台会打印事务生命周期日志(开启、提交、回滚等),方便排查。 ![image-20230107144312892](https://pic.bitday.top/i/2025/04/08/t15yw1-0.png) diff --git a/后端学习/Java笔记本.md b/后端学习/Java笔记本.md index 067e588..fc6ab9b 100644 --- a/后端学习/Java笔记本.md +++ b/后端学习/Java笔记本.md @@ -11,14 +11,13 @@ Intellij Ideav创建Java项目: IDEA快捷键: -| `Ctrl + L` | 格式化代码 | -| ------------- | ------------------------- | -| `Ctrl + /` | 注释/取消注释当前行 | -| `Ctrl + D` | 复制当前行或选中的代码块 | -| `Ctrl + Y` | 删除当前行 | -| `Ctrl + N` | 查找类 | -| `shift+shift` | 在文件中查找代码 | -| `alt+回车` | service接口类跳转到实现类 | +| `Ctrl + L` | 格式化代码 | +| ---------------- | ------------------------------------------------------------ | +| `Ctrl + /` | 注释/取消注释当前行 | +| `Ctrl + D` | 复制当前行或选中的代码块 | +| `Ctrl + N` | 查找类 | +| `shift+shift` | 在文件中查找代码 | +| **`alt+ enter`** | “意图操作” “快捷修复” 可以1:service接口类跳转到实现 2:补全函数的返回值 | diff --git a/后端学习/草稿.md b/后端学习/草稿.md index 7fec74f..fa3b712 100644 --- a/后端学习/草稿.md +++ b/后端学习/草稿.md @@ -1,19 +1,26 @@ -### 聚合 vs 领域服务 vs 仓储 —— 对比一览 +下面把 **传播行为** 拆成“**有父事务时怎么做**”和“**无父事务时怎么做**”两个维度,比原来的「是否必须有父事务 / 是否会新建事务」更直观——一眼就能看出它在两种场景下的动作差异。 -| 特性 | **聚合(Aggregate)** | **领域服务(Domain Service)** | **仓储(Repository)** | -| ------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -| **本质** | 相关实体和值对象的**组合**,以“聚合根”为唯一访问入口 | 无状态的业务逻辑单元,封装**跨实体 / 跨聚合**规则 | 抽象的数据访问接口,隐藏底层存储细节,为聚合**提供持久化能力** | -| **状态** | **有状态**——内部维护数据与不变式 | **无状态**——仅暴露行为 | **无业务状态**;实现层可能有缓存,但对外看作无状态 | -| **职责** | 1. 内部一致性2. 定义事务边界3. 提供领域行为(`order.pay()` 等) | 1. 承载跨实体规则2. 协调多个聚合完成业务动作 | 1. 加载 / 保存聚合根2. 把 PO ↔️ Entity 映射3. 屏蔽 SQL/ORM/缓存等技术细节 | -| **边界** | *聚合边界*:内部操作要么全部成功要么全部失败 | 无一致性边界,仅调用聚合或仓储 | *持久化边界*:一次操作针对一个聚合;不负责业务事务(由应用层控制) | -| **典型用法** | `Order.addItem()`,`Order.cancel()` | `PricingService.calculate(...)`,`InventoryService.reserveStock(...)` | `orderRepository.findById(id)`,`orderRepository.save(order)` | +| 传播行为 | **父事务已存在时** | **父事务不存在时** | 典型用途 / 说明 | +| ----------------------- | -------------------------------------------------- | ------------------------------------ | ------------------------------------------ | +| **`REQUIRED`** *(默认)* | **加入父事务**→ 共提交 / 回滚 | **创建新事务** | 日常业务写操作,保持一致性 | +| **`REQUIRES_NEW`** | **挂起父事务**→ **自己新建事务** | 自己新建事务 | 写日志、发送 MQ 等:外层失败也要单独成功 | +| **`SUPPORTS`** | 加入父事务 | **非事务方式执行** | 只读查询:有事务跟随一致性,没有就轻量查询 | +| **`NOT_SUPPORTED`** | **挂起父事务**→ 非事务方式执行 | 非事务方式执行 | 大批量/耗时操作,避免长事务锁表 | +| **`MANDATORY`** | 加入父事务 | **立即抛异常** | 防御性编程:强制要求调用方已开启事务 | +| **`NEVER`** | **立即抛异常** | 非事务方式执行 | 禁止在事务里跑的代码(如特殊 DDL) | +| **`NESTED`** | 同一物理事务,打 **SAVEPOINT**→ 子回滚只回到保存点 | 创建新事务(与 `REQUIRED` 效果相同) | 分段回滚;需 DB / JDBC 支持保存点 | -#### 快速记忆 +### 使用 Tips -- **聚合**:有数据 + 行为,是“业务发动机” -- **领域服务**:无数据,专管“发动机之间的协作” -- **仓储**:无业务规则,只负责把“发动机”**存取**到持久化介质 +- **不想被外层事务拖垮** ➜ `REQUIRES_NEW` +- **可有可无事务的读操作** ➜ `SUPPORTS` +- **耗时任务要彻底裸跑** ➜ `NOT_SUPPORTED` +- **局部失败但整体继续** ➜ `NESTED`(保存点) +- **强约束外层必须有事务** ➜ `MANDATORY` +- **坚决拒绝在事务里执行** ➜ `NEVER` -这样三者的定位就清晰了: +这样,你只需关心: -> “**聚合**管状态,**领域服务**管跨聚合业务,**仓储**管存取。” +> *「现在有没有父事务?✚ → 该传播行为会怎么做?」* + +就能快速判断是否满足你的业务需求。 diff --git a/杂项/草稿.md b/杂项/草稿.md new file mode 100644 index 0000000..8fb29aa --- /dev/null +++ b/杂项/草稿.md @@ -0,0 +1,5 @@ + + +![image-20250705151418820](https://pic.bitday.top/i/2025/07/05/p1fk0d-0.png) + +![image-20250705151426441](https://pic.bitday.top/i/2025/07/05/p1h7ev-0.png) \ No newline at end of file