Mybatis Plus 入门 简单的CRUD 使用详解 条件查询 分页查询 DML操作 MP代码生成器

news2024/11/26 15:35:34

Mybatis Plus入门

MP是

MybatisPlus,简称MP,是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变。MP为简化开发、提高效率而生。

它已经封装好了单表curd方法,我们直接调用这些方法就能实现单表CURD

注意:MP仅仅提供了单表的CURD。

          如果要实现多表联查,就只能自己动手实现了,按Mybatis的方式写

 是dao层的框架

小结

MP使用入门

步骤说明

MP是dao层操作数据库的框架,它的使用步骤和Mybatis类似但要简单的多

MP的使用步骤:

  1. 准备数据库表

  2. 创建一个SpringBoot工程,准备MybatisPlus环境:

    添加依赖:MybatisPlus的依赖坐标、数据库驱动包

    修改配置:配置数据库连接地址

    准备引导类

  3. 使用MybatisPlus

    准备实体类,对应数据库表

    创建一个Mapper接口,继承BaseMapper<实体类>,并添加注解@Mapper

  4. 测试MybatisPlus

1.准备数据库表

连接本机的MySQL,执行《资料/mp_db.sql》,或者执行以下脚本代码:

create database if not exists mp_db character set utf8;
use mp_db;
drop table if exists user;
CREATE TABLE user
(
    id       bigint(20) primary key auto_increment,
    user_name     varchar(32) not null,
    password varchar(32) not null,
    age      int(3)      not null,
    tel      varchar(32) not null,
    sex      char(1)     not null
);
insert into user values (null, 'tom', '123456', 12, '12345678910', '男');
insert into user values (null, 'jack', '123456', 8, '12345678910', '男');
insert into user values (null, 'jerry', '123456', 15, '12345678910', '女');
insert into user values (null, 'rose', '123456', 9, '12345678910', '男');
insert into user values (null, 'snake', '123456', 28, '12345678910', '女');
insert into user values (null, '张益达', '123456', 22, '12345678910', '男');
insert into user values (null, '张大炮', '123456', 16, '12345678910', '男');

2.准备创建一个SpringBoot工程,准备MybatisPlus环境MP项目环境

这里准备了两种方式,在实际开发中都可能会使用。大家习惯哪一种就用哪一种

建议使用手动创建方式

方式一:手动创建工程

使用idea创建一个maven工程,不用选择任何Artifact骨架,设置好工程坐标即可。然后按照如下步骤准备MP的开发环境:

1) 添加依赖

在工程的pom.xml里添加依赖:

<parent>
    <!--SpringBoot父工程坐标-->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.2</version>
</parent>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <!--MybatisPlus的起步依赖-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1</version>
    </dependency>
    <!--MySQL数据库驱动包-->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
    <!--SpringBoot测试起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
</dependencies>

2) 创建配置文件

在工程的resources文件夹里创建配置文件application.yaml,内容如下:

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp_db?useSSL=false
    username: root
    password: root

3) 创建引导类

在工程的java文件夹里创建引导类 com.itheima.MpApplication,代码如下:

@SpringBootApplication
public class MpApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpApplication.class, args);
    }
}

方式二:使用SpringInitializr

1) 创建Project

选择Spring Initializr方式

2) 设置工程信息

3) 选择依赖

注意:SpringBoot版本选择2.x.x;选择依赖时只要勾选MySQL Driver即可

 

4) 添加依赖

上一步中,我们仅仅是选择了一个数据库驱动依赖,还没有MybatisPlus的依赖。所以还需要修改pom.xml,添加MybatisPlus的坐标:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency> 

3.使用MybatisPlus

创建实体类

在工程的java文件夹里创建实体类com.itheima.pojo.User,对应数据库里的user表:

package com.myps.pojo;

public class User {
    private Long id;
    private String userName;
    private String password;
    private Integer age;
    private String tel;
    private String sex;

    //get和set方法,省略了,大家手动添加上即可
    //toString方法,省略了,大家手动添加上即可
}

创建Mapper

com.itheima.mapper包里创建一个接口UserMapper,要求:

  • 接口要继承BaseMapper<实体类>。我们的实体类是User,所以要继承的是BaseMapper<User>

  • 接口上添加注解@Mapper

4.测试MybatisPlus

创建一个单元测试类,测试MP的功能,代码如下:

package com.myps;

import com.myps.mapper.UserMapper;
import com.myps.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
public class Demo01MpTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test1(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
}

 

 

小结

什么是MP:
    是dao层的框架,是对Mybatis的增强,在Mybatis基础上只增强不改变
    MP提供好一整套的单表CURD功能,我们可以直接使用
MP的准备:
    添加MP的依赖坐标,MySQL的驱动包
    配置数据库的地址
MP的使用:
    1. 准备实体类
    2. 创建dao层的接口
        要求继承BaseMapper<实体类>
        要求接口上添加注解@Mapper

MP简单CURD【重点】

简单CURD操作 

CRUD

说明

MP内置了强大的BaseMapper,它已经提供好了单表CURD功能:只要我们的Mapper接口继承了BaseMapper,就可以直接使用整套的单表CURD功能了。

常用的基本curd方法有:

功能方法
新增int insert(T entity)
(根据id) 修改int updateById(T entity)
删除int deleteById(Serializable id)
根据id查询T selectById(Serializable id)
(根据条件) 查询列表List<T> selectList(Wrapper<T> w)
(根据条件) 查询数量Integer selectCount(Wrapper<T> w)
(根据条件) 分页查询IPage selectPage(IPage page, Wrapper<T> w);

查询

 

 分页查询需单独分析:

新增

Ctrl+p

修改

 

删除

小结

根据id查询selectById(id值)
查询列表:  selectList(条件对象)
查询数量:  selectCount(条件对象)


插入数据:  insert(实体类对象)
    方法执行插入时,默认会自动生成id值
    插入数据之后,会自动把id值设置到体类对象的主键属性
修改数据 updateById(id值)
删除数据:  deleteById(id值)

示例代码

import com.myps.mapper.UserMapper;
import com.myps.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

/**
 * Mp提供的CRUD的方法
 * 所有查询的方法:以select开头
 * 所有新增的方法:以insert开头
 * 所有修改的方法:以update开头
 * 所有删除的方法:以delete开头
 */
@SpringBootTest
public class Demo02CrudTest {
    @Autowired
    private UserMapper userMapper;

    /**
     * 查询id为1的用户
     */
    @Test
    public void test1Select() {
        User user = userMapper.selectById(1);
        System.out.println(user);
    }

