当前位置: 首页 > news >正文

MyBatisPlus快速入门:简化CRUD操作

一.MyBatisPlus 入门与简介

 Mybatis的入门案例与简介,这个和其他课程不一样,其他课程都是先介绍概念,然后再写入门案例,而对于MyBatisPlus的学习,我们将顺序做了调整,主要的原因MyBatisPlus主要是对MyBatis的简化,所有我们先体会下它的简化在哪,然后在学习是什么,以及它帮我们都做那些事。

步骤1: 创建数据库及表

步骤2:创建springboot工程

步骤3: 勾选配置使用

注意:由于MP并未被收录到idea的系统内置配置,无法直接选择加入,需要手动

在pom.xml中配置添加

步骤4:pom.xml补全依赖

步骤5:添加MP的相关配置信息

步骤6:根据数据库表创建实体类

步骤7:创建Dao接口

步骤8:编写引导类

说明:Dao接口要想被容器扫描到,有两种解决方案

        方案一:在Dao接口上添加@Mapper注解,并且确保Dao在引导类所在包或其子包中,

该方法缺点是需要在每一Dao中添加注解。

        方案二:在引导类上添加@MapperScan注解,其属性为所要扫描的Dao所在包,该方案

的好处是只需要写一次,则指定包下的所有Dao接口都能被扫描到,@Mapper就可以不写。

步骤9:编写测试类

二,标准数据层开发

2.1 标准的crud使用

2.2 分页功能

分页查询使用的方法:

        IPage<T> selectPage(IPage<T> page,wrapper<T> querywrapper)

        IPage:用来构建分页查询条件

        Wrapper:用来构建条件查询的条件,目前我们没有可之间传null.

        IPage:返回值,你会发现构建分页条件和方法的返回值都是IPage.

        IPage是一个接口,我们需要找到它的实现类来构建它,具体的实现类进入到Page

中按住ctrl+h,会找到其有一个实现类为page.

步骤1:调用方法传入参数获取返回值

// 创建Ipage分页对象,设置分页参数,1为当前页码,3为每页显示记录数

IPage<User> page = new Page<>(1.3);

// .执行分页查询

us2erDao.selectPage(page,null)

// 获取分页结果

当前页码值:page.getCurrent()

// 每页显示数

page.getSize()

//一共多少页

page.getPages()

//一共多少条数据

page.getTotal()

// 数据

page.getRecords()

步骤2:设置分页拦截器

        这个拦截器MP已经为我们提供好了,我们只需要将其配置成spring管理的bean对象即可。

@configuration

public class MybatisPlusConfig{

@Bean

public mybatisPlusInterceptor mybatisPlusInterceptor(){

// 创建MybatisPlusInterceptor拦截器对象

MybatisPlusInterceptor mpInterceptor = new MyBatisPludInterceptor():

// 添加分页拦截器

mpInterceptor addInnerInterceptor(new PaginationInnerInterceptor());

retrun mpInterceptor;

}}

步骤3:运行测试程序

如果想查看MP执行的sql哦语句,可以修改application.yml配置文件

mybatis-plus:

        configuration:

                log-impl:org.apache.ibatis.logging.stdout.stdOutImpl # 打印SQL语句到控制台。 

DQL编程控制

3.1.1构建条件查询

1.第一种,QueeryWrapper

@springBootTest

class Mybatisplus02DqlAppplicationTests{

@Test

void testGetAll(){

QueryWrapper qw = new Querywreapper();

qw.lt("age",18);

List<User> userList = userDao.selectList(qw);

System.out.println(userlist);

}

lt:小于(<),最终sql语句为

