DAO与Service层变量交互本质是职责分离:DAO专注数据操作(用DO),Service封装业务逻辑(用DTO/VO),传递需清晰、不越界、单向依赖。

DAO 和 Service 层之间的变量交互,本质是职责分离下的协作关系:DAO 只管“怎么拿数据”,Service 负责“拿回来后做什么”。关键不在于传什么变量,而在于传得清、接得住、不越界。
DAO 层只暴露数据操作接口,不暴露业务语义
DAO 接口方法签名应聚焦数据动作本身,比如 findById(Long id)、findByStatus(String status)、batchInsert(List<UserDO> users)。参数类型统一用 DO(Data Object),即与数据库表严格对应的实体类;返回值也以 DO 或 List<DO> 为主。不要在 DAO 方法里塞入布尔开关、分页标记、缓存策略等业务控制参数——这些属于 Service 层的决策范畴。
Service 层封装逻辑,用 DO 做输入输出,用 DTO/VO 做对外交付
Service 实现类通过构造注入或 Spring 自动装配获取 DAO 实例,调用其方法获取原始数据。之后进行校验、转换、组合、计算等操作:
- 接收 Controller 传来的 DTO,校验合法性后转为 DO 再交给 DAO
- 从 DAO 拿到 DO 列表,按业务规则过滤、排序、聚合,再组装成新的 DTO 或 VO 返回给 Controller
- 涉及多表关联或跨数据源时,Service 协调多个 DAO,自己做数据拼装,不把关联逻辑下沉到 DAO
变量传递要避免 null 泄漏和类型污染
DAO 方法内部不接受 null 参数,如 findById(null) 应抛 IllegalArgumentException;Service 层调用前自行判空或兜底处理。Controller 层传来的参数可以为 null(比如可选查询条件),但 Service 层需明确转化规则:
- 查询条件为 null → DAO 层对应字段忽略该条件(用动态 SQL 或 QueryWrapper 处理)
- 更新字段为 null → 不覆盖数据库原值(用 @JsonInclude(JsonInclude.Include.NON_NULL) 或字段级更新策略)
- 禁止将 VO、DTO 直接传进 DAO,也不允许 DAO 返回 VO 或 DTO
依赖方向必须单向:Service → DAO,不可反向
DAO 层不能持有 Service 引用,也不能回调 Service 方法。所有流程必须是 Controller → Service → DAO → DB 的直线调用。若出现“DAO 需要通知 Service 某个状态变化”,说明设计有误——应改为 Service 主动轮询、监听事件,或用领域事件机制解耦。Spring 中推荐用接口注入(如 UserServiceImpl 依赖 UserDAO 接口),而非具体实现类,便于单元测试时 Mock。

















