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

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);
        }
    }
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值