        select id,name,password ,age,tel from user where(age<?)

2.接着来看第三种:LambdaQueryWrapper

@springBootTest

class Mybatisplus02DqlApplicationTests{

@Test

void testGetAll(){

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();

lqw.lt(User::getAge,10);

List<User> userList = userDao.selectList(lqw);

System.out.println(userList);

gt:大于(>),最终的SQL语句为

        select id,name,password,age,tel from user where(age< ? and age>?)

构建多条件的时候,可以支持链式编程

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();

lqw.lt(User::getAge.10);

List<User> userList = userDao.selectList(lqw);

System.out.println(userList);

需求:查询数据库表中,年龄小于10或大于30 的数据

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();

lqw.lt(User::getAge,10).or().gt(User::getAge,30);

List<User> userList = userDao.selectList(lqw);

3.2查询投影

目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容,我们所说的查询投影即就是不查询所有字段,只查询出指定内容的数据。

select(...)方法用来设置查询字段列,可以设置多个,最终的sql语句为:

select id,name,age from user;

如果使用的不是lambda,就需要手动指定字段

3.2.2聚合查询

        count:总记录数

        max:最大值

        min:最小值

        avg:平均值

        sum:求和

QueryWrapper<User> lqw = new QueryWrapper<User>();

lqw.select("count(*) as count");

// select count(*) as count from user;

List<Map<String,Object>> userList = userDao.selectMaps(lqw);

3.2.3分组查询

需求:分组查询,完成group by 的查询使用

Querywrapper<User> lqw = new QueryWrapper<User>();

lqw.select("count(*) as count,tel");

lqw.groupBy("tel");

List<Map<String,Object>> list = userDao.selectMaps(lqw);

System.out.println(list);

groupBy为分组,最终的sql语句为:

        select count(*) as count,tel from user group by tel;

聚合与分组查询,无法使用lambda表达式来完成

3.3 查询条件

MP的查询条件有很多:

        范围匹配(> , = , between)

        模糊匹配(like)

        空判定(null)

        包含性匹配(in)

        分组(group)

        排序(order)

3.3.1 等值查询

需求:根据用户名和密码查询用户信息

LambdaQueryWrapper<User> lqw  = new LambdaQueryWrapper<user>();

lqw.wq(User::getName,"Jerry").eq(User::getPassword,"jerry");

User loginUser = userDao.selectOne(lqw);

eq:相当于=对应的sql语句为:

        select id,name,password,age,tel from user where(name=? and password=?)

selectList: 查询结果为多个或者单个

selectOne:查询结果为单个

3.3.2范围查询

需求:对年龄进行范围查询,使用lt(),le(),gt(),ge(),between()进行范围查询

LambdaQueryWrapper<User>lqw = new LambdaQueryWrapper<User>();

lqw.between(User::getAge,10,30);

List<User> userList = userDao.selectList(lqw);

gt():大于(>)

ge():大于等于(>=)

lt(): 小于(<)

lte():小于等于(<=)

between():between?and?

3.3.3模糊查询

需求:查询表中name属性的值以3开头的用户信息,使用like进行模糊查询

LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();

lqw.likeLeft(User::getName,"J");

// select id, name,password,age,tel from user where (name like ?)

List<User> userList = userDao.selectList(lqw);

like():前后加百分号,如%J%

likeLeft():前面加百分号,如%J

likeRight():后面加百分号,如J%

3.3.4排序查询

需求:查询所有数据,然后按照id降序

LambdaQueryWrapper<User> lwq = new LambdaQueryWrapper<>();

/*

condition:条件,返回boolean.

当condition为true,进行排序,如果为false,则不排序

isAsc:是否为升序,true为升序,false为降序

columns:需要操作的列

lwq.orderBy(true,true,user::getId);

*/

除了上面演示的这种实现方式,还有其他的排序方法:

orderBy排序

        condition:条件,true则添加排序,false则不添加排序

        isAsc:是否为升序,true升序,false降序

        columns:排序字段,可以有多个

orderByAsc/Desc(单个column):按照字段进行升序/降序

orderByAsc/Desc(多个column):按照多个字段进行升序/降序

3.4映射匹配兼容性

问题一:表字段与编码属性设计不同步

MP给我们提供了一共注解@TableField,使用该注解可以实现模型类属性名和表的列名之间的映射关系。

问题二:彪马中添加了数据库中未定义的属性

当模型中多了一个数据库表不存在的字段,就会导致生成的sql语句中在select的时候查询了数据库不存在的字段,程序就会报错------>通过@TableField的exist属性进行解决。

问题三:采用默认查询开发了更多字段查看权限

问题四:表名与编码开发设计不同步--->@TableName解决

四.DML编码控制

4.1 id生成策略控制

         在这里我们有需要用到MP的一个注解@TableId

4.1.1 ID生成策略对比

NONE: 不设置id生成策略,MP不自动生成,约等于INPUT,所以这两种方式都需要用户手动设置,但是手动设置第一个问题是容易出现相同的ID造成主键冲突,为了保证主键不冲突就需要做很多判定,实现起来比较复杂.

 AUTO:数据库ID自增,这种策略适合在数据库服务器只有1台的情况下使用,不可作为分布式ID使用.

ASSIGN_UUID:可以在分布式的情况下使用,而且能够保证唯一,但是生成的主键是32位的字符串,长度过长占用空间而且还不能排序,查询性能也慢.

ASSIGN_ID:可以在分布式的情况下使用,生成的是Long类型的数字,可以排序性能也高,但是生成的策略和服务器时间有关,如果修改了系统时间就有可能导致出现重复主键(雪花算法).

拓展:

        分布式ID是什么?

        当数据量足够大的时候,一台数据库服务器存储不下,这个时候就需要多台数据库服务器进行存储比如订单表就有可能被存储在不同的服务器上

如果用数据库表的自增主键,因为在两台服务器上所以会出现冲突

这个时候就需要一个全局唯一ID,这个ID就是分布式ID。

雪花算法:

        雪花算法(SnowFlake),是Twitter官方给出的算法实现 是用Scala写的。其生成的结果是一个64bit大小整数,它的结构如下图:

        

1. 1bit,不用,因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所

以最高位固定为0。

2. 41bit-时间戳,用来记录时间戳,毫秒级

3. 10bit-工作机器id,用来记录工作机器id,其中高位5bit是数据中心ID其取值范围0-31,低位5bit是

工作节点ID其取值范围0-31,两个组合起来最多可以容纳1024个节点

4. 序列号占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID

4.2 多记录操作

根据ID批量删除,参数是一个集合,可以存放多发个id值。

需求:根据传入的id集合将数据库表中的数据删除掉。

      

根据ID 批量查询,参数是一个集合,可以存放多个id值。

需求:根据传入的ID集合查询用户信息

4.3 逻辑删除

物理删除:业务数据从数据库中丢弃,执行的是delete操作.

逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中,执行的是update操作.

步骤1:修改数据库表添加deleted列

字段名可以任意,内容也可以自定义,比如0代表正常,值为0正常。

步骤2:实体类添加属性

        (1)添加与数据库表的列对应的一个属性名,名称可以任意,如果和数据表列名对不上,可以使用@TableField进行关系映射,如果一致,则会自动对应.

        (2)标识新增的字段为逻辑删除字段,使用@TableLogic.

        从测试结果来看,逻辑删除最后走的是update操作,会将指定的字段修改成删除状态对应的值。思考逻辑删除,对查询有没有影响呢?

执行查询操作,MP的逻辑删除会将所有的查询都添加一个未被删除的条件,也就是已经被删除的数据是不应该被查询出来的。

在配置文件中添加全局配置,如下:

mybatis-plus:

global-config:

  db-config:

     # 逻辑删除字段名

    logic-delete-field: deleted      # 逻辑删除字面值:未删除为0

    logic-not-delete-value: 0

     # 逻辑删除字面值:删除为1

    logic-delete-value: 1

介绍完逻辑删除,逻辑删除的本质为:

逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段

4.4 乐观锁

4.4.1 概念

在讲解乐观锁之前,我们还是先来分析下问题:业务并发现象带来的问题:秒杀

假如有100个商品或者票在出售,为了能保证每个商品或者票只能被一个人购买,如何保证不会出现超买或者重复卖对于这一类问题,其实有很多的解决方案可以使用第一个最先想到的就是锁,锁在一台服务器中是可以解决的,但是如果在多台服务器下锁就没有办法控制,比如12306有两台服务器在进行卖票,在两台服务器上都添加锁的话,那也有可能会导致在同一时刻有两个线程在进行卖票,还是会出现并发问题。

我们接下来介绍的这种方式是针对于小型企业的解决方案,因为数据库本身的性能就是个瓶颈,如果对其并发量超过2000以上的就需要考虑其他的解决方案了。

简单来说,乐观锁主要解决的问题是当要更新一条记录的时候,希望这条记录没有被别人更新。

4.4.2 实现思路

乐观锁的实现方式:

        数据库表中添加version列,比如默认值给1

        第一个线程要修改数据之前,取出记录时,获取当前数据库中的version=1

        第二个线程要修改数据之前,取出记录时,获取当前数据库中的version=1

        第一个线程执行更新时,set version = newVersion where version = oldVersion

                newVersion = version+1 [2]

                oldVersion = version [1]

        第二个线程执行更新时,set version = newVersion where version = oldVersion

                newVersion = version+1 [2]

                oldVersion = version [1]

        假如这两个线程都来更新数据,第一个和第二个线程都可能先执行

        假如第一个线程先执行更新,会把version改为2,

        第二个线程再更新的时候,set version = 2 where version = 1,此时数据库表的数据

        version已经为2,所以第二个线程会修改失败

        假如第二个线程先执行更新,会把version改为2,

        第一个线程再更新的时候,set version = 2 where version = 1,此时数据库表的数据

        version已经为2,所以第一个线程会修改失败

        不管谁先执行都会确保只能有一个线程更新数据,这就是MP提供的乐观锁的实现原理分析。

4.4.3 实现步骤
步骤1:数据库表添加列

列名可以任意,比如使用version,给列设置默认值为1

步骤2:在模型类中添加对应的属性

根据添加的字段列名,在模型类中添加对应的属性值

步骤3:添加乐观锁的拦截器

@Configuration

public class MpConfig {

   @Bean

   public MybatisPlusInterceptor mpInterceptor() {

       //1.定义Mp拦截器

       MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();        //2.添加乐观锁拦截器

       mpInterceptor.addInnerInterceptor(new

OptimisticLockerInnerInterceptor());

       return mpInterceptor;

  }

}

步骤4:执行更新操作

   @Autowired

   private UserDao userDao;

   @Test

   void testUpdate(){

       User user = new User();

       user.setId(3L);

       user.setName("Jock666");        user.setVersion(1);

       userDao.updateById(user);   }

}

所以要想实现乐观锁,首先第一步应该是拿到表中的version,然后拿version当条件在将version加1更新回到数据库表中,所以我们在查询的时候,需要对其进行查询......

