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站教学视频:
尚硅谷

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...