Commit on 2025/07/05 周六 18:51:00.07

This commit is contained in:
zhangsan 2025-07-05 18:51:00 +08:00
parent 07ff849798
commit 109b75d54b
5 changed files with 89 additions and 70 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
output/
自学/本地记录.md
后端学习/本地记录.md

View File

@ -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);
@Transactional(rollbackFor = Exception.class) // 捕获所有异常并回滚
public void delete(Integer id) { ... }
```
//模拟:异常发生
int num = id/0;
- **如果**只写 `@Transactional`,则 *仅* 运行时异常(`RuntimeException`)会触发回滚。
- **如要**让 *检查时异常*`Exception`)也能回滚,就需显式指定 `rollbackFor`
//删除部门下的所有员工信息
empMapper.deleteByDeptId(id);
##### ② 事务传播行为(`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)

View File

@ -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`** | “意图操作” “快捷修复” 可以1service接口类跳转到实现 2补全函数的返回值 |

View File

@ -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`
这样三者的定位就清晰了:
这样,你只需关心
> “**聚合**管状态,**领域服务**管跨聚合业务,**仓储**管存取。”
> *「现在有没有父事务?✚ → 该传播行为会怎么做?」*
就能快速判断是否满足你的业务需求。

5
杂项/草稿.md Normal file
View File

@ -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)