    /**
     * 查询所有用户 列表
     */
    @Test
    public void test1Selects() {
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 查询数量
     */
    @Test
    public void test1SelectCount() {
        Integer count = userMapper.selectCount(null);
        System.out.println("总数量是:" + count);
    }

    /**
     * 插入数据
     * 插入时MP默认会自动生成id值
     * 插入后MP会自动把id值设置到实体类对象的主键属性上
     */
    @Test
    public void test2Insert() {
        User user = new User();
        user.setUserName("凉介");
        user.setAge(25);
        user.setPassword("123456");
        user.setTel("18698946596");
        user.setSex("男");
        System.out.println("插入之前的user:" + user);

        //执行完成 insert之后,Mp会自动把主键值设置到 id属性上
        int insert = userMapper.insert(user);
        System.out.println("影响的行数 :" + insert);
        System.out.println("插入之后的user:" + user);
    }

    /**
     * 修改数据
     */
    @Test
    public void test3updateById() {
//        User user = new User();
//        user.setUserName("小鑫");
//        user.setId(1655891470097350657L);

        //先查:一般不会new一个对象修改
        User user = userMapper.selectById(1655891470097350657L);
        user.setUserName("李阔");
        //updateById的底层是根据id修改的 update ... set ... where id = xxx
        int i = userMapper.updateById(user);
        System.out.println("影响的行数 :" + i);
    }

    /**
     * 删除数据(根据id删除)
     */
    @Test
    public void test4DeleteById() {
        int i = userMapper.deleteById(1655891470097350657L);
        System.out.println("影响行数:" + i);
    }
}

三、MP使用详解

MP使用详情

  • 能控制MP输出执行日志

  • 能使用Lombok简化JavaBean

  • 能使用@TableField处理字段与属性的映射问题

  • 能使用@TableName处理表名与实体类名的映射问题

  • 能设置MP的主键生成策略

MP日志

开启MP日志打印SQL语句

​​​​​​问题说明

在刚刚所有的CURD操作中,都没有在控制台中打印SQL及其执行过程。如果程序执行出错,没有日志信息会对解决bug造成干扰。

解决方案

修改配置文件application.yaml,添加如下配置:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

再次执行SQL语句,在控制台里就有日志信息了

取消SpringBoot启动日志

问题说明

我们发现,控制台里输出的日志内容特别多。其中大部分内容是SpringBoot启动时的一些日志。这些日志并没有太大的实际作用,我们可以想办法取消掉

解决方案

在resources目录下新建一个文件,名称是logback.xml(必须是这个名称),内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    
</configuration>

 更多关于logback的信息,可以参考:logback.xml常用配置详解 - 简书

 修改后效果:

取消日志中的banner图标

问题说明

如果想要继续精简不必要的日志,我们可以取消输出SpringBoot的banner和MybatisPlus的banner

解决方案

修改配置文件application.yaml,添加如下配置:

spring:
  main:
    banner-mode: off #关闭SpringBoot的banner图标
mybatis-plus:
  global-config:
    banner: false #关闭MybatisPlus的banner图标

 输出SpringBoot的图标

 

 修改后:

Lombok的使用

Lombok介绍

我们在编写JavaBean类的时候,有大量冗余的模板式的代码,

像:get方法、set方法、toString方法、构造方法等等。

这些方法没有什么技术含量,但是在开发中又是必不可少的。

使用Lombok可以帮我们解决这些问题。

Lombok项目是一个java库,它简化了JavaBean的编写

避免了大量冗余的、样板式的代码,不需要再写getter、setter或equals方法,

只要有一个注解即可。

它提供了以下常用注解:

注解

说明

@Getter

生成get方法 加在哪个成员变量上,就给哪个成员变量生成get方法 加在类上,表示给所有成员变量生成get方法

@Setter

生成set方法 加在哪个成员变量上,就给哪个成员变量生成set方法 加在类上,表示给所有成员变量生成set方法

@ToString

加上类上,生成toString方法

@Data

加上类上,生成: 1. 所有成员变量的get和set方法 2. 生成toString方法 3. 生成equals方法 4. 生成hashCode方法

@NoArgsConstructor

加在类上,生成无参构造方法

@AllArgsConstructor

加在类上,生成全参构造方法

Lombok的使用

添加Lombok依赖坐标

修改工程的pom.xml,添加lombok的依赖坐标,如下:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

Lombok使用示例

改造一个我们的User类(后续所有的JavaBean,我们都可以这样写):

  • 通常我们只要添加一个@Data,就相当于有了:get、set、toString、无参构造方法

  • 如果还需要构造方法,

  • 就额外再添加:@NoArgConstructor@AllArgConstructor两个注解

@Data
public class User {
    private Long id;
    private String userName;
    private String password;
    private Integer age;
    private String tel;
    private String sex;
}

小结

Lombok是干什么的:
    简化JavaBean的编写,可以减少JavaBean里的冗余的、模板式的代码
Lombok的用法:
    1. 导入lombok的依赖坐标
    2. 哪个实体类要用Lombok,就在类上添加注解
        @Data:会生成get、set、无参构造、toString、hashCode、equals等等方法
        @NoArgsConstructor:生成无参构造
        @AllArgsConstructor:生成全参构造

MP的实体类注解

在刚刚的入门案例中,我们的User类里并没有特殊的属性、特殊的用法。但是实际开发场景往往比较复杂,MP提供了一些注解,可以供我们处理这些复杂的场景。

  • @TableField用于设置 JavaBean的属性 与 字段的映射关系

  • @TableName用于设置 JavaBean的类名 与 数据库表名 的映射关系

  • `@TableId用于配置主键

@TableField字段映射

@TableField用在JavaBean里的属性上,用于设置属性与数据库字段的映射关系。

这个注解提供了很多参数,我们这里介绍3个常用的:

属性名与字段名不匹配的情况

问题说明

默认情况下,MP会把数据库表的字段名 自动与 JavaBean属性名进行映射,

而映射的规则是:

  • 表的字段名:要求采用下划线式命名,单词之间使用_连接。例如:user_name, order_id

  • JavaBean的属性名:采用小驼峰式命名,首字母小字,后续每个单词首字母大写。例如:userName, orderId

MP在操作数据库时,如果有任何属性找不到对应的字段,就会报错“找不到字段”,如下图示例:

 这时候,我们就必须使用@TableField(value="字段名")

来手动设置属性与字段的映射关系。

解决方案

有实体类如下图。

其中一个属性名称为uname与字段名user_name不匹配。就在属性上加注解@TableField(value="字段名")

 查询用户列表,发现user_name字段的值也查询出来了

2 自定义属性没有对应字段的情况

问题说明:

有时候,我们会在JavaBean中添加一些自定义的属性,而这些属性 是没有任何字段与之对应的。

MP在操作数据库时,如果有任何属性找不到对应的字段,就会报错“找不到字段”。如下图   示例:

这时候我们可以通过添加@TableField(exist=false),告诉MP,这个属性没有对应的字段,MP就不会报错了  

解决方案

 有实体类User如下图。

其中有属性address,数据库里是没有对应的字段的。所以在属性上加注解@TableField(exist=false)

再次执行查询用户列表,发现不报错了  

某字段不需要查询的情况

问题说明

默认情况,MP会查询表里所有字段的值,封装到JavaBean对象中。但是实际开发中,

可能某些字段并不需要查询,这时候我们可以使用@TableField(select=false)

解决方案

有实体类User如下图。

其中的password属性我们不要查询,就在属性上添加注解@TableField(select=false)

 再次执行查询列表,结果password值都是null

2 @TableName表名映射

问题说明

 我们在使用MP查询的过程中,并没有指定要查询的表。

例如:我们调用了UserMapperselectList方法

MP就从user表里查询数据了。那么MP是怎么知道要查询哪张表呢?

实际上MP默认会把实体类名与表名做映射,不过是按照下划线命名与大驼峰命名的映射方式:

  • 表名称:以下划线式命名。例如:tb_order, tb_order_detail

  • 实体类名称:以大驼峰式命名。例如:TbOrderTbOrderDetail

所以我们调用UserMapper时继承的BaseMapper<User>

MP就知道要查询 实体类User对应的表user

但是,如果实体类名与表名不匹配的时候,MP就会报错 “表不存在”。

例如实体类名为AppUser,表名为user,报错如下图示例:

 解决方案

有实体类AppUser如下图。在类上添加注解@TableName("表名称")

 再次查询用户列表,可以正常查询

3 @TableId主键策略

说明

当我们调用了Mapper的insert方法,插入数据时,并不需要指定id值,

MP会按照既定的主键生成策略帮我们生成好主键值。

        

MP提供好了多种主键生成策略供我们使用:

  • IdType.AUTO数据库主键自增

  • IdType.INPUT:MP不生成主键值,在插入数据时由我们提供一个主键值

  • IdType.ASSIGN_ID:由MP使用雪花算法生成一个id值,可兼容数值型(long类型)和字符串

  • IdType.ASSIGN_UUID:由MP使用UUID算法生成一个id值

使用方法:修改实体类,在主键对应的属性上添加注解@TableId(type=主键策略)

  • 常用的主键策略IdType.AUTOIdType.ASSIGN_ID

 

小结

1. @TableField注解:用于设置JavaBean属性  和  表里的字段 的对应关系。用于非主键字段上
    如果属性名 和 字段名不同:在属性上加注解@TableField(value="字段名")
    如果某属性 没有对应的字段:在属性上加注解@TableField(exist=false)
    如果某属性 不需要查询它的值:在属性上加注解@TableField(select=false)

2. @TableName注解:用于设置 实体类名 和 数据库表名    的对应关系
    如果实体类名和表名不匹配:在实体类上加注解@TableName("表名称")

3. @TableId注解:用于设置主键字段与属性的映射关系,还可以设置主键的生成策略
    IdType.AUTO数据库自增使用最多,通常情况下单体数据库使用这种
    IdType.ASSIGN_UUID:让MP使用UUID算法生成32长的随机字符串作为id
    IdType.ASSIGN_ID:让MP使用雪花算法生成id值,兼容数值和字符串类型。分布式项目里使用的最多
    IdType.INPUT:由开发人员手动输入主键值。 
        比如 一对一关系的表,一张表自增,一张表手动输入
        例如 ap_user用户帐号信息表, ap_user_info用户个人资料信息表,两张表一对一关系
        可以 ap_user的主键设置为自增AUTO,ap_user_info的主键设置为INPUT手动输入
    
    经验:单体项目,主键通常用AUTO自增分布式项目,主键通常使用ASSIGN_ID雪花算法

四、 MP条件查询【重点】

上午回顾

介绍

条件查询介绍

以前使用Mybatis开发时,遇到动态条件的查询,就需要我们在xml文件里使用各种标签实现SQL语句的动态拼接。而MP把这些封装成了Java的API,我们可以以编程的形式完成SQL语句的构建并实现复杂的多条件查询。

而复杂的多条件查询的关键在于查询条件的构造,为此MP提供了查询条件抽象类:Wrapper,用于封装查询相关的所有条件和其它查询信息。

MP提供的所有条件式的操作,都需要传入这个类型的参数。比如:

  • selectList(Wrapper w):根据条件查询列表

  • selectOne(Wrapper w):根据条件查询一条数据

  • selectCount(Wrapper w):根据条件查询数量

  • selectPage(Page p, Wrapper w):根据条件分页查询

  • ……

Wrapper有两个常用的实现类

  • QueryWrapper:使用时容易写错但使用更灵活

  • LambdaQueryWrapper没有QueryWrapper灵活,但是不易写错使用的更多一些

在实际开发中,有时候会把QueryWrapper转换成LambdaQueryWrapper,两者混合使用。

开发环境准备

为了避免前边代码的干扰,我们准备一个新的工程,步骤略

实体类:User

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userName;
    private String password;
    private Integer age;
    private String tel;
    private String sex;
}

UserMapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

Query

Wrapper的使用

使用入门

能够使用QueryWrapper或者LambdaQueryWrapper实现条件查询

需求

查询年龄大于20岁的用户,只查询用户的id、姓名、年龄,结果按年龄降序排列

示例

设置查询条件

说明

如果要使用QueryWrapper进行条件查询,常用的查询条件方法有:

查询方法说明备注
eq(字段名,值)等于=字段名=值 条件
ne(字段名,值)不等与<>
gt(字段名,值)大于>
ge(字段名,值)大于等于>=
lt(字段名,值)小于<
le(字段名,值)小于等于<=
like(字段名,值)模糊查询 LIKEMybatisPlus会自动加上%
notLike(字段名,值)模糊查询 NOT LIKEMybatisPlus会自动加上%
in(字段名,值集合/数组)IN 查询
notIn(字段名,值集合/数组)NOT IN 查询
isNull(字段名)NULL 值查询
isNotNull(字段名)IS NOT NULL

示例

 

MP分页查询【重点】

掌握MP的分页查询步骤和查询方法

介绍

之前我们要实现分页查询,要么在mybatis中引入pageHelper插件,要么完全手动实现分页查询。

MybatisPlus本身就内置分页插件,不需要再额外导入任何插件,也不需要我们再手动实现了。

分页API

MP的Mapper提供的分页查询方法是:IPage selectPage(IPage page, Wrapper wrapper)

  • 参数page:用于封装分页条件,包括页码和查询数量

  • 参数wrapper:用于封装查询条件,实现条件查询并分页

  • 返回值Page:分页查询的结果

IPage:是一个接口;Page是它的实现类;是分页信息对象

  • 在执行分页查询前,把分页参数封装成Page对象

  • 当执行分页查询后,MP会把查询结果封装到这个Page对象中

  • 常用方法有

方法说明

new Page(pageNumber, pageSize)

创建分页信息Page对象

getCurrent()

获取当前页码

getPages()

获取总页数

getSize()

获取每页几条

getTotal()

获取总数量

getRecords()

获取数据列表

使用步骤

  1. 配置分页插件:创建一个配置类,在配置类中增加MP的分页插件

  2. 实现分页查询:调用Mapper的selectPage方法实现分页查询功能

示例

​​​​​

配置分页插件

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //创建拦截器对象MybatisPlusInterceptor
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件PaginationInnerInterceptor,注意数据库的类型。如果数据库是MySQL,就设置DbType.MYSQL
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

执行分页查询

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
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 Demo06PageTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testPage(){
        //准备查询条件:性别为男的
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getSex, "男");

        //执行分页查询
        Page<User> page = userMapper.selectPage(new Page<>(1, 3), wrapper);

        //得到分页查询结果
        System.out.println("总数量:" + page.getTotal());
        System.out.println("总页数:" + page.getPages());
        System.out.println("每页几条:" + page.getSize());
        System.out.println("当前页码:" + page.getCurrent());
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
    }
}

小结

分页查询:
    1. 先配置分页插件。如果不配置分页插件,将会是查询所有数据
        如果需要使用,直接拷贝粘贴,不建议手写
    2. 再调用分页查询的方法:
        IPage iPage = selectPage(new Page(页码,查询几条), wraper查询条件);
        iPage.getTotal()得到总数量
        iPage.getPages()得到总页数
        iPage.getRecord()得到数据列表

六、MP的DML操作

  • 掌握MP的批量操作
  • 了解MP的逻辑删除
  • 了解MP的乐观锁

MP的批量操作

说明

在实际开发中,通常需要批量操作。例如:批量删除、批量下单等等,如果下图所示

MP提供了批量操作的一些方法,常用的有:

  • deleteBatchIds(Collection idList):根据id集合,批量删除

  • selectBatchIds(Collection idList):根据id集合,批量查询  

示例

import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class Demo07BatchTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testBatchDelete(){
        List<Long> ids = new ArrayList<>();
        ids.add(1L);
        ids.add(2L);
        ids.add(3L);

        userMapper.deleteBatchIds(ids);
    }

    @Test
    public void testBatchSelect(){
        List<Long> ids = new ArrayList<>();
        ids.add(1L);
        ids.add(2L);
        ids.add(3L);

        List<User> users = userMapper.selectBatchIds(ids);
        for (User user : users) {
            System.out.println(user);
        }
    }
}

MP的逻辑删除

说明

如果需要删除一条数据,开发中往往有两种方案可以实现:

  • 物理删除:真正的从数据库中把数据删除掉

  • 逻辑删除:有一个字段用于标识数据是否删除的状态。删除时仅仅是把字段设置为“已删除”状态,数据还在数据库里,并非真正的删除

在实际开发中,逻辑删除使用的更多一些。所以MP也提供了逻辑删除的支持,帮助我们更方便的实现逻辑删除

使用步骤:

  1. 修改数据库表,增加一个字段deleted。字段名称可以随意,仅仅是用于标示数据的状态的

  2. 修改实体类,增加对应的属性,并在属性上添加注解@TableLogic

  3. 修改配置文件application.yaml,声明删除与未删除的字面值

MP逻辑删除的本质是:

  • 当执行删除时,MP实际上执行的是update操作,把状态字段修改为“已删除状态”

  • 当执行查询时,MP会帮我们加上一个条件 状态字段 = 未删除,从而只查询未删除的数据

示例

1 增加状态字段

执行SQL语句:

use mp_db;
alter table user add deleted int default 0;

2 增加属性并加@TableLogic注解

4 测试

MP的乐观锁

说明

随着互联网的发展,特别是国内互联网人数的增加,数据量、并发量也随之增加,并发访问控制的问题也越发凸显,例如:秒杀。如果一条数据被多个线程并发修改数据,就需要对数据加锁,避免其它线程修改这条数据。

MP已经帮我们实现了乐观锁,它的使用步骤非常简单:

  1. 修改数据库表,增加一个字段version。字段名称随意,仅仅是用于记录数据版本号

  2. 修改实体类,增加对应的属性,并在属性上增加注解@Version

  3. 创建配置类,在配置类里设置好乐观锁插件

  4. 修改数据时乐观锁就生效了:但是要先查询数据,再修改数据,乐观锁才会生效;直接修改数据是不生效的

MP的乐观锁实现逻辑也非常简单:

  1. 先查询数据,得到这条数据的version版本号。假如是0

  2. 在修改数据时,带上这个version版本号

    MP执行update时,会加上 set version = version + 1 where version = 这个version版本号

    如果数据库里的数据的version,和带上的这个version不一致,就放弃修改

示例

1 增加版本号字段

                执行以下SQL语句

use mp_db;
alter table user add version int default 0;

2 修改实体类

3 配置乐观锁插件

在配置类(MpConfig)里,并在配置类里配置好乐观锁插件

4 测试

小结

批量操作:
    deleteBatchIds(id集合):根据id集合批量删除
    selectBatchIds(id集合):根据id集合批量查询
逻辑删除:
    1. 修改表,增加一个状态字段,用于标记这个数据是否被删除。字段名称要见名知意,比如叫deleted
    2. 修改实体类,增加对应的属性,并且在属性上加注解@TableLogic
    3. 修改配置文件,告诉MP删除状态的值:已删除是什么值,未删除是什么值
乐观锁:
    如果数据涉及了查询和修改操作,为了避免其它线程对同一数据的干扰,可以加锁
    悲观锁:只要一查询数据,就对这条数据加排它锁,其它线程的任何操作都不能访问。安全性更高,但是性能低
    乐观锁:查询数据时,根据版本号进行控制。如果查询后修改前,数据被其它线程修改了,版本号会变。后边的修改操作会被放弃
    MP的乐观锁的实现步骤
        1. 修改表,增加一个版本号字段
        2. 修改实体类,增加对应的属性,并且要给属性加注解@Version
        3. 配置乐观锁插件
        4. 使用:先查询再更新,乐观锁会自动生效。

七、MP代码生成器【了解】

说明

MP可以帮我们有效的减轻开发中的工作量:所有的单表CURD完全不用自己编写了,直接使用MP就行。但是这还不够,因为我们还需要编写实体类、Mapper接口、Service、Controller代码。

为了更进一步的减轻我们的工作量,MP提供了代码生成器,可以根据数据库表,直接生成好实体类、Mapper接口,甚至Service和Controller。

示例

1 新建一个project

创建一个新的project:

  • pom.xml内容如下,注意:必须有MybatisPlus相关的起步依赖

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
    </parent>
    <dependencies>
        <!--MybatisPlus的起步依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <!-- MySQL驱动包 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
    </dependencies>

创建引导类:略

2 编写代码生成器

        1 添加依赖

<!-- MP生成器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
<!-- MP生成代码需要的模板引擎 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

2 编写生成器代码

精简版

精简版

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;

public class MpCodeGenerator {
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mp_db?useUnicode=true&useSSL=false&characterEncoding=utf8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        //开始执行
        mpg.execute();
    }
}

详细版

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;

public class MpCodeGenerator {
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        //  代码文件生成到哪:当前工程的src/main/java里
        gc.setOutputDir("src/main/java");
        //  代码作者
        gc.setAuthor("liuyp");
        //  生成后是否直接打开文件夹
        gc.setOpen(false);
        //  是否覆盖原代码文件
        gc.setFileOverride(true);
        //  主键生成策略
        gc.setIdType(IdType.AUTO);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mp_db?useUnicode=true&useSSL=false&characterEncoding=utf8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //  设置父包名。与代码所在文件夹不冲突
        pc.setParent("com.itheima");
        //  设置实体类所在包名。父包名+此包名,即是实体类所在位置
        pc.setEntity("pojo");
        //  设置Mapper所在包名。父包名+此包名,即是Mapper接口所在位置
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //  实体类是否使用Lombok
        strategy.setEntityLombokModel(true);
        //  是否生成Rest风格的Controller类
        strategy.setRestControllerStyle(true);
        mpg.setStrategy(strategy);
        mpg.execute();
    }
}

简答题

问题一

请简述MybatisPlus与Mybatis之间的关系是什么?

MybatisPlus是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发与提高效率。

问题二

逻辑删除与物理删除的区别是什么?

1.物理删除执行的是delete语句,会将数据从数据库表中直接删除掉。
2.逻辑删除执行的是update语句,会在数据库表中添加一列字段,该字段用来标识数据是否被删除,所以逻辑删除只需要修改该列的值即可完成逻辑删除,如果使用逻辑删除需注意在查询的时候需要把已被删除的数据排除掉

问题三

 MybatisPlus标识实体类对应的表、实体类主键字段与其他字段对应的注解分别是什么?

1. 标识实体类与对应表之间的关系使用@TableName
2. 标识实体类主键使用@TableId
3. 标识实体类其他非主键字段使用@TableField

问题四

 请简述MybatisPlus常见的主键生成策略有哪些并说说该如何修改其策略?

1.AUTO采用数据库ID主键自增策略
2.INPUT手动设置主键值
3.ASSIGN_ID使用雪花算法生成主键ID
4.ASSIGN_UUID使用UUID作为主键ID
5.NONE无状态,该类型为未设置主键类型
设置主键策略有两种方式
方式一:使用注解设置
    @TableId(type = IdType.AUTO)
方式二:使用配置文件的方式,在application.yml添加如下配置
    mybatis-plus:
      global-config:
        db-config:
          id-type: auto       #主键策略,auto自动增长

问题五

 简述MybatisPlus如何构建条件查询?

MybatisPlus支持两种查询方式,分别是QueryWrapper和LambdaQueryWrapper。
QueryWrapper的构建方式如下:
    1.构建查询对象,QueryWrapper<实体类> qw = new QueryWrapper<>();
    2.构建查询条件,如qw.eq(boolean condition, R column, Object val)
    condition若为true则添加条件查询若为false则不添加该条件
    column为查询的字段的名称
    val为查询的值,最终构建的条件为 where column = val
LambdaQueryWrapper的构建方式如下:
    1.构建查询对象,LambdaQueryWrapper<实体类> lqw = new LambdaQueryWrapper<>();
    2.构建查询条件,如lqw.eq(boolean condition, R column, Object val)
    condition若为true则添加条件查询若为false则不添加该条件
    column为查询的字段,编写方式为 实体类::getXxx
    val:为查询值,最终构建的条件为 where column = val
其中eq是等值查询,如果想构建其他条件查询,则可参考官网来构建,官网地址为:
    https://baomidou.com/pages/10c804/

问题六

 聊一聊MybatisPlus内部是如何简化Mybatis对表进行增删改查?

1.MybatisPlus的主旨是通过操作实体对象来操作数据库表,从而减少基础增删改查方法及对应SQL语句的编写;
2.MybatisPlus使用@TableName、@TableField、@TableId建立了实体类与表、实体类属性与表的列以及标识主键字段;
3.编写Mapper接口继承BaseMapper<实体类>,当调用其方法,比如 int insert(T entity); T即为传入的实体类,MybatisPlus会将insert方法翻译成 insert into ,根据实体类与表之间的关系获取到对应的表名,根据实体类的属性与表中列的关系获取表的列名,从传入的实体类对象中获取需要添加的数据,最终可以组装成对应的sql语句,即 insert into tableName (column1,column2,...) values(?,?,...),然后执行SQL语句完成数据库表的新增功能,而且这些操作都是框架内部自动完成;
4.综上所述,对于表基本的增删改查就可以直接通过调用BaseMapper接口中提供的方法进行完成,省略了编写方法与对应的SQL语句,达到简化开发提供效率。

训练目标

 

  1. 能够灵活的建立数据库表与实体类之间的对应关系

  2. 能够使用MybatisPlus实现表的增删改查

  3. 能够通过MybatisPlus官方文档学会使用MybatisPlus的自动填充功能

  4. 能够使用MybatisPlus实现数据库表的分页条件排序列表查询

需求描述

 现有一电商的后台项目需要你参与开发,并将其中的商品模块交予你进行维护。商品表及项目环境都已经在素材中提供,需要按照需求完成对应功能的开发与测试,最终实现的效果图展示如下:

题目一

  为了满足新商品上线,需要提供一个商品表的新增商品功能。

题目二

项目运行过程中,发现商品ID为(66)的商品存在恶意刷单的情况,需要将其进行下架处理。

题目三

双十一为了吸引客户,需要进行打折促销,现将品牌(brand)为华为,分类(category)为手机的商品价格调至200元同时库存调为100个。

题目四

在对商品进行维护的时候,其中创建时间(createTime)与修改时间(updateTime)需要实时更新,现在面临的问题是如果项目中所有的表都包含有这两个字段,每个表维护的时候都需要进行设置,存在重复工作和遗忘的风险。现在需要你根据MybatisPlus官网提供的自动填充功能https://mp.baomidou.com/guide/auto-fill-metainfo.html将上述问题解决掉。

题目五

用户需要进行商品搜索,可以提供按照商品的品牌(brand模糊)、分类(category等值)和价格(price区间)进行分页条件查询,并且按照价格进行升序。

素材及素材说明

  1. 作业素材的目录下有项目的基础环境,需将其部署到IDEA中,并将数据库连接信息修改成自己本地环境

  2. 作业素材的目录下有tb_item.sql文件,将数据库表与对应的数据导入到本地数据库mybatisplus_db中

提示

  1. 创建模型类的时候需要建立模型类与表之间的对应关系,其中isAD需要额外关注下

  2. 使用MybatisPlus完成数据库表的增删改查

  3. 需求中使用到MybatisPlus的自动填充,课程中未讲到,需要借助于官方文档按照步骤实现

  4. 在使用MybatisPlus实现分页查询的时候,需要配置分页插件

  5. 在查询的时候需要将已下架或者删除的商品排除掉

思路分析

  1. 将项目导入到IDEA中并将数据库及对应的表创建好

  2. 根据数据库表创建对应的模型类,在模型类中需要检查模型类与表,属性与列名以及主键的标识

  3. 编写Mapper接口使其继承BaseMapper

  4. 编写的Mapper接口需要被框架扫描到,有两种实现方式

  5. 调用Mapper接口提供的方法完成数据库表的增删改查

    1. 需求一:调用Mapper接口提供的insert方法完成功能

    2. 需求二与需求三:调用Mapper接口提供的update方法完成功能

    3. 需求四:需要使用@TableField(fill = FieldFill.INSERT)与MetaObjectHandler

    4. 需求五:需要配置PaginationInterceptor并调用selectPage方法进行查询

步骤一:根据tb_item表创建对应的模型类


import com.baomidou.mybatisplus.annotation.TableField;

import java.util.Date;

public class Item {
    private Long id; //主键ID
    private String name; //商品名称
    private Long price; //商品价格
    private Integer stock; //商品库存
    private String image;  //商品图片地址
    private String category; //商品分类
    private String brand;  //商品品牌
    private Integer sold; //商品销量
    private Integer commentCount; //商品评论数
    @TableField("isAD")
    /**
     * 此处需要注意:MybatisPlus默认是支持驼峰模式,
     * isAD按照驼峰模式找对应的列名为 is_a_d和数据库表的列不一致
     * 所以@TableField不能省略
     */
    private Boolean isAD; //是否是首页推广广告,true/false
    private Integer status; //商品状态 1-正常,2-下架,3-删除
    private Date createTime; //创建时间
    private Date updateTime;//更新时间

    //setter...getter...toString...省略
}

 

步骤二:创建Mapper接口继承BaseMapper

 


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.Item;

public interface ItemMapper extends BaseMapper<Item> {
}

注意:BaseMapper后面的泛型不能省略

步骤三:让框架能够扫描到Mapper接口

方式一:接口上添加 @Mapper注解,此方式需要在每个Mapper接口上都需要添加注解



import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.Item;
@Mapper
public interface ItemMapper extends BaseMapper<Item> {
}

 

方式二:引导类上添加 @MapperScan,此方式只需要添加一次即可,但是需要将所有的Mapper接口放在被扫描的包中
@SpringBootApplication
@MapperScan("com.itheima.mapper")//指定扫描Mapper接口的包路径
public class MpTaskApp {
    public static void main(String[] args) {
        SpringApplication.run(MpTaskApp.class, args);
    }
}

步骤四:创建测试类,注入ItemMapper


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {
    
    @Autowired
    private ItemMapper itemMapper;
    
}

 需求一:完成商品表的新增功能


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;

@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {

    @Autowired
    private ItemMapper itemMapper;


    @Test
    public void addItem(){
        Item item = new Item();
        item.setName("华为P50");
        item.setPrice(668800L); //设置价格单位是分
        item.setImage("https://img11.360buyimg.com/n1/s450x450_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg");
        item.setCategory("手机");
        item.setBrand("华为");
        item.setCreateTime(new Date());
        item.setUpdateTime(new Date());
        //下面属性都有默认值,可以不用设置
//        item.setStock(1000);
//        item.setSold(0);
//        item.setCommentCount(0);
//        item.setAD(true);
//        item.setStatus(1);
        //最终sql语句:INSERT INTO tb_item ( name, price, image, category, brand, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
        itemMapper.insert(item);//新增
    }
}

 需求二:将id为66的商品进行下架

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;

@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {

    @Autowired
    private ItemMapper itemMapper;
    
    @Test
    public void offShelf(){
        Item item = new Item();
        item.setId(66L);
        item.setStatus(2); //设置状态为2-下架
        item.setUpdateTime(new Date());//更新修改时间
        //最终的SQL语句:UPDATE tb_item SET status=?, update_time=? WHERE id=?
        itemMapper.updateById(item); //根据ID进行修改
    }
}

 需求三:将所有华为手机的价格调至200元,库存调至100个

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;

@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {

    @Autowired
    private ItemMapper itemMapper;

    @Test
    public void editItem(){
        UpdateWrapper<Item> updateWrapper = new UpdateWrapper<>();
        //设置修改条件
        updateWrapper.eq("brand","华为");
        updateWrapper.eq("category","手机");
        //设置修改内容
        updateWrapper.set("price",20000);//单位为分,200元需要换算成分
        updateWrapper.set("stock",100); //库存调至100个
        updateWrapper.set("update_time",new Date()); //记录商品修改时间
        //最终的sql语句为:UPDATE tb_item SET price=?,stock=?,update_time=? WHERE (brand = ? AND category = ?)
        itemMapper.update(null,updateWrapper);
    }
}

 需求四:在新增与修改的时候自动填充createTime与updateTime

(1)修改Item模型类,在createTime与updateTime属性上添加@TableField注解用以标识自动填充字段

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;

import java.util.Date;

public class Item {
    private Long id;
    private String name;
    private Long price;
    private Integer stock;
    private String image;
    private String category;
    private String brand;
    private Integer sold;
    private Integer commentCount;
    @TableField("isAD")
    private Boolean isAD;
    private Integer status; 
    //FieldFill.INSERT指定为新增的时候自动填充
    @TableField(fill = FieldFill.INSERT) 
    private Date createTime;
    //FieldFill.INSERT_UPDATE指定为新增和修改的时候自动填充
    @TableField(fill = FieldFill.INSERT_UPDATE) 
    private Date updateTime;
	//setter...getter...toString...省略
}

(2)编写类实现MetaObjectHandler接口,设定填充内容

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component //让Spring来管理该类
public class MyMetaObjectHandler implements MetaObjectHandler{

    /**
     * 在执行新增的时候会自动执行的方法,在该方法中为createTime与updateTime进行设值
     * @param metaObject 元数据
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        /**
         * 新增填充,参数一:元数据,参数二:填充的字段属性名称,参数三:填充值的类型,参数四:填充具体的值
         */
        this.strictInsertFill(metaObject,"createTime",Date.class,new Date());
        /**
         * 修改填充,参数一:元数据,参数二:填充的字段属性名称,参数三:填充值的类型,参数四:填充具体的值
         */
        this.strictUpdateFill(metaObject,"updateTime",Date.class,new Date());
    }

    /**
     * 在执行修改的时候会自动执行的方法,在该方法中为updateTime进行设置
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        /**
         * 修改填充,参数一:元数据,参数二:填充的字段属性名称,参数三:填充值的类型,参数四:填充具体的值
         */
        this.strictUpdateFill(metaObject,"updateTime",Date.class,new Date());
    }
}

(3)再次执行新增或修改的时候,就不需要手动设置createTime与updateTime,操作成功后数据库表中自动填充了createTime与updateTime

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;

@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {

    @Autowired
    private ItemMapper itemMapper;


    @Test
    public void addItem(){
        Item item = new Item();
        item.setName("华为P51");
        item.setPrice(668800L); //设置价格单位是分
        item.setImage("https://img11.360buyimg.com/n1/s450x450_jfs/t1/198503/3/476/106191/61029696Ee5633f37/f059c1e9ffbadf3c.jpg");
        item.setCategory("手机");
        item.setBrand("华为");
        //将设置时间的内容去掉,执行成功后createTime与updateTime依然会有值
        //item.setCreateTime(new Date());
        //item.setUpdateTime(new Date());
        //下面属性都有默认值,可以不用设置
//        item.setStock(1000);
//        item.setSold(0);
//        item.setCommentCount(0);
//        item.setAD(true);
//        item.setStatus(1);
        //最终sql语句:INSERT INTO tb_item ( name, price, image, category, brand, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
        itemMapper.insert(item); //新增
    }

    @Test
    public void offShelf(){
        Item item = new Item();
        item.setId(66L);
        item.setStatus(2); //设置状态为2-下架
        //将设置时间的内容去掉,执行成功后updateTime依然会更新
        //item.setUpdateTime(new Date());//更新修改时间
        //最终的SQL语句:UPDATE tb_item SET status=?, update_time=? WHERE id=?
        itemMapper.updateById(item); //根据ID进行修改
    }
}

(4)如果所有的表中都有createTime和updateTime两个属性,为了避免重复工作,可以将createTime和updateTime抽取到一个POJO类中,然后让其他实体类都继承该POJO,就不需要在每个实体类声明这两个属性,减少重复工作


import java.util.Date;

public class BasePojo {
    private Date createTime;
    private Date updateTime;
	//setter...getter...toString...省略
}


package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;

import java.util.Date;

public class Item extends BasePojo{
    private Long id; //主键ID
    private String name; //商品名称
    private Long price; //商品价格
    private Integer stock; //商品库存
    private String image;  //商品图片地址
    private String category; //商品分类
    private String brand;  //商品品牌
    private Integer sold; //商品销量
    private Integer commentCount; //商品评论数
    @TableField("isAD")
    /**
     * 此处需要注意:MybatisPlus默认是支持驼峰模式,
     * isAD按照驼峰模式找对应的列名为 is_a_d和数据库表的列不一致
     * 所以@TableField不能省略
     */
    private Boolean isAD; //是否是首页推广广告,true/false
    private Integer status; //商品状态 1-正常,2-下架,3-删除
//	  因为继承了BasePojo,所以下面的属性与对应的方法都可以省略不写
//    @TableField(fill = FieldFill.INSERT) //FieldFill.INSERT指定为新增的时候自动填充
//    private Date createTime; //创建时间
//    @TableField(fill = FieldFill.INSERT_UPDATE) //FieldFill.INSERT_UPDATE指定为新增和修改的时候自动填充
//    private Date updateTime;//更新时间
//    public Date getCreateTime() {
//        return createTime;
//    }
//
//    public void setCreateTime(Date createTime) {
//        this.createTime = createTime;
//    }
//
//    public Date getUpdateTime() {
//        return updateTime;
//    }
//
//    public void setUpdateTime(Date updateTime) {
//        this.updateTime = updateTime;
//    }
    //getter...setter...toString...省略
}

需求五:实现商品表的分页条件排序列表查询

(1)参考官方文档配置分页插件https://mp.baomidou.com/guide/page.html

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //因为使用的是MYSQL,所以此处需要修改为DbType.MYSQL
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

(2)进行分页条件排序列表查询

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.domain.Item;
import com.itheima.mapper.ItemMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;

@SpringBootTest(classes = MpTaskApp.class)
public class MpTest {

    @Autowired
    private ItemMapper itemMapper;
    @Test
    public void testFindByCondition(){
        //虚拟搜索条件,这些条件可有可无
        String brandName = "小米";
        String category = "平板";
        Long minPrice = 100000L; //1000元
        Long maxPrice = 1000000L; //10000元
        //分页参数
        Integer page = 1; //当前页码
        Integer pagesize=2; //每页显示记录条数
        //构建查询条件
        QueryWrapper<Item> wrapper = new QueryWrapper<>();
        wrapper.like(brandName!=null &&!"".equals(brandName),"brand",brandName); // brand like %?%
        wrapper.eq(category!=null && !"".equals(category),"category",category); //category =?
        wrapper.ge(minPrice!=null,"price",minPrice);// price >= minPrice
        wrapper.le(maxPrice!=null,"price",maxPrice); //price <= maxPrice
        //需要查询状态为正常的,将下架和删除的商品排除掉
        wrapper.eq("status",1); //status = 1
        //构建按照价格price升序
        wrapper.orderByAsc("price");
        //构建分页对象
        IPage<Item> iPage = new Page<>(page,pagesize);
        iPage = itemMapper.selectPage(iPage,wrapper);
        long total = iPage.getTotal();
        System.out.println("总记录数为:"+total);
        //当前页的列表集合
        List<Item> records = iPage.getRecords();
        for (Item record : records) {
            System.out.println(record);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/513515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一分钟图情论文:《AIGC驱动的智慧图书馆转型:框架、路径与挑战》

一分钟图情论文&#xff1a;《AIGC驱动的智慧图书馆转型&#xff1a;框架、路径与挑战》 AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;是一种全新的生产方式&#xff0c;利用人工智能技术自动生成文本、图片、语音、视频甚至虚拟现实等各种形式的数…

5 创建映射

5 映射 上边章节安装了ik分词器&#xff0c;如果在索引和搜索时去使用ik分词器呢&#xff1f;如何指定其它类型的field&#xff0c;比如日期类型、数 值类型等。 本章节学习各种映射类型及映射维护方法。 5.1 映射维护方法 1、查询所有索引的映射&#xff1a; GET&#xf…

【MCAL_UART】-1.2-图文详解RS232,RS485和MODBUS的关系

目录 1 UART&#xff0c;RS232和RS485通信拓扑 2 什么是RS232 2.1 RS232标准的演变 2.2 RS232标准讲了哪些 2.2.1 RS232通信的电平 2.2.2 RS232通信的带宽 2.2.3 RS232通信距离 2.2.4 RS232通信的机械接口 3 什么是RS485 3.1 RS485标准的演变 3.2 RS485标准讲了哪些…

java运算符和表达式

文章目录 一、Java运算符和表达式二、Java算数运算符实例讲解三、Java关系运算符实例讲解四、Java逻辑运算符实例讲解五、Java位运算符实例讲解六、Java赋值运算符实例讲解七、Java条件运算符实例讲解八、Java instanceof运算符实例讲解九、Java运算符的优先级和结合性总结 一、…

steam/CSGO搬砖绝对是副业中的天花板

这个项目的主要逻辑就是——把Steam上CSGO的装备卖到国内上的平台&#xff0c;网易buff去交易赚一个汇率差。 这玩法有点像新疆出产的棉花占全国产量的85%&#xff0c;当地产量大&#xff0c;价格相对其他不产棉花的地区来说&#xff0c;自然就便宜了&#xff1b; 那么就会有商…

gazebo仿真

常用的仿真器 nvidia 场景非常真实&#xff0c;收费 物理仿真比较好&#xff0c;渲染差一点 为什么用仿真器&#xff0c;因为比较穷 gazebo与ros集成的比较好&#xff0c;有很多插件&#xff0c;机器人开发 刚体仿真器 ode 安装gazebo ros自带 机器人算法开发与验证 打开…

2023年湖北武汉安全员ABC报名条件和报名资料是什么?全国通用?

2023年湖北武汉安全员ABC报名条件和报名资料是什么&#xff1f;全国通用&#xff1f; 一、湖北安全员ABC报名条件要求&#xff1a; 1.安全员A证针对的是企业主要负责人&#xff0c;包括法定代表人、总经理&#xff08;总裁&#xff09;、分管安全生产的副总经理&#xff08;副…

java版本spring cloud 企业电子招投标采购系统源码之首页设计

​ ​功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 全程数字化的采购管理 智能化平台化电子化内外协同 明理满足采购业务全程数字化&#xff0c; 实现供应商管理、采购需求、全网寻源、全网比价、电子招 投标、合同订单执行的…

医疗血氧仪方案产品规格书

血氧仪是一种测量人体血氧饱和度的医疗设备&#xff0c;它通过指夹感应器将光源通过皮肤照射到血液中&#xff0c;测量出血液的血氧饱和度&#xff0c;从而帮助医生判断患者是否有缺氧的情况。下面是一份血氧仪产品规格书&#xff0c;具体内容如下&#xff1a; 产品名称&#x…

select、poll、epoll之间的区别总结[整理]

select&#xff0c;poll&#xff0c;epoll都是IO多路复用的机制。I/O多路复用就通过一种机制&#xff0c;可以监视多个描述符&#xff0c;一旦某个描述符就绪&#xff08;一般是读就绪或者写就绪&#xff09;&#xff0c;能够通知程序进行相应的读写操作。但select&#xff0c;…

gitlab cicd

CICD是指持续集成和部署&#xff0c;一般涵盖以下过程 常规步骤如下&#xff1a; 1、代码开发 2、代码提交(dev分支) 3、 持续集成自动检查和编译 包含&#xff1a;1、sonar初步检查&#xff0c;代码规范 2、自动编译&#xff0c;代码正确性检查 3、单元测试&#xff0c;goo…

2023年,网络安全方面 5 大值得学习的编程语言

Python 到目前为止&#xff0c;Python 在网络安全领域一直处于领先地位。这是一种通用的服务器端脚本语言&#xff08;无需编译&#xff09;&#xff0c;已经被应用到成千上万的安全项目中。你会发现绝大多数安全工具和 PoCs 都是用 Python 编写的&#xff0c;这样做是有充分理…

Introduction to modern Cryptography 现代密码学原理与协议第三章笔记

在第二章中讨论的安全叫信息理论安全或完美安全&#xff0c;因为他们的安全性是基于敌手没有足够的信息来成功地完成攻击&#xff0c;而不管敌手地计算能力。 计算安全比信息理论安全要稍微弱一些&#xff0c;也是大多数现代密码学构造方法的目标。由于完美安全所需密钥的长度过…

前任临终前想要见你最后一面,你会去吗?(feat.安全出口fm 贴心闺蜜)

点击文末“阅读原文”即可参与节目互动 特别感谢 / 深夜谈谈播客网络、阿那亚 后期 / 老段 监制 / 姝琦 运营 / 卷圈&#xff0c;Sand 封面 / 姝琦MidJourney 产品统筹 / bobo 场地支持 / 空岛studio 节目主播&#xff1a;姝琦 / 馋虫 / 薇塔 / 老段 录制时间&#xff1a…

【SpringCloud01】

SpringCloud01 1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 2.服务拆分和远程调用2.1.服务拆分原则2.2.服务拆分示例2.2.1.导入Sql语句2.2.2.导入demo工程 2.3.实现远程调用案例2.3.1.案例需求&#xff1a;2.3.2.注册RestTemplate2.…

C++——函数模板与类模板

0.关注博主有更多知识 C知识合集 目录 1.泛型编程 2.函数模板 2.1函数模板实例化 2.2函数模板参数的匹配原则 3.类模板 4.模板的分离编译 1.泛型编程 实际上泛型编程的难度是比较高的&#xff0c;但我们泛型编程的初学者&#xff0c;当然要从简单的地方开始入手。 我…

重磅!OpenAI最新研究:用GPT-4解释神经元行为,网友:AI套娃?

来源 | 机器之心 这就是 GPT 的「抽象」&#xff0c;和人类的抽象不太一样。 虽然 ChatGPT 似乎让人类正在接近重新创造智慧&#xff0c;但迄今为止&#xff0c;我们从来就没有完全理解智能是什么&#xff0c;不论自然的还是人工的。 认识智慧的原理显然很有必要&#xff0c;如…

K8S 部署 seata

文章目录 创建 Deployment 文件创建 ConfigMap 文件创建 Service 文件运行访问高可用部署踩坑 官方文档 k8s中volumeMounts.subPath的巧妙用法 创建 Deployment 文件 deploymemt.yaml namespace&#xff1a;指定命名空间image&#xff1a;使用 1.5.2 版本的镜像ports&#xf…

加密算法和非对称加密的简单学习

加密算法和非对称加密的简单学习 前言对称加密算法DES特点&#xff1a;为什么不使用&#xff1a; 3DES&#xff08;Triple DES 或者 DESede&#xff09;特点&#xff1a;使用场景&#xff1a;为什么不用&#xff1a; AES&#xff08;Advanced Encryption Standard&#xff09;特…

SoLVES模型的详细使用教程

SoLVES&#xff08;Social Values for Ecosystem Services&#xff09;模型是由美国地质调查局落基山地理科学中心&#xff08;RMGSC&#xff09;和科罗拉多州立大学联合研究开发&#xff0c;主要用于评估生态系统服务的社会价值&#xff0c;能够量化美学、生物多样性、休闲生活…