跳转到正文

使用与配置

Base Business 通过继承和方法重写扩展 CRUD,没有模块专属的 YAML 配置前缀。先明确对象转换和持久化规则,再注册具体 Bean;最小装配见快速开始

选择实体模型

PO 基类字段与行为
BaseEntityPoLong idASSIGN_ID 主键策略;LocalDateTime createTime
BaseEntityPo.Create增加 createUserIdcreateUserName
BaseEntityPo.Modified继承 Create,增加修改人、modifiedTimeisDeletedversion
CreateMemo / ModifiedMemo在对应模型上增加 memo
CreateSort / ModifiedSort在对应模型上增加 sort,实现顺序辅助计算

这些类没有 @TableName,应用为具体 PO 指定表并建立相应列。Create 不含逻辑删除字段;Modified.isDeleted 使用 @TableLogic(value="0", delval="1")version 使用 @Version。数据库应初始化未删除标记及版本号,并在需要乐观锁时装配拦截器。Base Business 本身不创建表、不安装乐观锁拦截器,也不初始化这两个字段。

VO 和 SimpleVo 应选择与 PO 匹配的变体:BaseEntityVo 复制 ID、创建时间;Create/Modified 及 Memo/Sort 变体再复制对应字段。BaseEntitySimpleVo 只复制 ID,Memo/Sort 变体再带备注或排序,不复制审计人。应用自己的字段仍需在构造器中赋值。

isDeleted 在 PO 与完整 Modified VO 上标记为 JSON 忽略。公共 Long 和日期输出遵循 Base JSON 约定及应用转换器配置。

显式填充审计信息

  • po.init() 每次都把创建时间设为当前时间;它不会填创建人,即便对象继承了 Create。
  • po.init(loginUser) 只在创建时间为空时填时间;Create 在创建人 ID 为空时填创建人 ID 和名称。
  • Modified 的 init(loginUser) 还调用 update(loginUser),后者每次更新修改时间和修改人。
  • Sort 变体在 init(loginUser) 中将空排序值设为 0,提供的 getLastSort 只计算边界,不会写库或重排其他记录。

默认 addByPoupdateByPo 不调用以上初始化方法,也不调用 beforeInsert()。应在保存 Hook 或明确的应用服务中填充;审计用户取自可信的 SessionUserProvider,不能直接信任请求体中的操作人。数据库默认值不保证回填到新增时的内存 VO,需要完整数据库结果时重新查询。

DTO 校验与转换

BaseEntityDto.check() 仅调用默认空实现 init();没有必填、长度或权限校验。addByDtoupdateByDto 不调用 check(),应用入口应显式调用它,或用应用自己的校验方式。

BaseEntityDto.toPo() 调用 toPo(null);基类 toPo(entity) 原样返回参数,因此不重写时新增会得到 null。应用应显式创建新 PO 或修改旧 PO,再复制字段,完整示例见快速开始

带 Supplier 的辅助重载 toPo(entity, supplier) 在空对象时仅返回 Supplier 结果,在非空时动态调用 toPo(entity)。若子类在自己的 toPo(entity) 中再次调用这一辅助重载,更新分支会递归回到自身。应使用 entity == null ? new Entity() : entity 等明确分支;不要把辅助方法当作无条件复制字段的父类实现。

Memo/Sort DTO 的单参数 toPo(entity) 仅在参数非空时复制对应字段;新对象需要子类保证这些值已赋入。更新也不天然等同 PATCH:空字段是否保留、清空及最终 SQL 字段策略,由 DTO 与 Mapper 配置共同决定。

注册服务与构造器

常规服务直接继承:

text
AbstractBaseService<PO, DTO, VO, MAP, QUERY, MAPPER>

具体类注册为 Spring Bean,Mapper 通过 MyBatis 扫描注册,MapperUtil 单独导入或精确扫描。基类通过当前类的直接泛型父类反射提取 6 个实参,要求 PO 和 Query 有 public 无参构造器,VO 和 MAP 有 public 构造器且参数类型精确等于具体 PO 类

不要将直接父类改成无泛型的中间实现,或保留未解析的类型变量,再假定反射仍能识别。缺失构造器会使 toVotoMapVonewPoInstancenewQueryInstance 在调用时抛出对应构造器异常;不符合父类形态时也可能在实例化阶段失败。

@ServiceInfo(name="...") 可为缺失对象错误提供模块名称,但不注册接口或授予权限。BaseController 本身没有 CRUD 路由,仅声明 @CrossOrigin("*");应用可像快速开始一样直接定义 Controller,按实际需要设置跨域规则。

保存与读取 Hook

Hook 是 Service 内部的方法扩展,不是自动收集的独立 Bean 列表,也不是在事务提交后发送的事件。

