04-实体与数据访问 pojo-mapper¶
📄 创建: ZCode AI 2026-08-14 · 修改: JimWb 2026-08-14
bams-pojo(50 个类)定义共享实体/DTO/VO 和类型处理器;bams-mapper(30 个类)提供 MyBatis-Plus 数据访问增强。
一、BaseEntity —— 所有业务实体的父类¶
cn.cnbm.bams.pojo.base.BaseEntity
字段清单¶
| 字段 | 类型 | 注解 | 说明 |
|---|---|---|---|
id |
String |
@TableId + @JsonSerialize(ToStringSerializer) |
主键。String 类型,雪花算法生成。JSON 序列化强制转字符串防前端精度丢失 |
createdBy |
String |
@TableField("created_by", fill=INSERT) |
创建人 ID |
createdByName |
String |
@TableField(exist=false) |
创建人名称(不入库,仅展示) |
createdTime |
LocalDateTime |
@TableField(fill=INSERT) + @JsonFormat |
创建时间 |
updatedBy |
String |
@TableField(fill=INSERT_UPDATE) |
更新人 ID |
updatedByName |
String |
@TableField(exist=false) |
更新人名称 |
updatedTime |
LocalDateTime |
@TableField(fill=INSERT_UPDATE) + @JsonFormat |
更新时间 |
自动填充机制¶
id 的填充不由 @TableId 完成,而是由 EnergyMetaObjectHandler.insertFill()(bams-pojo)在插入时调 IDUtil.getId() 生成。
EnergyMetaObjectHandler(抽象类,cn.cnbm.bams.ibatis.handler):
- insertFill:id 为空则 IDUtil.getId();createdTime/updatedTime 设当前时间;createdBy/updatedBy 从 getCurrentLoginUser().get("userId") 取(默认 0L)
- updateFill:只填 updatedTime/updatedBy
- 抽象方法 getCurrentLoginUser():返回 Dict,由业务应用实现
common-func 的
MyBatisPlusMetaObjectHandler继承它,从PermissionContextHolder取 userId。详见 09-通用功能。
继承注意事项¶
⚠️ 子类必须用
@EqualsAndHashCode(callSuper = true)才能把 BaseEntity 的字段纳入 equals/hashCode。历史代码中部分子类用了callSuper = false(BdDept/BdPerson/BdPost 等),这是遗留问题。详见 12-FAQ与注意事项。
// ✅ 正确写法
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("my_table")
public class MyEntity extends BaseEntity {
private String name;
}
// ⚠️ 不推荐(id 等字段不纳入 equals/hashCode)
@Data
@EqualsAndHashCode(callSuper = false) // 历史遗留
public class LegacyEntity extends BaseEntity { ... }
BaseEntity.of() 静态方法¶
把 Map 或 Bean 转成目标实体(用 hutool BeanUtil),方便从 Dict 查询结果反构实体。
二、实体清单(按业务域)¶
bd 域 —— 基础主数据¶
| 实体 | 表名 | 包路径 | 说明 |
|---|---|---|---|
BdCorp |
bd_corp |
pojo.bd |
公司/工厂。实现 ITreeAble<BdCorp>(树形)。含编码/类型/经纬度/联系人/def1-def10 扩展字段 |
BdDept |
bd_dept |
pojo.bd |
部门。编码/名称/上级/corpCode/deleted |
BdPerson |
bd_person |
pojo.bd |
人员。编码/姓名/性别/部门/岗位/accountCode 对接底座。@CheckRepeat 校验编码唯一 |
BdPost |
bd_post |
pojo.bd |
岗位。编码/名称/排序/corpCode/deleted |
BdUnit |
bd_unit |
pojo.bd |
计量单位。@SequenceGenerator 自动生成编码(前缀 "U")。含换算系数/精度/量纲 |
BdDictionary |
bd_dictionary |
pojo.bd |
字典项。dkey/dtype/dname/dvalue + 3 扩展值/colorType |
BdDictionaryHead |
bd_dictionary_head |
pojo.bd |
字典分类。dtype/dname |
OrgInfo |
(约定) | pojo.bd |
组织机构。不继承 BaseEntity,独立定义。含 tenantId + fieldText1-15 等通用扩展字段,对接外部组织数据 |
sys 域 —— 系统管理¶
| 实体 | 表名 | 包路径 | 说明 |
|---|---|---|---|
SysUser |
sys_user |
pojo.sys |
用户。含主子账号(pid/accountType)、superAdmin、loginName(@CheckRepeat)、密码、登录IP/时间 |
SysRole |
sys_role |
pojo.sys |
角色。roleCode/roleName(@CheckRepeat)、dataScope 数据权限范围 |
SysResource |
sys_resource |
pojo.sys |
菜单资源。实现 ITreeAble(树形)。menuType(dir/menu)/menuCode/path |
SysConfig |
sys_config |
pojo.sys |
参数配置。category/configKey/val/visible |
SysLog |
sys_log |
pojo.sys |
接口日志。requestIp/requestUrl/requestParams/responseBody/costTime |
SysOperLog |
sys_oper_log |
pojo.sys |
业务操作日志。businessType(增改删)/operName/operParam/jsonResult |
SysTenantDb |
sys_tenant_db |
pojo.sys |
多租户数据源配置(见下文专述) |
SysBusinessStatus |
sys_business_status |
pojo.sys |
业务状态配置。appCode/businessCode/businessStatus JSON |
SysButton |
sys_button |
pojo.sys |
资源按钮 |
SysMenuButton |
sys_menu_button |
pojo.sys |
菜单按钮关联(含 authCode 权限码) |
SysApp |
sys_app |
pojo.sys |
应用管理。appCode/authMode 认证模式/supportTerminal |
SysUserRole |
sys_user_role |
pojo.sys |
用户-角色关联(不继承 BaseEntity,轻量 POJO) |
SysRoleResource |
sys_role_resource |
pojo.sys |
角色-资源关联(不继承 BaseEntity) |
SysRoleButton |
sys_role_button |
pojo.sys |
角色-按钮关联(不继承 BaseEntity) |
SysAppRole |
sys_app_role |
pojo.sys |
应用-角色关联(不继承 BaseEntity) |
规律:关联表实体(UserRole/RoleResource/RoleButton/AppRole)都是轻量 POJO,不走 BaseEntity 的审计字段。
pub 域 —— 公共¶
| 实体 | 表名 | 说明 |
|---|---|---|
PubFileUpload |
pub_file_upload |
文件上传记录。fileName/content(byte[])/bizId/bizType |
PubSequence |
pub_sequence |
序列号配置。sequenceCode/resetType/前缀/后缀/步长/currNo + @Version 乐观锁 |
三、BaseMapperExt —— 数据访问增强¶
cn.cnbm.bams.mapper.ext.BaseMapperExt<T>
继承 MyBatis-Plus 的 BaseMapper<T>,新增 5 个方法。业务 Mapper 一律继承此类。
新增方法¶
| 方法签名 | 说明 |
|---|---|
<P extends IPage<Dict>, R extends Dict> P selectPageMaps(P page, @Param(Constants.WRAPPER) Wrapper<R> params) |
核心:分页查询返回 Dict(Map 子类)而非实体。适合动态列表/报表,免去建 VO |
boolean executeSqlForInsert(@Param("sql") String sql) |
执行原生 INSERT SQL |
Dict executeSqlForSelectOne(@Param("sql") String sql) |
原生 SELECT 返回单行 Dict |
List<Dict> executeSqlForSelectList(@Param("sql") String sql) |
原生 SELECT 返回 Dict 列表 |
List<Map<String,Object>> executeSqlForList(@Param("sql") String sql) |
原生 SELECT 返回 Map 列表 |
⚠️ 后 4 个
executeSql*方法用${sql}字符串拼接(非预编译),存在 SQL 注入风险,应由 SDK 内部受控调用,不要直接透传用户输入。
selectPageMaps 示例¶
// 分页查 Map 结果(无需建 VO)
Page<Dict> page = new Page<>(1, 10);
QueryWrapper<MyEntity> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1);
Page<Dict> result = myMapper.selectPageMaps(page, wrapper);
Dict 类¶
cn.cnbm.bams.common.core.lang.Dict —— 自定义 Map 子类,重写了 getOrDefault 以区分 null 值。
四、SqlInjectorExt 机制 —— 自定义方法全局注入¶
三个类协作,把 selectPageMaps 自动注入到所有继承 BaseMapperExt 的 Mapper:
工作原理¶
SqlMethodExt(枚举,定义方法元信息)
│ 定义方法名 "selectPageMaps" + SQL 模板
▼
SelectPageMaps(继承 AbstractMethod)
│ injectMappedStatement() 构造 MappedStatement
│ 返回类型映射为 Dict.class
▼
SqlInjectorExt(@Component,继承 DefaultSqlInjector)
│ getMethodList() 在 MP 默认方法基础上追加 SelectPageMaps
│ 因是 @Component,Spring Boot 启动时对所有 Mapper 全局生效
关键文件¶
| 文件 | 路径 | 说明 |
|---|---|---|
SqlMethodExt |
mapper.ext.enums |
枚举,定义 SELECT_PAGE_MAP 方法元信息 |
SelectPageMaps |
mapper.ext.methods |
继承 MP AbstractMethod,构造 MappedStatement |
SqlInjectorExt |
mapper.ext.injector |
@Component,继承 DefaultSqlInjector,重写 getMethodList 追加自定义方法 |
辅助工具¶
| 类 | 说明 |
|---|---|
SqlScriptHelper |
用 Druid SQL 解析器改写子查询、给列名加表前缀;遍历 @Formula 注解拼 select 列 |
SqlUtil |
反射读 @TableName/@TableField 生成 SELECT col as field FROM table |
五、@Formula 注解 —— 实体字段 = 子查询¶
cn.cnbm.bams.common.annotations.mybatis.Formula
标记在实体字段上,让该字段的值来自一个子查询表达式。
@Formula("(SELECT COUNT(*) FROM order WHERE order.cust_id = id)")
private Integer orderCount; // 查询时自动执行子查询填充
ServiceExtImpl.getQueryWrapper和SqlScriptHelper会解析@Formula注解,把子查询拼到 SELECT 列中。getById也重写了以支持@Formula。
六、多租户数据源机制¶
@TenantDataSource 注解¶
cn.cnbm.bams.common.annotations.TenantDataSource(在 common-tool 模块)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TenantDataSource {
String value() default "master"; // 数据源标识,默认主库
}
标记类/方法走哪个数据源。如 @TenantDataSource("sys") 表示操作 sys 系统库。
SysTenantDb 实体¶
cn.cnbm.bams.pojo.sys.SysTenantDb(表 sys_tenant_db)
承载每个租户的数据源连接信息:一个租户一行记录 = 一个独立数据库连接配置。
| 字段 | 说明 |
|---|---|
tenantCode |
租户编码 |
dsKey |
数据源标识 |
jdbcDriver / jdbcUrl / jdbcPort / jdbcDb |
连接信息 |
jdbcUsername / jdbcPasswd |
凭证 |
tenantDbInitFlag |
是否已初始化库表(0否1是) |
enableState |
启用状态 |
corpCode |
公司编码 |
⚠️ 动态数据源是半成品:
AbstractRoutingDataSource+ ThreadLocal + Manager 框架完整,@TenantDataSource注解已定义,但缺少 AOP 切面实现注解→自动切换,且DynamicDataSourceBean 未注册。详见 05-业务服务层。
七、DTO / VO 补充¶
常用 DTO¶
| DTO | 包路径 | 说明 |
|---|---|---|
TokenInfoDTO |
dto |
底座 token 返回(accountId/orgCode/userAccount) |
RedisPointData |
dto.redis |
实时点位数据(tm/unit/val) |
SysMenuButtonDTO |
dto.sys |
按钮请求参数 |
SysResourceDTO |
dto.sys |
菜单树 DTO(实现 ITreeAble) |
BdCorpCacheDTO |
dto.bd |
公司缓存(含 parentCodeList/childCodeList 祖先后代链) |
常用 VO¶
| VO | 包路径 | 说明 |
|---|---|---|
QueryCondition |
vo |
单查询条件(field/opt/value) |
TableData |
vo |
表格数据导出容器(继承 BasicParamWrapper) |
TableHeader |
vo |
表头(含 children 树形,支持 dictCode 字典翻译) |
SysAccount |
vo |
底座虚拟账户 JSON 反序列化模型 |
VersionInfo |
vo |
服务版本信息 |
MessageVo / MqDataVo |
vo.mq |
MQ 消息/信封 |
BasicParamWrapper / BasicParamers¶
cn.cnbm.bams.pojo.basics.BasicParamWrapper(继承 BasicParamers)
通用查询参数容器。前端传一个扁平 JSON,后端按操作符分桶构建 QueryWrapper。
| 参数 Map | 操作符 | 示例 |
|---|---|---|
andMap |
= | 精确匹配 |
likeMap / andLikeMap / rightLikeMap |
LIKE | 模糊匹配 |
geMap / leMap |
>= / <= | 范围 |
inMap / notInMap |
IN / NOT IN | 集合 |
orMap |
OR | 或条件 |
orderBy |
ORDER BY | 排序 |
// 前端传参示例
{
"current": 1,
"size": 10,
"searchVal": "关键词",
"andMap": {"status": 1, "corp_code": "C001"},
"likeMap": {"name": "张"},
"orderBy": {"created_time": "desc"}
}
// Service 层使用
Page<MyEntity> page = myService.listPage(basicParamWrapper);
ServiceExtImpl.getQueryWrapper()会解析 BasicParamWrapper 自动构建 MyBatis-Plus QueryWrapper,详见 05-业务服务层。
八、ibatis 类型处理器¶
| 类 | 包路径 | 说明 |
|---|---|---|
EnergyMetaObjectHandler |
ibatis.handler |
抽象类,BaseEntity 审计字段自动填充(见上文) |
SerializableTypeHandler |
ibatis.handler |
Serializable 与 VARCHAR/BIGINT 互转 |
MapToBeanMapper<BEAN> |
ibatis.mapper |
Map→Bean 的 Function,.map(MapToBeanMapper.create(Xxx.class)) |
RefreshRedisEvent |
events |
Spring 事件,通知刷新 Redis 缓存 |