mybatis-plus
概述
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
入门
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>....</version>
</dependency>
mapper接口
extends BaseMapper
就行
public interface UserMapper extends BaseMapper<User> {
// 所有的CRUD操作都已经编写完成了
// 你不需要像以前的配置一大堆文件了!
}
注意点,我们需要在主启动类上去扫描我们的mapper包下的所有接口 @MapperScan("com.kuang.mapper")
测试
@SpringBootTest
class MybatisPlusApplicationTests {
// 继承了BaseMapper,所有的方法都来自己父类
// 我们也可以编写自己的扩展方法!
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
// 参数是一个 Wrapper ,条件构造器,这里我们先不用 null
// 查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
CRUD
插入
// 测试插入
@Test
public void testInsert(){
User user = new User();
user.setName("狂神说Java");
user.setAge(3);
user.setEmail("24736743@qq.com");
int result = userMapper.insert(user); // 帮我们自动生成id
System.out.println(result); // 受影响的行数
System.out.println(user); // 发现,id会自动回填
}
数据库插入的id的默认值为:全局的唯一id
主键生成策略
默认 ID_WORKER 全局唯一id
分布式系统唯一id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html
雪花算法: snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为 毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味 着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯 一!
主键自增
-
实体类字段上 @TableId(type = IdType.AUTO)
-
数据库字段一定要是自增!
其他
public enum IdType {
AUTO(0), // 数据库id自增
NONE(1), // 未设置主键
INPUT(2), // 手动输入
ID_WORKER(3), // 默认的全局唯一id
UUID(4), // 全局唯一id uuid
ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}
更新
// 测试更新
@Test
public void testUpdate(){
User user = new User();
// 通过条件自动拼接动态sql
user.setId(6L);
user.setName("关注公众号:狂神说");
user.setAge(18);
// 注意:updateById 但是参数是一个 对象!
userMapper.updateById(user);
}
自动填充
创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!
方式一:数据库级别(工作中不允许你修改数据库)
1、在表中新增字段 create_time, update_time(设置为自动更新),两个字段默认值都为CURRENT_TIMESTAMP
方式二:代码级别
-
删除数据库的默认值、更新操作!
-
实体类字段属性上需要增加注解
// 字段添加填充内容 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
-
编写处理器处理注解
@Component // 一定不要忘记把处理器加到IOC容器中! public class MyMetaObjectHandler implements MetaObjectHandler { // 插入时的填充策略 @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } // 更新时的填充策略 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime",new Date(),metaObject); } }
乐观锁
乐观锁:总是认为不会出现问题,无论干什么都不加锁!出现问题就更新值测试
悲观锁:总是认为会出现问题,无论干什么都加锁!
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
乐观锁:1、先查询,获得版本号 version = 1
-- A
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
-- B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败!
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
测试
-
给数据库中增加version字段!,可以设置一个默认值1,更新后数据库中version字段自动加1
-
我们实体类加对应的字段
@Version //乐观锁Version注解 private Integer version;
-
注册组件
// 扫描我们的 mapper 文件夹 @MapperScan("com.kuang.mapper") @EnableTransactionManagement @Configuration // 配置类 public class MyBatisPlusConfig { // 注册乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
-
测试
// 测试乐观锁成功! @Test public void testOptimisticLocker(){ // 1、查询用户信息 User user = userMapper.selectById(1L); // 2、修改用户信息 user.setName("kuangshen"); user.setEmail("24736743@qq.com"); // 3、执行更新操作 userMapper.updateById(user); } // 测试乐观锁失败!多线程下 @Test public void testOptimisticLocker2(){ // 线程 1 User user = userMapper.selectById(1L); user.setName("kuangshen111"); user.setEmail("24736743@qq.com"); // 模拟另外一个线程执行了插队操作 User user2 = userMapper.selectById(1L); user2.setName("kuangshen222"); user2.setEmail("24736743@qq.com"); userMapper.updateById(user2); // 通过自旋锁来多次尝试提交! userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值! }
查询
// 测试查询
@Test
public void testSelectById() {
User user = userMapper.selectById(1L);
System.out.println(user);
}
// 测试批量查询!
@Test
public void testSelectByBatchId() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
// 按条件查询之一使用map操作
@Test
public void testSelectByBatchIds() {
HashMap<String, Object> map = new HashMap<>();
// 自定义要查询
map.put("name", "狂神说Java");
map.put("age", 3);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
-
配置分页拦截器
// 扫描我们的 mapper 文件夹 @MapperScan("com.kuang.mapper") @EnableTransactionManagement @Configuration // 配置类 public class MyBatisPlusConfig { // 注册乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } //配置分页插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
-
测试
// 测试分页查询 @Test public Page<User> testPage(){ // 参数一:当前页 // 参数二:页面大小 // 使用了分页插件之后,所有的分页操作也变得简单的! Page<User> page = new Page<>(2,5); userMapper.selectPage(page,null); return page; //打印数据 page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
删除
// 测试根据 id 删除记录
@Test
public void testDeleteById(){
userMapper.deleteById(1240620674645544965L);
}
// 通过id批量删除
@Test
public void testDeleteBatchId(){
userMapper.deleteBatchIds(Arrays.asList(1240620674645544961L,1240620674645544962L));
}
// 通过map删除
@Test
public void testDeleteMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","狂神说Java");
userMapper.deleteByMap(map);
}
逻辑删除
物理删除 :从数据库中直接移除
逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 => deleted = 1
-
数据库增加deleted字段,默认为0(未删除)
-
实体类中增加属性
@TableLogic //逻辑删除 private Integer deleted;
-
配置
-
配置bean
// 逻辑删除组件! @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }
-
配置逻辑删除
mybatis-plus: global-config: db-config: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
-
-
测试
//删除之后数据库记录还在,只是deleted字段值变为1,查询是时候就会过滤被默认删除的值 userMapper.deleteById(1);
性能分析插件
作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间
-
导入插件
//sql执行效率插件 @Bean @Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率 public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); // 设置sql执行的最大时间(毫秒),如果超过了则不执行 performanceInterceptor.setMaxTime(100); // 是否格式化代码 performanceInterceptor.setFormat(true); return performanceInterceptor; }
注意:需在yml或properties中配置环境为dev或者test,这样就不会在正正式环境中启动此插件,以免降低效率
spring: profiles: active: dev
-
测试。sql执行时间超过规定时间就会抛出异常
条件构造器Wrapper
//示例
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("account", user.getAccount()).eq("is_delete", Constants.NOT_DELETE);
代码生成器
public class MyCode {
public static void main(String[] args) {
//代码自动生成器对象
AutoGenerator mpg = new AutoGenerator();
//配置策略
//1.全局配置
GlobalConfig gc = new GlobalConfig();
//项目路径,可以自己看本项目路径,手动写入
String projectPath = System.getProperty("user.id");
gc.setOutputDir(projectPath + "src/main/java");
gc.setAuthor("jpy");//设置作者
gc.setOpen(false);
gc.setFileOverride(false);//是否覆盖原来生成的
gc.setServiceName("%sService");//去除service的I前缀
gc.setServiceImplName("%sServiceImpl");
gc.setMapperName("%sMapper");
gc.setXmlName("%sMapper");
gc.setIdType(IdType.AUTO);//主键生成策略
gc.setDateType(DateType.ONLY_DATE);//日期类型
gc.setSwagger2(true);//swagger文档
mpg.setGlobalConfig(gc);
//2.设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("");
dsc.setDriverName("");
dsc.setUsername("");
dsc.setPassword("");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3.包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("");
pc.setParent("");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setController("controller");
mpg.setPackageInfo(pc);
//4.策略配置
StrategyConfig sc = new StrategyConfig();
sc.setInclude("user");//设置要映射的表名,可变长参数
sc.setNaming(NamingStrategy.underline_to_camel);//表名转驼峰
sc.setColumnNaming(NamingStrategy.underline_to_camel);//列名转驼峰
sc.setEntityLombokModel(true);//自动生成lombok
sc.setLogicDeleteFieldName("deleted");//设置逻辑删除字段
//自动填充
TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
List<TableFill> tableFills = new ArrayList<>();
tableFills.add(createTime);
tableFills.add(updateTime);
sc.setTableFillList(tableFills);
//乐观锁
sc.setVersionFieldName("version");
sc.setRestControllerStyle(true);
sc.setControllerMappingHyphenStyle(true);//url下划线,localhost:8080/hello_id_2
mpg.setStrategy(sc);
mpg.execute();//执行
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY