Skip to content

MyBatis 数据库 ​

这篇解决什么 ​

给新表写 DO 和 Mapper,单表分页用 selectPage,联表分页用 selectJoinPage。读完能改 AdminUserDO / AdminUserMapper 这一路,并知道逻辑删除、自动填充、JSON / 加密字段怎么挂。

DO 转 VO、字段翻译见 VO 对象转换与数据翻译。多租户字段见 多租户。模块怎么拆见 项目结构。

默认端口 48080,管理端前缀 /admin-api。

组件位置 ​

名称说明仓库路径
StarterMyBatis Plus、分页插件、自动填充、Joinruoyi-office/yudao-framework/yudao-spring-boot-starter-mybatis/
BaseDO审计字段 + @TableLogic.../mybatis/core/dataobject/BaseDO.java
TenantBaseDOBaseDO 加 tenantIdyudao-spring-boot-starter-biz-tenant/.../TenantBaseDO.java
BaseMapperX单表 / 联表 CRUD 与分页.../mybatis/core/mapper/BaseMapperX.java
LambdaQueryWrapperX空值不进 SQL.../mybatis/core/query/LambdaQueryWrapperX.java
MPJLambdaWrapperX联表条件,同样有 xxxIfPresent.../mybatis/core/query/MPJLambdaWrapperX.java
配置id-type、逻辑删除值、加密密钥ruoyi-office/yudao-server/src/main/resources/application.yaml
范文用户 DO / Mapperyudao-module-system 的 AdminUserDO、AdminUserMapper

YudaoMybatisAutoConfiguration 会扫描带 @Mapper 的接口,并注册 DefaultDBFieldHandler、PaginationInnerInterceptor。

请求怎么落到表 ​

列表接口不在 Controller 里拼 Wrapper。Controller 收 VO,Service 编排,Mapper 查表。

Controller 到表

示意图:管理端用户列表落到 AdminUserMapper,再进 system_users。

getUserPage 在 AdminUserServiceImpl:先按角色 / 部门收窄 userIds、deptIds,再调用 userMapper.selectPage。

实体 ​

DO 放 dal.dataobject,类名以 DO 结尾。注解先对齐表,再写业务字段。

名称说明仓库路径
@TableName表名。JSON / 加密字段必须 autoResultMap = trueAdminUserDO → system_users
@TableId主键,推荐 Long 自增AdminUserDO.id
@KeySequenceOracle / PostgreSQL 等序列;MySQL 可不写AdminUserDO、DataSourceConfigDO
@TableField(fill)插入 / 更新自动填BaseDO 的审计字段
@TableLogic逻辑删除BaseDO.deleted
@TableField(typeHandler)JSON、集合、加密AdminUserDO.postIds、DataSourceConfigDO.password
java
@TableName(value = "system_users", autoResultMap = true)
@KeySequence("system_users_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class AdminUserDO extends TenantBaseDO {

    @TableId
    private Long id;
    private String username;
    @TableField(typeHandler = JacksonTypeHandler.class)
    private Set<Long> postIds;
    // ...
}

postIds 在库里是 JSON 字符串。没有 autoResultMap = true,TypeHandler 读不回来。

BaseDO 字段 ​

createTime / updateTime 是 LocalDateTime,不是 Date。creator / updater 是 String,存登录用户 id。

名称说明填充
createTime创建时间FieldFill.INSERT
updateTime最后更新时间FieldFill.INSERT_UPDATE
creator创建人 idFieldFill.INSERT
updater更新人 idFieldFill.INSERT_UPDATE
deleted逻辑删除,0 未删、1 已删@TableLogic

DefaultDBFieldHandler 在插入、更新时补这些字段。登录用户来自 SecurityFrameworkUtils.getLoginUserId(),写入时转成字符串。

TenantBaseDO 只多 tenantId。AdminUserDO 继承它。拦截器怎么拼租户列,见 多租户。

application.yaml 里 id-type 默认 NONE:按数据源自动选 AUTO 或 INPUT。要改成雪花,把 mybatis-plus.global-config.db-config.id-type 设为 ASSIGN_ID,并去掉实体上的 @KeySequence。

不要用 DO 当接口出入参

创建用户只该收账号、昵称这些业务字段。用 DO 会让 createTime、creator 也能被传入。Swagger 和校验挂在 VO 上,DO 只映表。

整颗 DO 从前端回传再更新前,先 clean(),清掉 creator / createTime / updater / updateTime。

逻辑删除不要自己拼

BaseMapperX 的 SELECT / UPDATE / DELETE 会自动带 deleted = 0。不要关 @TableLogic 又手写 deleted = 0。要查已删行,只能手写 XML 或 @Select。

唯一索引带上 deleted

逻辑删除后再插入同一业务键,会撞唯一索引。建唯一键时把 deleted(以及需要时的 tenant_id)加进去,例如资产表的 uk_asset_code_tenant。

编码约定 ​

名称说明仓库路径
包DO 在 dal.dataobject,Mapper 在 dal.mysql例如 .../dal/mysql/user/AdminUserMapper.java
注释外键、枚举、冗余写清楚AdminUserDO.sex、status
查询放 MapperService 不直接 new LambdaQueryWrapperTenantMapper.selectPage
方法名selectBy条件selectByUsername、selectListByDeptIds
条件优先 Lambda,少写列名字符串AdminUserDO::getUsername

简单单表查询用 Mapper 的 default 方法,方便复用。

为什么查询不写在 Service

同样的 selectByUsername 会在登录、导入、校验里复用。散落在 Service 里会复制同一段 Wrapper,也难聚焦业务。

