MyBatis-Plus的入门学习
创始人
2024-06-01 11:45:54
0

MyBatis-Plus入门学习

    • 简介
    • 特性
    • 快速开始
    • MyBatis-Plus的注解详解
      • @Tableld
        • 主键生成策略
          • 1、数据库自动增长 AUTO
          • 2、UUID
          • 3、Redis生成id
          • 4、MP主键自动生成
    • @TableName
      • @TableField
        • 自动填充
    • 测试方法:
      • update
        • 乐观锁
      • select
        • 查所有
        • 根据id查
        • 多个id批量查询
        • 简单条件查询(通过map封装条件)
        • 分页
      • delete
        • 根据id删除
        • 批量删除
        • 简单的条件查询删除
        • 逻辑删除
    • 性能分析
    • Wrapper
      • wrapper条件构造器测试
        • 1、ge、gt、le、lt、isNull、isNotNull
        • 2、eq、ne
        • 3、between、notBetween
        • 4、allEq
        • 5、like、notLike、likeLeft、likeRight
        • 6、in、notIn、inSql、notInSql、exists、notExists
        • 7、or、and
        • 8、嵌套or、嵌套and
        • 9、orderBy、orderByDesc、orderByAsc
        • 10、last
        • 11、set、setSql

简介

MyBatis-Plus是MyBatis的增强工具,在MyBatis的基础上做出增强改变,简化开发,提高效率

mybatis-plus的官网链接

特性

mybatis-plus官网的特性详解

快速开始

第一步:创建一张表

CREATE TABLE user
(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);

第二步:插入对应数据

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

第三步:初始化一个项目工程

在这里插入图片描述

第四步:引入依赖

org.springframework.bootspring-boot-starter-parent2.0+ 版本
org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-testtestcom.baomidoumybatis-plus-boot-starter最新版本com.h2databaseh2runtime

第五步:配置数据库连接信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?
serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

第五步:在SpringBoot项目的启动类中添加**@MapperScan注解**,用于对mapper的扫描

package com.example.mpdemo001;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.example.mpdemo001.mapper")
public class Mpdemo001Application {public static void main(String[] args) {SpringApplication.run(Mpdemo001Application.class, args);}}

第六步:编码

实体类:

package com.example.mpdemo001.bean;import lombok.Data;/*** @Title: User* @Package com.example.mpdemo001.bean* @Author: CXY* @Copyright CXY* @CreateTime: 2023/3/9 12:35*/
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}

mapper接口(MyBatis-Plus不需要在接口中写很多方法,只需要继承 BaseMapper< T>即可,T表示泛型

package com.example.mpdemo001.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mpdemo001.bean.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper {
}

**注意:**在mapper接口上面需要添加一个@Mapper的注解,用于将Mapper接口加入Spring管理

第七步:测试

package com.example.mpdemo001;import com.example.mpdemo001.bean.User;
import com.example.mpdemo001.mapper.UserMapper;
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 Mpdemo001ApplicationTests {@Autowiredprivate UserMapper userMapper;@Testpublic void contextLoads() {List users = userMapper.selectList(null);for (User user:users) {System.out.println(user);}}}

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

测试结果:

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)

可以配置MyBatis-Plus的日志文件,这样可以看到详细的SQL语句实现过程

日志文件的配置如下:

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

MyBatis-Plus的注解详解

@Tableld

IdType:顾名思义即表示主键的类型

主键生成策略

参考资料:分布式系统唯一ID生成方案汇总:https://www.cnblogs.com/haoxinyue/p/5208136.htm

1、数据库自动增长 AUTO

这个是指在建表的时候可以直接设置id自增,这样的话idType即为AUTO;

**缺点:**这样id自增的主键,不利于数据库的分表实现,因为id是按顺序递增的,所以每一次分表的时候都需要知道前一张表的最后一个id的值,再那个基础上进行id加一的操作

2、UUID

每次生成随机唯一的值

**优点:**每次分表的时候不需要知道上一张表的最后一个元素的id值

**缺点:**id无法进行排序

3、Redis生成id

Redis是单线程的,所以也可以用来生成全局唯一的id,可以用Redis的原子操作INCRINCRBY来实现

但是使用这个来进行id生成的时候,需要指定初始值和步长

使用Redis集群可以防止单点故障的问题

单点故障:(英语:single point of failure,缩写SPOF)是指系统中一点失效,就会让整个系统无法运作的部件,换句话说,单点故障即会整体故障。

**优点:**不依赖于数据库,灵活方便,且性能优于数据库,数字id天然排序,对分页或者需要排序的结果很有帮助

**缺点:**如果系统中没有Redis,还需引入新的主键,增加系统的复杂度;需要编码和配置的工作量比较大