   private UserDao userDao;

   @Test

   void testUpdate(){

       //1.先通过要修改的数据id将当前数据查询出来        User user = userDao.selectById(3L);        //2.将要修改的属性逐一设置进去

       user.setName("Jock888");

       userDao.updateById(user);

  }

}

模拟加锁状况:

   @Autowired

   private UserDao userDao;

   @Test

   void testUpdate(){

      //1.先通过要修改的数据id将当前数据查询出来

       User user = userDao.selectById(3L);     //version=3

       User user2 = userDao.selectById(3L);    //version=3

       user2.setName("Jock aaa");

       userDao.updateById(user2);              //version=>4

       user.setName("Jock bbb");

       userDao.updateById(user);               //verion=3?条件还成立吗?   }

}

http://www.dtcms.com/a/395285.html

相关文章:

  • 网络编程套接字(三)---简单的TCP网络程序
  • 背景建模(基于视频,超炫)项目实战!
  • ios26版本回退到ios18
  • OpenCV直方图比较:原理与四种方法详解
  • OpenCV - 图像金字塔
  • 寄存柜频繁维护还卡顿?杰和IB2-281主板:智能化升级高效省心
  • 海外短剧系统开发:多语言适配与跨地区部署的架构实践
  • JVM内存模型详解:看内存公寓如何分配“房间“
  • 【论文阅读】4D-VLA:时空视觉-语言-动作预训练与跨场景校准
  • 【论文阅读】MDM : HUMAN MOTION DIFFUSION MODEL
  • 【论文阅读】RynnVLA-001:利用人类示范改进机器人操作
  • Leecode hot100 - 105.从前序与中序遍历序列构造二叉树
  • 联邦学习论文分享:Federated Learning with GAN-based Data Synthesis for Non-IID Clients
  • 绕过百度网盘无限制下载
  • 【自记】PyCharm 更换阿里云国内源教程
  • 【Axure原型分享】区间K线图
  • javascript之Es6八股文
  • npm和pnpm命令大全
  • kali下安装beef-xss报错-启动失败-简单详细
  • 政策法规下的LLM安全:合规之路
  • 《第21课——C typedef:从Java的“实名制”到C的“马甲生成器”——类型伪装术与代码整容的艺术》
  • 【每天一个知识点】什么是知识库?
  • 豆包·Seedream 4.0深度测评:4K多模态时代的图像创作革命(图文增强版)
  • [新启航]发动机喷管推进剂输送孔光学 3D 轮廓测量 - 激光频率梳 3D 轮廓技术
  • 深入理解 TCP 协议:三次握手与四次挥手的底层原理
  • PyTorch 神经网络工具箱
  • 机器学习-多因子线性回归
  • 国产化Excel开发组件Spire.XLS教程:Python 写入 Excel 文件,数据写入自动化实用指南
  • 08 - spring security基于jdbc的账号密码
  • 解决SSL证书导致源站IP被泄露的问题