Mapper 方法 ​

Mapper 继承 BaseMapperX<T>,它再继承 MyBatis Plus Join 的 MPJBaseMapper。入参分页用 PageParam,出参用 PageResult。

名称说明仓库路径
selectOne按一到三个字段查一条AdminUserMapper.selectByUsername
selectCount条件计数TenantMapper.selectCountByPackageId
selectList多条;in 空集合直接返回空列表selectListByDeptIds
selectPagePageParam → IPage → PageResultTenantMapper.selectPage、AdminUserMapper.selectPage
selectJoinPage联表分页,结果可投影到别的 classCrmBusinessMapper.selectPage、WmsItemSkuMapper.selectPage
insertBatch批量插入;SQL Server 会改成逐条BaseMapperX.insertBatch

pageSize 为 PageParam.PAGE_SIZE_NONE(-1)时改成查全部,导出接口常用。

java
@Mapper
public interface TenantMapper extends BaseMapperX<TenantDO> {

    default PageResult<TenantDO> selectPage(TenantPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<TenantDO>()
                .likeIfPresent(TenantDO::getName, reqVO.getName())
                .likeIfPresent(TenantDO::getContactName, reqVO.getContactName())
                .likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile())
                .eqIfPresent(TenantDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime())
                .orderByDesc(TenantDO::getId));
    }
}

AdminUserMapper.selectByUsername 就是 selectOne(AdminUserDO::getUsername, username)。带部门、关键字的用户分页看同源文件的 selectPage。

并发下可能插出多条时,不要用会报「期望一条」的 selectOne,改用 selectFirstOne 或 selectLastOne。行锁用 selectOneForUpdate,必须包在事务里。

条件构造器 ​

LambdaQueryWrapperX、QueryWrapperX 在 Plus 的 Wrapper 上加了 xxxIfPresent:值为空就不拼进 SQL。

名称说明
likeIfPresent / likeRightIfPresent字符串有内容才 like
eqIfPresent / neIfPresent相等 / 不等
inIfPresent集合或数组非空才 in
gtIfPresent / geIfPresent / ltIfPresent / leIfPresent比较
betweenIfPresent两个都有走 between;只传一端则 >= 或 <=

联表用 MPJLambdaWrapperX,方法同名,列可以用主表或副表的方法引用。

联表分页 ​

单表能解决就多次查询、内存拼。必须 Join 时,先写 MPJLambdaWrapperX,不要先上 XML。

java
MPJLambdaWrapperX<WmsItemSkuDO> query = new MPJLambdaWrapperX<WmsItemSkuDO>()
        .selectAll(WmsItemSkuDO.class)
        .innerJoin(WmsItemDO.class, WmsItemDO::getId, WmsItemSkuDO::getItemId)
        .likeIfPresent(WmsItemDO::getName, reqVO.getItemName())
        .likeIfPresent(WmsItemSkuDO::getCode, reqVO.getCode())
        .orderByDesc(WmsItemSkuDO::getId);
return selectJoinPage(reqVO, WmsItemSkuDO.class, query);

完整实现在 yudao-module-wms 的 WmsItemSkuMapper。CRM 商机列表在 CrmBusinessMapper.selectPage 里用同一套 selectJoinPage 拼数据权限。

需要把副表字段平铺或内嵌时,把目标 class 传给 selectJoinPage / selectJoinList:selectAs 平铺一列,selectAssociation 嵌一整段对象。不要把多表列全堆回主 DO。

application.yaml 的 mybatis-plus-join.sub-table-logic 默认 true,副表也会加逻辑删除。

XML 分页有两种:自己写 LIMIT + COUNT;或方法入参 / 返回用 IPage,只写查询 SQL,由分页插件补条数。能用第二种就用第二种,总条数为 0 时不会再查列表。

XML 列表要自己核数据权限

部门数据权限走 SQL 改写。复杂手写 XML 可能拼不上规则。能用 Wrapper 的列表不要改成 XML。

复杂字段 ​

TypeHandler 做 Java 类型和 JDBC 类型转换。自定义实现放 cn.iocoder.yudao.framework.mybatis.core.type。

名称说明仓库路径
JacksonTypeHandlerJSON ↔ Java 对象 / 集合AdminUserDO.postIds
EncryptTypeHandler字段 AES 加解密DataSourceConfigDO.password、OaMailAccountDO
LongListTypeHandler 等逗号分隔的 id 列表.../mybatis/core/type/
java
@TableName(value = "infra_data_source_config", autoResultMap = true)
public class DataSourceConfigDO extends BaseDO {

    @TableField(typeHandler = EncryptTypeHandler.class)
    private String password;
}

密钥是 mybatis-plus.encryptor.password。加密列只能等值查询,条件里要先 EncryptTypeHandler.encrypt(...)。不能 like。

Mapper XML ​

XML 放各 yudao-module-xxx-server 的 src/main/resources/mapper/。统计、窗口函数这类 Wrapper 写不清的 SQL 再放这里,例如 VisitStatsMapper.xml。

动态 WHERE 仍优先 ifPresent 或 <if test>,少拼字符串。分页不要和 selectPage 两套计数混用。

配置与操作 ​

Mapper 继承 BaseMapperX,条件用 LambdaQueryWrapperX。XML 只放 Wrapper 写不清的 SQL。改数据源 yaml 要重启。

开启见 框架层。

相关篇 ​

联系我们

获取报价、演示和二开方案

微信咨询二维码

微信咨询

17156169080

添加时备注「RuoYi Office」

在线体验商业版