4、MP主键自动生成

mp可以通过雪花算法自动生成一个19位数字的id,

@TableName

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类

在这里插入图片描述

@TableField

  • 描述:字段注解(非主键)

自动填充

将数据自动填充进去

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。 我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作

具体实现过程:

在实体类中需要自动进行自动填充的对象上面添加注解:**@TableField(fill=FieldFill.Xxx)**FieldFill的取值:

在这里插入图片描述

然后创建一个类用于实现MetaObjectHandler接口,实现接口中的方法,

注意:需要在这个类上面添加注解,让Spring进行管理

package com.example.mpdemo001.bean;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;import java.util.Date;/*** @Title: MyBatisMeta* @Package com.example.mpdemo001.bean* @Author: CXY* @Copyright CXY* @CreateTime: 2023/3/10 14:02*/
@Component
//这个不能忘,忘了就无法进行了
public class MyBatisMeta implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {//插入时间,第一次创建的时候,需要插入创建时间和更新时间this.setFieldValByName("createTime",new Date(),metaObject);this.setFieldValByName("updateTime",new Date(),metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {//每一次更新数据的时候,插入时间this.setFieldValByName("updateTime",new Date(),metaObject);}
}

测试结果:

在这里插入图片描述

测试方法:

update

@Test
public void testUpdateById(){User user=new User();user.setId(1L);user.setName("战三");int i = userMapper.updateById(user);System.out.println(i);}

结果:
在这里插入图片描述

注意:update生成的是动态sql

乐观锁

针对一种特定问题的解决方案,主要解决的是丢失更新的问题

主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数 据更新

如果不考虑事务的隔离性的话,会产生的问题有:

1、产生读问题:脏读,不可重复读,幻读

