DDD4J MyBatis-Plus 持久层约定
编码 DDD4J 项目中 MyBatis-Plus 的使用规则。LLM 会用标准 MyBatis-Plus 写法,但不符项目约定。
为什么需要这个技能
LLM 会用 implements Serializable 定义实体——DDD4J 要求 extends BaseEntity<T extends Model<?>>(ActiveRecord模式)。LLM 会在 XML 中写 #{entity.beginTime}——DDD4J 要求 #{model.beginTime}(@Param("model"))。这些约定 LLM 不知道。
Capability Boundaries
✅ Strong Suits
- 实体继承链 — BaseEntity→Model(ActiveRecord),PaginationEntity→BaseEntity
- 审计字段 — createBy/createTime(INSERT填充)、updateBy/updateTime(INSERT_UPDATE填充)
- 逻辑删除 — @TableLogic 标记 isDeleted 字段
- @Param("model") 约定 — getPagedList 的参数名是 model,不是 entity
- 分页封装 — PaginationEntity(默认15条/页,Oracle风格offset=(pageNo-1)*limit+1)
- 类型处理器 — BooleanEnum(0/1)、JSON(双套)、List/Set/Array
- BaseMapper 内置方法 — getPagedList/setStatus/getCountBy*
❌ Out of Scope
- 实体/Controller/Service 继承层级 → ddd4j-core
- Jackson JSON 配置 → ddd4j-jackson
LLM 最常犯的错误
| # | 错误 | 正确做法 |
|---|---|---|
| 1 | implements Serializable |
extends BaseEntity<T>(继承 Model,ActiveRecord) |
| 2 | 重复声明 createBy/createTime | BaseEntity 已声明,子类不要再加 |
| 3 | XML 写 #{entity.beginTime} |
写 #{model.beginTime} |
| 4 | new Page<T>(1, 10) |
用 PaginationEntity<T> 封装 |
| 5 | 逻辑删除写 UPDATE SET is_delete=1 |
用 @TableLogic,MyBatis-Plus 自动处理 |
| 6 | 自定义 Boolean 字段映射 | 用 BooleanEnum + CustomBooleanEnumTypeHandler |
| 7 | 手动 offset 计算 (pageNo-1)*limit |
PaginationEntity.getOffset() 已封装 |
核心规则速查
// ✅ 正确:实体定义
@Data
@TableName("sys_user")
public class SysUser extends BaseEntity<SysUser> { // Model<T> → ActiveRecord
// ↓ 已继承,不再声明
// Long createBy, createTime, updateBy, updateTime
// Integer isDeleted (@TableLogic)
// LocalDateTime beginTime, endTime (@TableField(exist=false), @JsonIgnore)
// String keywords, params (@TableField(exist=false), @JsonIgnore)
private String username;
private String status; // BooleanEnum 映射: 0=否, 1=是
}
// ✅ 分页查询
PaginationEntity<SysUser> entity = new PaginationEntity<>();
entity.setPageNo(1); // 第1页
entity.setLimit(15); // 默认15条
entity.setBeginTime(LocalDateTime.now().minusDays(7));
List<SysUser> list = baseService.getPagedList(
new Page<>(entity.getPageNo(), entity.getLimit()), entity
);
// ✅ Mapper XML(注意参数名 model)
<select id="getPagedList" resultType="SysUser">
SELECT * FROM sys_user
WHERE is_deleted = 0
<if test="model.beginTime != null">
AND create_time >= #{model.beginTime}
</if>
</select>
// ✅ 内置方法(BaseServiceImpl 已提供)
baseService.setStatus(id, "1"); // @Transactional
baseService.getCountByName("admin");
baseService.getCountByCode("ADMIN");
baseService.getCountByParent(parentId);
baseService.getValue("some_key"); // 从缓存读取
Gotchas
- 实体必须 extends BaseEntity,不能 implements Serializable — 失去 ActiveRecord 能力
- @Param("model") 是固定约定 — XML 引用必须是 #{model.xxx}
- PaginationEntity.getOffset() 是 Oracle 风格 —
((pageNo−1)*limit+1),不是0开头 - beginTime/endTime 在 Entity 上,不在独立 QueryDTO — 加
@TableField(exist=false)即可 - BooleanEnum 映射 Integer 0/1 — 数据库不用原生 Boolean 类型
- JSON 类型处理器有两套 — 核心用 fastjson2(jdbc包),cmet用 hutool
- 逻辑删除条件自动拼接 — SQL 不用写
is_deleted = 0(但复杂查询建议加上) - setStatus 是 @Transactional — 子类重写也必须加上事务注解
Data Privacy
本技能不收集、存储或传输任何用户数据。