微服务-mybatisPlus
mybatisPlus官网
详细mybatisPlus解析
一.快速入门
1.1 入门案例
- 引入MybatisPlus的起步依赖(导入依赖)(使用mybatisplus依赖替代mybatis依赖);
- 定义Mapper(自定义Mapper继承MybatisPlus提供的BaseMapper接口,如public interface UserMapper extends BaseMapper{})
BaseMapper中的方法规则:
新增-insert();
修改-updateXXX();
删除-deleteXXX();
查询-selectXXX();
注意:继承父类BaseMapper时,一定要带上实体类泛型,如User;
1.2 常见注解
MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息;
实体类符合数据库表格字段定义【约定大于定义】:
- 类名驼峰转下划线作为表名;
- 名为id的字段作为主键;
- 变量名驼峰转下划线作为表的字段名;
注:以上是要做到实体类和数据库表格字段定义规则;
实体不符合数据库表格字段定义(需要使用注解):
@TableName:用来指定表名,如@TableName("tb_user)加到实体类User上;
@TableId:用来指定表中的主键字段信息,如@TableId(value=“id”,type=IdType.type)加到主键属性上;
value:指数据库表中的主键名;IdType.type:type指数据库表中的主键是否呈现自增长类型(AUTO:数据库自增长,INPUT:通过set方法自行输入,ASSIGN_ID:分配ID(默认id使用),接口IdentitierGenerator的方法nextId来生成id,默认实现类为DefaultdentifierGenerator雪花算法-Long类型);
- @TableField:用来指定表中的普通字段信息,如@TableField(“数据库表中字段名”);
使用@TableField的常见场景:
- 成员变量名于数据库字段名不一致;
- 成员变量名以is开头,且是布尔值;
- 成员变量名于数据库关键字冲突,如order关键字:@TableField(“
order”); - 成员变量不是数据库字段,如@TableField(exist = false),表示被标识的变量不是数据库表中的字段;
1.3 常见配置
MyBatisPlus的配置项继承了Mybatis原生配置和一些自己特有的配置:
mybatis-plus:type-aliases-package:com.yueyue.mp.domain.po#实体类,别名扫描包mapper-locations:"classpath*:/mapper/**/*.xml"#Mapper.xml文件地址,默认值configuration:map-underscore-to-camel-case:true#是否开启下划线和驼峰的映射cache-enabled:false#是否开启二级缓存global-config:db-config:id-type:assign_id#id为雪花算法生成update-strategy:not_null#更新策略:只更新非空字段;二.核心功能
2.1 条件构造器
Wrapper(AbstractWrapper(AbstractLambdaWrapper(LambdaUpdateWrapper,LamndaQueryWrapper)),UpdateWrapper,QueryWrapper)
LambdaUpdateWrapper:当在使用数据库表中对应的字段时,直接使用实体类进行引用字段,而非写定,若为写定的则为硬编码;
2.2 自定义sql
利用MyBatisPlus的Wrapper来构建复杂的where条件,然后自己定义SQL语句中剩下的部分;
(1)基于Wrapper构建where条件;
(2)在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew;
(3)自定义SQL,并使用Wrapper条件;
2.3 Service接口
Service接口中方法规则:
新增--saveXXX();
新增或修改--saveOrUpdateXXX();
删除--removeXXX();
查询多个--listXXX();
查询一个--getXXX();
查询数量--countXXX();
分页查询--pageXXX();
复杂条件查询--lambdaXXX();
service层提供接口类继承IService接口类,其中还存在ServiceImpl实现类
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{}UserMapper:对应的mapper层;
User:实体类;
(1)MP的Service接口使用流程是怎样的?
自定义Service接口继承IService接口;
publicinterfaceIUserServiceextendsIService<User>{}(2)自定义Service实现类,实现自定义接口并继承ServiceImpl类;
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{}注意:
- 在controller层中使用service层,推荐使用【构造函数】注入:
在类上加上注解@RequiredArgsConstructor
privatefinalIUserServiceuserService;- 可以使用hutool工具将一个实体类中的字段值复制到含有相同属性的实体类中;
dto:
vo:查询时使用;
2.4 总结
| Wrapper类 | 用途 | 字段引用方式 | 适用场景 |
|---|---|---|---|
| QueryWrapper | 查询 | 字符串 (如"name") | 动态条件多,字段名可配置 |
| UpdateWrapper | 更新 | 字符串 (如"name") | 动态更新字段,不依赖实体对象 |
| LambdaQueryWrapper | 查询 | Lambda (如User::getName) | 推荐,类型安全,避免字段名硬编码 |
| LambdaUpdateWrapper | 更新 | Lambda (如User::getName) | 推荐,类型安全,避免字段名硬编码 |
| 类别 | 方法 | 说明 | SQL示例 | 使用示例 (LambdaWrapper) |
|---|---|---|---|---|
| 比较 | eq | 等于 = | name = '张三' | .eq(User::getName, "张三") |
ne | 不等于 <> | age <> 18 | .ne(User::getAge, 18) | |
gt | 大于 > | age > 18 | .gt(User::getAge, 18) | |
ge | 大于等于 >= | age >= 18 | .ge(User::getAge, 18) | |
lt | 小于 < | age < 18 | .lt(User::getAge, 18) | |
le | 小于等于 <= | age <= 18 | .le(User::getAge, 18) | |
between | BETWEEN … AND … | age BETWEEN 18 AND 30 | .between(User::getAge, 18, 30) | |
notBetween | NOT BETWEEN … AND … | age NOT BETWEEN 18 AND 30 | .notBetween(User::getAge, 18, 30) | |
| 模糊 | like | LIKE ‘%值%’ | name LIKE '%张%' | .like(User::getName, "张") |
notLike | NOT LIKE ‘%值%’ | name NOT LIKE '%张%' | .notLike(User::getName, "张") | |
likeLeft | LIKE ‘%值’ | name LIKE '%张' | .likeLeft(User::getName, "张") | |
likeRight | LIKE ‘值%’ | name LIKE '张%' | .likeRight(User::getName, "张") | |
| 空值 | isNull | IS NULL | name IS NULL | .isNull(User::getName) |
isNotNull | IS NOT NULL | name IS NOT NULL | .isNotNull(User::getName) | |
| 范围 | in | IN (值集合) | id IN (1,2,3) | .in(User::getId, Arrays.asList(1,2,3)) |
notIn | NOT IN (值集合) | id NOT IN (1,2,3) | .notIn(User::getId, Arrays.asList(1,2,3)) | |
inSql | IN (子查询/语句) | id IN (SELECT id FROM ...) | .inSql(User::getId, "SELECT id FROM user WHERE age > 18") | |
notInSql | NOT IN (子查询/语句) | id NOT IN (SELECT id FROM ...) | .notInSql(User::getId, "SELECT id FROM user WHERE age < 5") | |
| 逻辑 | and | AND 嵌套 | WHERE (name='A' AND age=18) | .and(w -> w.eq(User::getName, "A").eq(User::getAge, 18)) |
or | OR 连接 / OR 嵌套 | WHERE id=1 OR name='李' | .eq(User::getId, 1).or().eq(User::getName, "李") | |
nested | 独立嵌套子句 | WHERE (name='王' AND status=1) | .nested(w -> w.eq(User::getName, "王").eq(User::getStatus, 1)) | |
| 排序/分组 | orderByAsc | ORDER BY … ASC | ORDER BY id ASC, name ASC | .orderByAsc(User::getId, User::getName) |
orderByDesc | ORDER BY … DESC | ORDER BY id DESC | .orderByDesc(User::getId) | |
groupBy | GROUP BY … | GROUP BY age | .groupBy(User::getAge) | |
having | HAVING … | HAVING COUNT(*) > 1 | .having("COUNT(*) > {0}", 1) | |
| 高级SQL | apply | 拼接SQL片段(参数安全绑定) | date_format(ctime, '%Y') = '2023' | .apply("date_format(ctime, '%Y-%m-%d') = {0}", "2023-01-01") |
last | 拼接至最后(直接拼,有注入风险,慎用) | LIMIT 1 | .last("LIMIT 1") | |
exists | EXISTS (子查询) | EXISTS (SELECT 1 FROM ...) | .exists("SELECT 1 FROM order WHERE user_id = user.id") | |
notExists | NOT EXISTS (子查询) | NOT EXISTS (SELECT 1 FROM ...) | .notExists("SELECT 1 FROM order WHERE user_id = user.id") | |
| 查询字段 | select | 指定查询的列 | SELECT id, name FROM user | .select(User::getId, User::getName) |
| 更新操作 | set | 设置UPDATE的SET列 (UpdateWrapper专用) | SET name = '新名字' | .set(User::getName, "新名字") |
setSql | 直接设置SET片段 (UpdateWrapper专用) | SET name = '新名字' | .setSql("name = '新名字'") | |
setIncrBy | 字段自增 (3.5.6+) | num = num + 1 | .setIncrBy(Product::getNum, 1) | |
setDecrBy | 字段自减 (3.5.6+) | num = num - 1 | .setDecrBy(Product::getNum, 1) |
三.扩展功能
3.1 代码生成
- 实体类
使用注解(@TableName("user")、@TableId(type=IdType.AUTO等等)),使实体类和数据库表对应; - mapper层
publicinterfaceUserMapperextendsBaseMapper<User>{}- service接口
publicinterfaceIUserServiceextendsIService<User>{}- service接口实现类
@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{}代码生成器:
MybatisX官方快速开发插件,其是IDEA的快速开发插件,能够生成代码;
Mybatisplus插件,其也是IDEA的快速开发插件,能够生成代码(推荐使用);
- 安装上面两种插件;
- 在idea中上方出现Other选项,在其中配置连接数据库,
- 选择Other中的code Generator,填写module(没有子模块不用填);
3.2 静态工具
Db:方法和IService中类似,但Db中的方法都是静态的,在调用方法的方式上有所不同;
3.3 逻辑删除
逻辑删除:基于代码逻辑模拟删除效果,但并不会真正删除数据;
application.yml文件中配置逻辑删除的字段名称和值:
mybatis-plus:global-config:db-config:logic-delete-field:flag#全局逻辑删除的实体字段名,字段类型可以是boolean、integerlogic-delete-value:1#逻辑已删除值(默认为 1)logic-not-delete-value:0#逻辑未删除值(默认为 0)3.4 枚举处理器
枚举处理器:实体类中有一个用户状态字段;
在application.yml中配置全局枚举处理器
3.5 JSON处理器
数据库表中,存在【json】类型的字段,其实体类应该对应类型是【String】类型,但我们所需要的是直接将json类型的值,调整为对象类型对应的值;
将json类型数据转换为对象类型数据:
- 定义与实体类A中json类型中数据对应的实体类B;
- 在实体类A中定义json类型数据为实体类B,且实体类A中json类型字段上面加上注解
@TableField(typeHandler = JacksonTypeHandler.class)
四.插件功能
4.1 分页插件
- 在配置类中注册MyBatisPlus的核心插件,同时添加分页插件;
- 直接使用:
interceptor.addInnerInterceptor(),添加使用的插件等; - 直接使用分页的API
4.2 通用分页实体
直接将分页功能,存放到一个分页公共类中,如果想要进行分页操作,直接调用分页类中的函数即可。
五、MyBatis-Plus 面试问答汇总
| 序号 | 分类 | 面试问题 | 参考答案 |
|---|---|---|---|
| 1 | 快速入门 | 为什么使用MyBatis-Plus?它解决了什么问题? | MyBatis-Plus是MyBatis的增强工具,在MyBatis基础上只做增强不做改变,简化开发效率。解决的问题:- 重复的CRUD代码:单表CRUD操作无需编写Mapper XML和SQL语句 - 通用功能封装:分页、条件构造、代码生成等开箱即用 - 简化配置:自动实现实体类与数据库表的映射(约定大于配置)核心特性:- 内置BaseMapper,提供通用CRUD方法 - 强大的条件构造器Wrapper - 支持 ActiveRecord 模式 - 自动代码生成器 |
| 2 | 快速入门 | MyBatis-Plus中实体类常用的注解有哪些? | 1. @TableName:指定表名@TableName("tb_user")当类名与表名不一致时使用。2. @TableId:指定主键字段@TableId(value="id", type=IdType.AUTO)- value:数据库主键字段名 - type:主键生成策略(AUTO自增、INPUT手动输入、ASSIGN_ID雪花算法)。3. @TableField:指定普通字段 使用场景:- 成员变量名与数据库字段名不一致:@TableField("db_column")- 成员变量以is开头且为布尔值 - 字段名与数据库关键字冲突:@TableField("\order`")- 非数据库字段:@TableField(exist = false)` |
| 3 | 快速入门 | MyBatis-Plus支持哪些主键生成策略? | IdType枚举定义了多种主键策略:- AUTO:数据库ID自增,适用MySQL、PostgreSQL等 - INPUT:用户手动输入 - ASSIGN_ID:雪花算法(默认),分布式环境生成Long类型唯一ID - ASSIGN_UUID:UUID生成String类型唯一ID - NONE:无状态,跟随全局配置。雪花算法优势:全局唯一适合分布式系统,趋势递增利于数据库索引,无需依赖数据库性能高。 |
| 4 | 快速入门 | MyBatis-Plus的常用配置有哪些? | mybatis-plus:type-aliases-package: com.example.domain.pomapper-locations: classpath*:/mapper/**/*.xmlconfiguration:map-underscore-to-camel-case: truecache-enabled: falselog-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: assign_idupdate-strategy: not_nulllogic-delete-field: deleted |
| 5 | 核心功能 | 什么是条件构造器(Wrapper)?有哪些类型? | Wrapper是MyBatis-Plus的核心条件构造器,用于构建复杂的SQL查询条件,避免硬编码。继承结构:Wrapper → AbstractWrapper → QueryWrapper / UpdateWrapper / AbstractLambdaWrapper → LambdaQueryWrapper / LambdaUpdateWrapper。推荐使用LambdaWrapper:通过方法引用获取字段名避免硬编码字符串,编译期检查重构友好,例如wrapper.eq(User::getName, "张三")。 |
| 6 | 核心功能 | LambdaQueryWrapper的常用方法有哪些? | 比较操作:eq等于、ne不等于、gt大于、ge大于等于、lt小于、le小于等于、between区间、notBetween不在区间、like模糊匹配、likeLeft/likeRight、in/notIn、isNull/isNotNull。排序/分组:orderByAsc/orderByDesc排序、groupBy分组、having分组过滤。逻辑连接:and、or、nested嵌套条件。 |
| 7 | 核心功能 | 如何实现自定义SQL + Wrapper条件? | 使用场景:复杂SQL中,WHERE条件由Wrapper构建,其他部分自定义。实现步骤:1. Mapper接口定义:@Select("SELECT * FROM user ${ew.customSqlSegment}") List<User> selectByWrapper(@Param(Constants.WRAPPER) Wrapper<User> wrapper);2. Service层调用:LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getStatus, 1).gt(User::getAge, 18); List<User> users = userMapper.selectByWrapper(wrapper);注意:参数必须使用@Param(Constants.WRAPPER)注解,Constants.WRAPPER值为"ew",SQL中使用${ew.customSqlSegment}拼接WHERE条件。 |
| 8 | 核心功能 | MyBatis-Plus的Service接口有哪些规范? | Service层接口需继承IService,实现类继承ServiceImpl。接口定义:public interface IUserService extends IService<User> {}实现类:@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {}IService提供的方法:save新增、saveOrUpdate新增或修改、remove删除、get查询单个、list查询多个、page分页查询、count统计。注入方式:使用构造函数注入(配合@RequiredArgsConstructor)。 |
| 9 | 扩展功能 | MyBatis-Plus的代码生成器怎么用? | 两种主流方式:1. MybatisX插件(推荐):IDEA插件市场安装MybatisX,右侧菜单Other → Code Generator,配置数据库连接选择表,生成Entity、Mapper、Service、Controller。2. 代码生成器依赖(旧版):使用FastAutoGenerator配置url、username、password,设置全局配置、包配置、策略配置后执行。自动生成的代码包括:Entity实体类、Mapper接口、Mapper XML、Service接口、ServiceImpl实现类、Controller控制器。 |
| 10 | 扩展功能 | 什么是逻辑删除?如何配置? | 逻辑删除:通过标记字段表示删除状态,实际数据不从数据库删除。优势:数据可恢复、保留历史记录、满足合规要求。配置方式:yaml中配置logic-delete-field为deleted,logic-delete-value为1,logic-not-delete-value为0。实体类:@TableLogic private Integer deleted;使用效果:MP会自动在查询时加上WHERE deleted=0,删除时执行UPDATE SET deleted=1。 |
| 11 | 扩展功能 | 如何实现枚举类型与数据库字段的映射? | 使用场景:实体类中状态字段为枚举(如StatusEnum),数据库中存储整数或字符串。实现步骤:1. 定义枚举实现IEnum接口,重写getValue方法返回数据库存储值。2. 配置枚举处理器:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler。3. 实体类直接使用枚举类型:private StatusEnum status;数据库中存储0或1,实体类自动映射为枚举对象。 |
| 12 | 扩展功能 | JSON字段如何映射为Java对象? | 使用场景:数据库JSON类型字段自动转换为Java对象。实现步骤:1. 定义JSON对应的实体类UserInfo(含nickname、age、tags等字段)。2. 在大实体类中使用@TableField(typeHandler = JacksonTypeHandler.class)标注JSON字段。依赖:需添加Jackson依赖。效果:数据库JSON字段自动序列化/反序列化为UserInfo对象。 |
| 13 | 插件功能 | 如何实现分页查询? | 实现步骤:1. 注册分页插件:配置类中创建MybatisPlusInterceptor,添加PaginationInnerInterceptor并指定数据库类型(如DbType.MYSQL)。2. 使用分页查询:创建Page对象传入pageNum和pageSize,构建LambdaQueryWrapper条件,调用mapper.selectPage(page, wrapper)。3. 获取结果:result.getRecords()获取当前页数据,result.getTotal()获取总记录数,result.getPages()获取总页数。 |
| 14 | 插件功能 | 什么是通用分页实体?有什么好处? | 通用分页实体:封装分页请求参数和响应结果的公共类。请求参数类PageReq:包含pageNum(默认1)和pageSize(默认10)。响应结果类PageResp:包含total总记录数、pages总页数、list当前页数据。好处:代码复用无需重复定义分页属性,统一规范前后端接口标准一致,易于维护修改分页逻辑只改一处,方便扩展添加排序筛选等通用参数。 |
| 15 | 插件功能 | MyBatis-Plus支持的插件有哪些? | 核心插件:分页插件PaginationInnerInterceptor自动生成分页SQL,乐观锁插件OptimisticLockerInnerInterceptor防止并发更新丢失,防全表更新/删除BlockAttackInnerInterceptor防止误操作全表数据,多租户插件TenantLineInnerInterceptor自动过滤租户数据,动态表名插件DynamicTableNameInnerInterceptor按时间或条件切换表名。配置示例:创建MybatisPlusInterceptor,依次添加分页插件、乐观锁插件、防全表删除插件并返回。 |
