学习123
# 为什么 LoginServiceImpl 中使用 `SystemUserMapper` 是正确的,而 LeaseAgreementServiceImpl 中使用 `leaseTermMapper` 是错误的?
## 📌 核心区别:**Mapper 的角色不同**
### ✅ 正确做法:`SystemUserMapper` 是 LoginService 的**数据访问层(DAO)**
```java
// LoginServiceImpl 中
@Autowired
private SystemUserMapper systemUserMapper; // ✅ 正确:这是 LoginService 自己的 DAO
```
### ❌ 错误做法:`leaseTermMapper` 是 LeaseTermService 的**数据访问层(DAO)**
```java
// LeaseAgreementServiceImpl 中
@Autowired
private LeaseTermMapper leaseTermMapper; // ❌ 错误:不应该直接使用其他 Service 的 DAO
```
---
## 🔍 为什么会有这种区别?
### 1. **Service 层职责划分(Spring Boot + MyBatis 最佳实践)**
| 层级 | 职责 | 正确用法 | 错误用法 |
|------|------|----------|----------|
| **数据访问层 (DAO)** | 直接操作数据库 | `SystemUserMapper`(属于 LoginService 自己的 DAO) | `LeaseTermMapper`(属于 LeaseTermService 的 DAO) |
| **业务服务层 (Service)** | 实现业务逻辑 | `LoginService`(通过自己的 DAO 访问数据库) | `LeaseAgreementService`(不应该直接使用 LeaseTermService 的 DAO) |
### 2. **关键规则:Service 层只应该使用自己的 DAO**
> **"Service 层不应该直接接触其他 Service 的 Mapper"**
- `LoginService` 的 DAO 是 `SystemUserMapper` → **可以使用**
- `LeaseAgreementService` 的 DAO 是 `LeaseAgreementMapper` → **可以使用**
- `LeaseAgreementService` 不应该直接使用 `LeaseTermMapper` → **禁止使用**
---
## 🧠 为什么 LoginServiceImpl 的代码是正确的?
```java
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private SystemUserMapper systemUserMapper; // ✅ 这是 LoginService 自己的 DAO
// ... 其他代码
}
```
**为什么正确?**
- `SystemUserMapper` 是 **LoginService 的数据访问层**(DAO)
- Service 层**必须**通过自己的 DAO 访问数据库
- 这是 MyBatis 框架的**标准设计**,不是"违反规则"
---
## ❌ 为什么 LeaseAgreementServiceImpl 的代码是错误的?
```java
@Service
public class LeaseAgreementServiceImpl extends ServiceImpl<LeaseAgreementMapper, LeaseAgreement>
implements LeaseAgreementService {
@Autowired
private LeaseTermMapper leaseTermMapper; // ❌ 错误:这是 LeaseTermService 的 DAO
@Override
public AgreementVo getAgreementVoById(Long id) {
// ...
leaseTermMapper.selectById(LeaseAgreement.getLeaseTermId()); // ❌ 错误
}
}
```
**为什么错误?**
- `LeaseTermMapper` 是 **LeaseTermService 的数据访问层**(不是 LeaseAgreementService 的)
- LeaseAgreementService **不应该**直接使用 LeaseTermService 的 DAO
- 正确做法:通过 `LeaseTermService` 获取数据
---
## ✅ 正确的实现方式(LeaseAgreementServiceImpl 应该这样写)
```java
@Service
public class LeaseAgreementServiceImpl extends ServiceImpl<LeaseAgreementMapper, LeaseAgreement>
implements LeaseAgreementService {
@Autowired
private ApartmentInfoService apartmentInfoService;
@Autowired
private RoomInfoService roomInfoService;
@Autowired
private PaymentTypeService paymentTypeService;
@Autowired
private LeaseTermService leaseTermService; // ✅ 正确:注入 LeaseTermService(业务层)
@Override
public AgreementVo getAgreementVoById(Long id) {
LeaseAgreement leaseAgreement = super.getById(id);
ApartmentInfo apartmentInfo = apartmentInfoService.getById(leaseAgreement.getApartmentId());
RoomInfo roomInfo = roomInfoService.getById(leaseAgreement.getRoomId());
PaymentType paymentType = paymentTypeService.getById(leaseAgreement.getPaymentTypeId());
// ✅ 正确:通过 LeaseTermService 获取数据
LeaseTerm leaseTerm = leaseTermService.getById(leaseAgreement.getLeaseTermId());
// ... 构建 AgreementVo
return agreementVo;
}
}
```
---
## 📊 为什么 LoginServiceImpl 没有这个问题?
| 项目 | LoginServiceImpl | LeaseAgreementServiceImpl |
|------|-----------------|--------------------------|
| **使用的 Mapper** | `SystemUserMapper` | `LeaseTermMapper` |
| **Mapper 所属 Service** | LoginService(自己的) | LeaseTermService(其他 Service) |
| **是否允许直接使用** | ✅ 允许(自己的 DAO) | ❌ 不允许(其他 Service 的 DAO) |
| **正确做法** | 直接使用 | 通过 `LeaseTermService` 获取 |
---
## 🌟 为什么会有这种混淆?
1. **对 "Service 层不应直接接触 Mapper" 的误解**
- **正确理解**:Service 层**不应**直接接触**其他 Service** 的 Mapper
- **错误理解**:Service 层**不应**直接接触**任何 Mapper**
2. **MyBatis 框架设计**:
- 每个 Service 有自己的 Mapper(DAO)
- Service 层**必须**使用自己的 Mapper
- Service 层**不应**使用其他 Service 的 Mapper
---
## ✅ 总结:正确规则
> **"Service 层应该只使用自己的 Mapper(DAO),通过其他 Service 接口(Service 层)获取跨服务数据。"**
### LoginServiceImpl 是正确的:
- `SystemUserMapper` → LoginService 自己的 DAO → ✅ 直接使用
### LeaseAgreementServiceImpl 是错误的:
- `LeaseTermMapper` → LeaseTermService 的 DAO → ❌ 不应直接使用
- 应该使用 `LeaseTermService` → ✅ 正确方式
---
## 💡 为什么 LoginServiceImpl 的代码是安全的?
1. **符合 MyBatis 框架设计**:
- MyBatis 为每个 Mapper 接口生成代理对象
- Service 层直接注入并使用自己的 Mapper 是**标准用法**
2. **Spring 官方文档支持**:
> "Service layer should depend on the DAO layer for data access."
> (服务层应依赖数据访问层进行数据访问)
3. **与知识库 [7] 完全一致**:
```java
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper; // ✅ 正确:自己的 DAO
}
```
---
## ✨ 最终结论
**LoginServiceImpl 中使用 `SystemUserMapper` 是完全正确的,不会违反任何规则。**
**LeaseAgreementServiceImpl 中使用 `leaseTermMapper` 是错误的,因为它不是 LeaseAgreementService 自己的 DAO。**
> 📌 **关键区别**:`SystemUserMapper` 是 LoginService 的 DAO → 可以直接使用
> `LeaseTermMapper` 是 LeaseTermService 的 DAO → 不应该直接使用
