MybatisPlus 蓝鸟魂斗罗
引入MyBatis-Plus不影响Mybatis的使用
快速入门
'''
定义Mapper
自定义的Mapper继承MybatisPlus提供的BaseMapper接口:
public interface UserMapper extends BaseMapper<User>{
}
~~选择性浏览↓ ~~
:SpringBoot3.2 与 MyBatisPlus 踩坑小记一、现象
- 启动测试即报:
Failed to load ApplicationContext Caused by: IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': >java.lang.String二、根因
SpringBoot 3.2 对FactoryBean类型校验变严,而 MyBatis-Plus 早期版本把 >factoryBeanObjectType设成字符串,导致不兼容。三、解决步骤
- 升级 MyBatis-Plus →
3.5.8+(或最新3.5.9)<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.9</version> </dependency>
- 覆盖旧版 mybatis-spring(关键!)
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency>刷新 Maven
IDEA 右上角 ↻ → 重新下载依赖 → 运行测试 → 通过。四、心得
- 报错信息看似吓人,实际是依赖版本不匹配。
- 升级时别只看starter,传递依赖也要一起升。
- 控制台 WARNING(agent、sharing)与业务无关,可忽略或加 JVM 参数隐藏。
升级两坐标,问题全解决;SpringBoot3 与 MP 继续愉快玩耍!
快速入门
1.在IDEA中配置好 Spring项目
包括
- java JDK版本
- Maven 构建
依赖:
- lombok
- Spring web
- mysql driver
新建项目时,注意

软件包的位置就是默认 application 类生成的位置
2.配置好配置文件
pom.xml:手动在小毛驴中添加Mybatis-plus的依赖
引l入MybatisPlus的起步依赖
MyBatisPlus官方提供了starter,其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:
<!--mybatisPlus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
(可能要改其org.mybatis:mybatis-spring:2.1.2 (omitted for conflict with 3.0.3),因测试时遇到Spring和Mybatis-plus版本不和谐的问题)
application.yml:配置mysql
在源文件下
创建 实体,Mapper这两个关键包,相当于jvav实体与mysql中user表的映射
在application类中导入
@MapperScan("com.lantu.mapper") // 显式扫描 Mapper 接口,解决自动配置问题
(或者在UserMapper上加入@Mapper注解)
4.HelloWorld
package com.lantu;
import com.lantu.entity.User;
import com.lantu.mapper.UserMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class UserMapperTest {
@Resource
//@Autowired也可以
private UserMapper userMapper;
@Test
//查(east版)
public void testGetAll(){
//userMapper.insert() 传入一个userMapper对象
//userMapper.selectById
//userMapper.update()
//userMapper.deleteByod()
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);//遍历
或者
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.isTrue(5 == userList.size(), "");
userList.forEach(System.out::println);
}
//控制台输出:
//User(id=1, name=Jone, age=18, email=test1@baomidou.com)
//User(id=2, name=Jack, age=20, email=test2@baomidou.com)
//User(id=3, name=Tom, age=28, email=test3@baomidou.com)
//User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
//User(id=5, name=Billie, age=24, email=test5@baomidou.com)
}
//增
@Test
public void testInsert(){
User user = new User(null,"xxx",18,"xxx@qq.com");
// user.setId();//这样就不用设置值了
userMapper.insert(user);//插入语句通过Mapper加入到mysql中
}
//改(这里功能是错的)
public void testUpdate(){
userMapper.updateById(user);
}
}
注意:
UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件
注解
1.@TableName
当表名字改为 user_info
实体类 还是 User就没有对上
开发过程中确实会出现类似情况
这时候给一个注解(user.java实体类文件下)显示指明
@TableName("user_info)")
public class User implements Serializable{
...
}
2.@TbaleField()
a. 当表中列的名字想区别于(user.java实体类文件下)显示指明时
b. 当代码中想要新增一个属性同时不需要数据库表中有时
c. 当你不想查询某一个字段时
3.@TableId()
主键注解
@TableName("user_info")
public class User implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
//添加在这里a. c.
@TableField(value = "email",select = false)
private String mail;
//假设这里还有一个数据库表里没有的状态(也不需要有)
//b.
@TableField(exist = false)
private String status;
}
- 也就是说这前两个注解都可以用来指定映射关系(括号内必须为数据库的内容)
对于注解3.@TableId
如果出现
1.
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
则说明数据库本身 id 字段没有设置 auto
解决方法:
ALTER TABLE user MODIFY id BIGINT(PRIMARY KEY)AUTO_INCREMENT;
(如果id字段已经是主键则去掉括号的内容)describe user;
则有
![image]>(https://img2024.cnblogs.com/blog/3725840/202511/3725840->20251129130027630-19408117.png)
Navicat中设置主键自动递增初始值时,保存自动回弹到默认值的问题
其自身的bug,其实已经生效,不过要设置正确的数字才生效
4.@TableLogic
表字段逻辑处理注解(逻辑删除)
适合在数据量小时使用,情景:用户把购物消费记录删除,真的删除了吗?
在后续查询操作时可以降低代码工作量
属性| 类型 |描述
value|String|逻辑未删除值
delval|String|逻辑删除值
全局
1.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted #全局逻辑删除的实体字段名,配置后可不用@TableLogic注解
logic-delete-value: 1 #逻辑已删除
logic-not-delete-value: 0 #逻辑未删除
#以上all配置,本质也都是一个映射关系->
2.entity(局部,可能会导致整个项目不统一)
字段一致时使用
@TableLogic
private String deleted;
本质:
(这里事先采用了yml全局设置)
@Test
//查(east版)
public void testGetAll() {
//userMapper.insert() 传入一个userMapper对象
//userMapper.selectById
//userMapper.update()
//userMapper.deleteByod()
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);//遍历
}
@Test
//删除
public void testDelete() {
userMapper.deleteById(9);
}
执行测试 查询代码时本质上是:
(这里 WHERE deleted = 0 就是自动加的)
==> Preparing: SELECT id,name,age,email AS mail,deleted FROM user WHERE deleted=0
执行测试 删除代码时本质上是:
(同理)
==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
这也是mp一种对逻辑删除的一个简化操作
5.@Version
乐观锁注解,当要更新一条记录的时候, 希望这条记录没有被别人更新
区别于 悲观锁, 查询受不受限这一块
是一种 进行操作时 进行锁表, 让其他后来的人无法获取正在进行的数据(但在并发时效率低,后面的人要排队)
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时, 带上这个 version
- 执行更新时, set version = new Version where vision = oldVersion
- 如果 version 不对, 就更新失败
情景:
账户余额 balance = 100 ,同时两个人存钱
获取账户余额: 100
A: set balance = 100 + 20,version=2 where version = 1
B: set balance = 100 + 30,version=2 where version = 1
MP 则是自动回加上这个 version 版本号
使用步骤:
0. 为表新增字段: version,balance
User 实体字段里相应的加 属性
@Version
private Integer version;
private Integer balance;
- config
package com.lantu.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MpConfig {
//拦截器/插件可以在官网中查到,不用去记
//直接就配好了
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
分页查询
MyBatis-Plus 的分页插件 PaginationInnerInterceptor 提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。
提示
于 v3.5.9 起,PaginationInnerInterceptor 已分离出来。如需使用,则需单独引入 mybatis-plus-jsqlparser 依赖 , 具体请查看 MP官网安装 一章。
关键修正:artifactId正确,但版本号必须与MyBatis-Plus主版本一致(如3.5.9
com.baomidou
mybatis-plus-jsqlparser
3.5.9 (这里不写也不行)
于是就有了小插曲
发现这个
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
怎么也导入不了
于是只能
同时清理Maven本地仓库中的相关依赖和IDE的缓存
- mvn clean dependency:purge-local-repository -U
- Invalidate Caches
- Reload Project
- 总结:构思Maven,不如pip一根
- 配置分页拦截器
package com.lantu.config;
...
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
- 查询前设置分页参数
public void testGetAll2(){
Page<User> page = new Page<>(1,5);//page对象
Page<User> userPage = userMapper.selectPage(page, null);
System.out.println("总数: "+userPage.getTotal());
//获得具体明细数据
page.getRecords().forEach(System.out::println);
}
结果
==> Preparing: SELECT id,name,age,email AS mail,deleted,version,balance FROM user WHERE deleted=0 LIMIT ?
条件构造器
当条件语句过于复杂时还是写在 xml 里好
https://baomidou.com/guides/wrapper/
详细指令参见
- 常规使用
public void testSelect() {
// userMapper.selectList(null);//queryWrapper这个封装器,又叫条件构造器
// 等到你自定义(设置查询语句)时就 不是写 null了
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age",20);//大于 20 岁
wrapper.likeRight("name","赵");//查姓赵的
wrapper.orderByDesc("id");// id降序(从大到下
//不建议在任何场景下使用前后模糊,因其效率非常低 (左不用,右用索引)
//这里传入的wrapper就是类似于 写好打包好的 sql查询语句
List<User> users = userMapper.selectList(wrapper);//得到一个结果集
users.forEach(System.out::println);
}
- 推荐使用 LambdaQueryWrapper
public void testSelect() {
//直接避免万一你打错字没有及时发现的情况,使用 User:: 直接一步到位
//eg. "age"万一写成"agr"时
LambdaQueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt(User::getAge,20);//大于 20 岁
wrapper.likeRight(User::getName,"赵");//查姓赵的
wrapper.orderByDesc(User::getId);// id降序(从大到小
//不建议在任何场景下使用前后模糊,因其效率非常低 (左不用,右用索引)
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
代码生成器(new)
这个没必要打在main里面,放test里面即可
1.快速生成
在 CodeGenerator 中的 main 方法中直接添加生成器代码,并进行相关配置,然后直接运行即可生成代码。

a.先加入必要依赖(Maven BOM管理版本[可选])
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
b.配置代码生成器并启动,注意设置生成路径,前缀过滤条件等等
package com.lantu;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.sql.Types;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
// 设置
FastAutoGenerator.create("jdbc:mysql:///b24120526", "root", "ssw")
.globalConfig(builder -> {
builder.author("Ssw") // 设置作者
//
.outputDir("C:\\Users\\ssw\\Desktop\\Computer\\java_learning\\hello-Mybaits-Plus\\src\\main\\java"); // 指定输出目录
})
.dataSourceConfig(builder ->
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)
.packageConfig(builder ->
builder.parent("com.lantu") // 设置父包名
.moduleName("user") // 设置父包 模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "C:\\Users\\ssw\\Desktop\\Computer\\java_learning\\hello-Mybaits-Plus\\src\\main\\resources\\mapper\\user")) // 设置mapperXml生成路径
//上面这个一般是放resources上的,路径就放到下面的 Mapper 再下面的 模块名称的文件夹下
)
.strategyConfig(builder ->
builder.addInclude("xxx_user_info","xxx_dept_info") // 设置需要生成的表名,有三种方式写多种
.addTablePrefix("xxx_") // 设置过滤表前缀
// 之后会生成 实体类, 过滤后就不会有 xxx_
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
c.生成代码后再测试能不能正常使用
//test/java/com/lantu/UserServiceTest.java
package com.lantu;
import com.lantu.user.entity.UserInfo;
import com.lantu.user.service.IUserInfoService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class UserServiceTest {
@Autowired
private IUserInfoService userInfoService;
@Test
public void testQueryUserInfo() {
List<UserInfo> list = userInfoService.list();
list.forEach(System.out::println);
}
}
记得在
src/main/java/com/lantu/HelloMybaitsPlusApplication.java
配置好MapperScanner
package com.lantu;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.lantu.mapper") // 显式扫描 Mapper 接口,解决自动配置问题
@MapperScan("com.lantu.*.mapper")//新增这一行,对应图中User模块下的mapper
public class HelloMybaitsPlusApplication {
public static void main(String[] args) {
SpringApplication.run(HelloMybaitsPlusApplication.class, args);
}
2.交互式生成(推荐使用第一种)
交互式生成在运行之后,会提示您输入相应的内容,等待配置输入完整之后就自动生成相关代码。
字面意思:
请输入作者名称?
liudehua
请输入包名?
com.lantu
请输入表名,多个英文逗号分隔?所有输入 all
xxx_user_info,xxx_dept_info
package com.lantu;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CodeGenerator2 {
public static void main(String[] args) {
FastAutoGenerator.create("urljdbc:mysql:///b24120526", "root", "ssw ")
// 全局配置
.globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")))
// 包配置
.packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))
// 策略配置
.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
.entityBuilder()
.enableLombok()
.addTableFills(
new Column("create_time", FieldFill.INSERT)
)
.build())
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
// 处理 all 情况
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
}

浙公网安备 33010602011771号