2、产生写问题:丢失更新(就是指多个人同时修改同一条数据,最后提交的把之前提交的数据覆盖了

解决丢失更新问题:

悲观锁:串行,只有一个人能在当前情况下进行操作

乐观锁:添加了一个version版本号,当一个人在进行数据操作的时候,提交数据时会比较当前数据版本号和数据库中的版本号是否一直,如果一致则可以进行修改,修改后版本号进行加一的操作。反之不能进行修改。

**乐观锁的使用例子有:**12306抢票系统,当别人把最后一张票抢走之后,另外的人就不能再进行抢票了

乐观锁的使用流程:

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败

当有很多配置的时候,建议新建一个配置类,将所有配置写在配置类中

package com.example.mpdemo001.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Title: MyBatisConfigurations* @Package com.example.mpdemo001.config* @Author: CXY* @Copyright CXY* @CreateTime: 2023/3/10 15:12*/
@Configuration
@MapperScan("com.example.mpdemo001.mapper")
public class MyBatisConfigurations {/*** 乐观锁插件** 新版*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}}

测试成功代码:

@Test
public void testOptimisticLockerFail() {User user = userMapper.selectById(1l);user.setName("死亡法师");user.setAge(10000);int i = userMapper.updateById(user);System.out.println(i);
}

测试成功的结果

在这里插入图片描述

测试失败代码:

@Test
public void testOptimisticLockerFail() {User user = userMapper.selectById(2l);user.setName("哈利波特");user.setAge(300);//模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了versionuser.setVersion(user.getVersion()-1);int i = userMapper.updateById(user);System.out.println(i);
}

结果:

在这里插入图片描述

select

查所有

@Test
public void testSelect(){List users = userMapper.selectList(null);users.forEach(System.out::println);
}

结果
在这里插入图片描述

根据id查

在这里插入图片描述

多个id批量查询

在这里插入图片描述

简单条件查询(通过map封装条件)

在这里插入图片描述

注意:map中的key对应的是数据库中的列名。例如数据库user_id,实体类是userId,这时map的key需 要填写user_id

分页

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

添加插件:

/*** 分页插件(官网最新)*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor1() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;
}

测试分页实现:

@Test
public void testPageSelect(){Page page=new Page<>(1,3);userMapper.selectPage(page, null);page.getRecords().forEach(System.out::println);System.out.println(page.getCurrent());System.out.println(page.getPages());System.out.println(page.getSize());System.out.println(page.getTotal());System.out.println(page.getRecords());}

测试结果

在这里插入图片描述

分页的各个属性:

在这里插入图片描述

delete

根据id删除

@Test
public void testDeletedById(){int i = userMapper.deleteById(4l);System.out.println(i);
}

批量删除

@Test
public void testDeletedByIds(){int i = userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));System.out.println(i);
}

简单的条件查询删除

@Test
public void testDeleteByMap(){HashMap hashMap=new HashMap<>();hashMap.put("name","Tom");hashMap.put("age",28);int i = userMapper.deleteByMap(hashMap);System.out.println(i);
}

逻辑删除

逻辑删除就是指在数据中多加一个标志属性,删除的时候只对该属性进行修改,而不是真正的删除数据,逻辑删除有利于数据的恢复

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍 旧能看到此条数据记录

在这里插入图片描述

在实体类上添加deleted属性

@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;

如果想修改默认属性值,则可在application.properties中进行配置

默认0表示未被删除,1表示被删除

在这里插入图片描述

在配置类中注册bean

@Bean
public ISqlInjector sqlInjector() {return new LogicSqlInjector();
}

高版本不用配置插件了,上述java代码无需编写。

测试删除:

@Test
public void testLogicDelete() {int result = userMapper.deleteById(1L);System.out.println(result);
}

在这里插入图片描述

被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操做

性能分析

在配置类中添加性能优化插件

@Profile({“dev”,“test”})表示对什么环境起作用

dev:开发环境

test:测试环境

prod:生产环境,项目部署好之后的环境 最好不要在这里使用性能优化插件

环境配置在配置文件中设置:

spring.profile.active=xxx(dev,test,prod)

**参数:**maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。

**参数:**format: SQL是否格式化,默认false

Wrapper

在这里插入图片描述

  1. Wrapper : 条件构造抽象类,最顶端父类
  2. AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
  3. QueryWrapper : Entity 对象封装操作类,不是用lambda语法(使用较多
  4. UpdateWrapper : Update 条件封装,用于Entity对象更新操作
  5. AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
  6. LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
  7. LambdaUpdateWrapper : Lambda 更新封装Wrapper

wrapper条件构造器测试

1、ge、gt、le、lt、isNull、isNotNull

@Test
public void testQueryWrapper(){QueryWrapper queryWrapper=new QueryWrapper<>();queryWrapper.isNull("email").ge("age",30).isNotNull("name");//上面代码可以表示多个条件int delete = userMapper.delete(queryWrapper);System.out.println(delete);
}

gt:大于;ge:大于等于;le:小于;lt:小于等于

2、eq、ne

eq表示等于

ne表示不等于

@Test
public void testSelectOne() {QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.eq("name", "Tom");User user = userMapper.selectOne(queryWrapper);System.out.println(user);
}

注意:selectOne方法返回的是一条实体记录,如果返回多条时,会报错

3、between、notBetween

是包含边界值的查询条件

需要注意小值在前,大值在后

@Test
public void testSelectCount() {QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.between("age", 20, 30);Long count = userMapper.selectCount(queryWrapper);System.out.println("==============="+count);
}

4、allEq

多条件均相等

@Test
public void testSelectList() {QueryWrapper queryWrapper = new QueryWrapper<>();Map map = new HashMap<>();map.put("id", 2);map.put("name", "Jack");map.put("age", 20);queryWrapper.allEq(map);List users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}

5、like、notLike、likeLeft、likeRight

like:模糊查询,表示like %内容%

notLike:不包含

likeLeft:表示like %内容

likeRight:表示 like 内容%

@Test
public void testQuerrySelectMaps() {QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.notLike("name","san").likeRight("email","test1");List> maps = userMapper.selectMaps(queryWrapper);maps.forEach(System.out::println);}

6、in、notIn、inSql、notInSql、exists、notExists

in notIn用法如下:

notIn(“age”,{1,2,3})—>age not in (1,2,3)

notIn(“age”, 1, 2, 3)—>age not in (1,2,3)

inSql、notInSql可以实现子查询

@Testpublic void testSelectObjs() {QueryWrapper queryWrapper = new QueryWrapper<>();
//queryWrapper.in("id", 1, 2, 3);queryWrapper.inSql("id", "select id from user where id < 3");List objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表objects.forEach(System.out::println);}
 

7、or、and

不调用or则默认为使用 and 连

8、嵌套or、嵌套and

这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号

 @Testpublic void testUpdate2() {
//修改值User user = new User();user.setAge(99);user.setName("Andy");
//修改条件UpdateWrapper userUpdateWrapper = new UpdateWrapper<>();userUpdateWrapper.like("name", "h").or(i -> i.eq("name", "李白").ne("age", 20));
//        i -> i.eq("name", "李白").ne("age", 20)这一段是lambda表达式int result = userMapper.update(user, userUpdateWrapper);System.out.println(result);}

最后的sql语句为:

UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ?

OR ( name = ? AND age <> ?)

9、orderBy、orderByDesc、orderByAsc

**orderBy:**默认是升序

**orderByDesc:**表示升序排列,该方法可以有多个条件参与排列

**orderByAsc:**表示降序排列,该方法可以有多个条件参与排列

10、last

直接拼接到 sql 的最后

注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

@Test
public void testSelectListLast() {QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.last("limit 1");List users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}

sql语句:SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 limit 1

11、set、setSql

最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段

@Testpublic void testUpdateSet() {
//修改值User user = new User();user.setAge(99);
//修改条件UpdateWrapper userUpdateWrapper = new UpdateWrapper<>();userUpdateWrapper.like("name", "h").set("name", "老李头")//除了可以查询还可以使用set设置修改的字段.setSql(" email = '123@qq.com'");//可以有子查询int result = userMapper.update(user, userUpdateWrapper);}

sql语句:

UPDATE user SET age=?, update_time=?, name=?, email = ‘123@qq.com’ WHERE deleted=0 AND name LIKE ?

b站教学视频:
尚硅谷

相关内容

热门资讯

奇思妙想的作文400字(精选... 奇思妙想的作文400字 篇一标题:梦幻的花园我有一个奇妙的梦想,梦见自己拥有了一个令人惊叹的花园。这...
成长的烦恼四年级作文【优秀5... 成长的烦恼四年级作文 篇一成长的烦恼我是一名四年级的学生,正在经历着成长的烦恼。在成长的道路上,我遇...
爱人的作文【最新3篇】 爱人的作文 篇一爱人的作文我有一个特别重要的人,那就是我的爱人。他是我生命中最亲密的伴侣,也是我最深...
中秋之夜的小学作文500字 中秋之夜的小学作文500字  中秋节已悄悄的离我们近了,近了,此时我的心情又怎能不振奋呢?面临三天的...
有你真好的作文(最新6篇) 有你真好的作文 篇一爱与陪伴人生中,有太多的瞬间让我感受到了你的好。你是我最亲近的人,也是我最信任的...
火烧云小学作文【最新6篇】 火烧云小学作文篇一:火烧云的美丽火烧云是一种非常美丽的自然景观,它们在天空中绽放出绚丽的色彩,给人们...
人间自有温情在作文(精简3篇... 人间自有温情在作文 篇一温情的力量人间自有温情,在日常生活中,我们时常能够感受到这种力量。温情是指人...
小学三年级抗疫情作文(精简5... 小学三年级抗疫情作文 篇一:我们一起抗疫,共克时艰新冠疫情的突然爆发给全世界带来了巨大的挑战,人们的...
有你真好的作文(通用6篇) 有你真好的作文 篇一有你真好每个人的生活中都会有那么一个重要的人,他们的存在让我们的生活变得更加美好...
我上学了(优选3篇) 我上学了 篇一我上学了已经有好几年了,回想起来,这段时光仿佛就在眨眼间过去了。刚开始上学的时候,我还...
有趣的游戏小学作文400字【... 有趣的游戏小学作文400字 篇一标题:童年乐园——躲猫猫游戏在我心中,有一个让我欢乐无比的游戏,那就...
那次玩得真高兴作文(优秀5篇... 那次玩得真高兴作文 篇一那次玩得真高兴上个周末,我和我的朋友们一起去了游乐园玩,那次真是玩得太高兴了...
初一七年级学生作文题目【优选... 初一七年级学生作文题目 篇一我的暑假计划暑假即将来临,我对未来的两个月充满了期待和计划。今年的暑假,...
假如考上了名校,我要回来看看... 假如考上了名校,我要回来看看我的母校小学作文 篇一当我考上了名校,我内心感到无比的兴奋和自豪。这是我...
校园桂花香小学作文【最新3篇... 校园桂花香小学作文 篇一校园桂花香小学作文我所在的学校是一所名为桂花香小学的学校,这个名字来源于学校...
春天小学一年级作文300字(... 春天小学一年级作文300字 篇一春天的花儿春天是一个美丽的季节,大地万物都在春天苏醒,充满了生机和活...
小学中秋节的作文【优选3篇】 小学中秋节的作文 篇一中秋节是中国传统的节日之一,也是我最喜欢的节日。在这一天,我和家人一起庆祝,品...
梦里清江小学作文【优质3篇】 梦里清江小学作文 篇一我爱梦里清江小学梦里清江小学是我上学的地方,它位于美丽的梦里清江边。每当我踏进...
难忘的秋游小学作文450字(... 难忘的秋游小学作文450字 篇一难忘的秋游今天,我们全班去郊游了,这是我度过的一个最难忘的秋天。我们...
可怜的骆驼作文(优质3篇) 可怜的骆驼作文 篇一骆驼是一种生活在沙漠中的动物,它们背上长着一座座驼峰,可以存储大量的水分,帮助它...