Commit on 2025/07/05 周六 18:51:00.07
This commit is contained in:
parent
07ff849798
commit
109b75d54b
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
output/
|
||||
自学/本地记录.md
|
||||
后端学习/本地记录.md
|
||||
|
@ -1852,7 +1852,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
|
||||
|
||||
## 全局异常处理
|
||||
|
||||
**当前问题:**如果程序因不知名原因报错,响应回来的数据是一个JSON格式的数据,但这种JSON格式的数据不符合开发规范当中所提到的统一响应结果Result吗,导致前端不能解析出响应的JSON数据。
|
||||
**当前问题:**如果程序因不知名原因报错,响应回来的数据是一个JSON格式的数据,但这种JSON格式的数据不符合开发规范当中所提到的统一响应结果Result,导致前端不能解析出响应的JSON数据。
|
||||
|
||||

|
||||
|
||||
@ -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 会打印出关于事务的详细信息,例如事务的开启、提交、回滚以及数据库操作。
|
||||
**效果**:控制台会打印事务生命周期日志(开启、提交、回滚等),方便排查。
|
||||
|
||||

|
||||
|
||||
|
@ -12,13 +12,12 @@ Intellij Ideav创建Java项目:
|
||||
IDEA快捷键:
|
||||
|
||||
| `Ctrl + L` | 格式化代码 |
|
||||
| ------------- | ------------------------- |
|
||||
| ---------------- | ------------------------------------------------------------ |
|
||||
| `Ctrl + /` | 注释/取消注释当前行 |
|
||||
| `Ctrl + D` | 复制当前行或选中的代码块 |
|
||||
| `Ctrl + Y` | 删除当前行 |
|
||||
| `Ctrl + N` | 查找类 |
|
||||
| `shift+shift` | 在文件中查找代码 |
|
||||
| `alt+回车` | service接口类跳转到实现类 |
|
||||
| **`alt+ enter`** | “意图操作” “快捷修复” 可以1:service接口类跳转到实现 2:补全函数的返回值 |
|
||||
|
||||
|
||||
|
||||
|
35
后端学习/草稿.md
35
后端学习/草稿.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`
|
||||
|
||||
这样三者的定位就清晰了:
|
||||
这样,你只需关心:
|
||||
|
||||
> “**聚合**管状态,**领域服务**管跨聚合业务,**仓储**管存取。”
|
||||
> *「现在有没有父事务?✚ → 该传播行为会怎么做?」*
|
||||
|
||||
就能快速判断是否满足你的业务需求。
|
||||
|
Loading…
x
Reference in New Issue
Block a user