入口默认调用顺序
addByDto(dto)dto.toPo()addByPo
addByPo(po)beforeAdd(po)mapper.inserttoVoafterAdd
updateByDto(id, dto)getPoByIddto.toPo(oldPo)updateByPo
updateByPo(po)beforeUpdatemapper.updateByIdtoVoafterUpdate
批量新增列表版 beforeAdd(List)MapperUtil.batchInsert → 逐条转换及 afterAdd(VO)
批量更新逐条 beforeUpdateMapperUtil.updateByIdBatch → 逐条转换及 afterUpdate
deleteById / ID 列表 / 外键删除直接 Mapper 删除;不调用 beforeDeleteafterDelete
updateByWrapper直接 mapper.update(null, wrapper);不调用保存 Hook
updateSwitchStatus读取 PO → 计算并设置 0/1 → 直接 mapper.updateById;不调用更新 Hook

默认 beforeAdd/beforeUpdate 分别执行 checkBeforeAdd/checkBeforeUpdate 返回的函数;后置方法同理。它们默认归到 checkBefore/checkAfter,这两个公共方法默认返回 null,表示不处理。

单条 beforeAdd(PO) 的重写不会被默认批量新增逐条调用。需要单条、批量一致时可重写 checkBeforeAdd(),或分别实现两个 beforeAdd 重载;也要保留原有规则的调用,避免重写方法后意外跳过公共函数。

queryPoByIdqueryPoByWrapper 的具体实现已经调用 afterGetPo,默认后者转调 afterQueryPo;非空结果再经过 getPoById 等强制存在入口时,会再次调用 afterGetPo。带默认 Supplier 的 query 重载也可能额外经过 afterQueryPo。这些 Hook 不应承担只能执行一次的扣减、发送等副作用。

列表补充通过 afterList 扩展,但并非所有列表重载都经过它,具体见查询与分页

当前批量写入的实现限制

MapperUtil.batchInsertupdateByIdBatch 调用 ListUtil.groupList(list, length, fun) 后没有遍历其返回结果。该分组是惰性的,只有访问分组时才执行 Mapper 回调,因此当前批量方法不会触发预期 SQL。addBatchByPoList / updateBatchByPoList 仍可能转换并返回 VO、执行后置 Hook,不能据返回列表判断落库。使用前需修正调用链并验证数据库结果;增加事务不能解决回调未执行的问题。方法及源码见 MapperUtil

事务与影响行数

普通 CRUD 和 MapperUtil 没有声明事务。多次写入、批次分组及后置处理需要整体回滚时,应用在通过 Spring 代理调用的公开服务入口声明事务。

单条新增/更新和多数删除方法不检查 Mapper 影响行数;更新返回的是当前内存对象转换成的 VO。乐观锁冲突或不存在的 ID 不能仅凭返回 VO 或 Controller 的 data=true 判断成功,需在应用层检查实际写入结果。

updateSwitchStatus 在指定值为空时切换已有状态;旧值为 null 会报错,旧值等于 1 时变 0,其余变 1。指定值非空时仅 1 映射为 1,其他整数映射为 0。这不是严格的输入枚举校验。

Mapper 与运行配置

装配项由谁提供用途与限制
数据源、驱动和 schema应用Base Business 不建表,不内置外部数据库驱动
@MapperScan 或受支持的 Mapper 注册方式应用扫描实际 Mapper 包;扫描 Service 不能替代 Mapper 注册
MapperUtil Bean应用导入/扫描AbstractBaseService 的必需依赖,即使只用单条方法也要注册
pagehelper.*传递依赖的 PageHelper Starterquery.page() 后的数据库查询提供分页拦截
mybatis-plus.*MyBatis-Plus Starter表映射、XML 位置及框架参数;不是本模块专属配置
扩展 SQL 注入器应用或 MyBatis 组件使 BaseMapper 额外方法有可执行的 SQL

BaseMapper 除标准 MyBatis-Plus API 外,还声明 insertBatchinsertIgnoreupdateByIdBatchupdateByIdBatchSomeColumn、最大/最小排序、排序增减,以及 ignoreLogicDeleteUpdate*。只继承接口不等于 SQL 已存在。

需要框架的扩展实现时,在已管理 Component 版本的应用中增加:

xml
<dependency>
    <groupId>com.own.component</groupId>
    <artifactId>springboot-component-mybatis</artifactId>
</dependency>

MyBatis 组件的自动配置按 Bean 缺失条件提供 CustomInjector 及乐观锁、防全表更新删除拦截器。已有自定义 ISqlInjectorMybatisPlusInterceptor 时,需要应用合并所需能力。扩展 SQL 的方言、字段要求和审计切面是 MyBatis 组件的职责;不要把单表 H2 示例理解成这些扩展已在所有数据库验证可用。

MapperUtil 默认每 1000 条分组调用 insertBatchupdateByIdBatch,可用带 length 的重载传入正分组长度。它不检查每组影响行数、不提供重试或全批事务;批量调用前应先完成校验,再配置事务及所需 SQL。

忽略逻辑删除的方法明确绕过正常删除过滤,不能直接作为普通用户更新入口;恢复规则和授权由应用定义。关联操作与导入导出边界见关系绑定与任务