Mybatis-Plus极速入门

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

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

支持数据库

  • mysql 、mariadb 、oracle 、db2 、h2 、hsql 、sqlite 、postgresql 、sqlserver 、presto 、Gauss 、Firebird

  • Phoenix 、clickhouse 、Sybase ASE 、 OceanBase 、达梦数据库 、虚谷数据库 、人大金仓数据库 、南大通用数据库 、

框架结构

framework

快速开始

以springboot项目为例

引入mybatisPlus的依赖:
<!-- mybatis-plus -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.4.0</version>
</dependency>

完整依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tp</groupId>
    <artifactId>demo-mp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo-mp</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional> <!-- 标识依赖不会传递 -->
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
配置数据库连接信息
server:
  port: 7269
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useSSL=true&serverTimezone=GMT%2B8
      username: root
      password: tp123456
logging:
  level:
    root: info
    com.tp: debug
创建一个数据库表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for USER
-- ----------------------------
DROP TABLE IF EXISTS `USER`;
CREATE TABLE `USER` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `manager_id` bigint(20) DEFAULT NULL COMMENT '直属上级ID',
  `create_time` datetime DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

SET FOREIGN_KEY_CHECKS = 1;
项目中创建实体User.java
@Data
@TableName("user")
public class User {

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /** 姓名 */
    private String name;

    /** 年龄 */
    private Integer age;

    /** 邮箱 */
    private String email;

    /** 直属上级ID */
    private Integer managerId;

    /** 创建时间 */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") //此注解用来接收字符串类型的参数封装成LocalDateTime类型
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") //此注解将date类型数据转成字符串响应出去
    private LocalDateTime createTime;

    /** 对于数据库中没有的字段,如果我们不处理,在新增或修改的时候会出现错误,mp为我们提供了注解@TableField解决此类问题 */
    @TableField(exist = false) // exist = false标识数据库没有此字段,告诉mp映射时忽略此字段,防止出现错误,默认为true
    List<Integer> roleIds;
}

注意:对于数据库中没有的字段,如果我们不做特殊处理,我们在操作数据库时MP会给我们自动进行映射,但是因为数据库中并没有这个字段,这样就会报错:

org.springframework.jdbc.BadSqlGrammarException: 
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'role_ids' in 'field list'

针对这种情形,我们可以使用MP提供的注解@TableField(exist = false)来表明某个字段不是数据库的字段,其中exist表示是否存在,默认为true

创建Mapper,继承自MP给我提供的BaseMapper
package com.tp.demo.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tp.demo.mp.entity.User;

/**
 * FileName: UserMapper
 * Author:   TP
 * Description: 用户服务Mapper
 */
public interface UserMapper extends BaseMapper<User> {

}

接下来就可以测试啦,实际开发中我们应该写Service,但是这里为了方便,就不写了

常用增删改查

新增:
package com.tp.demo.mp;

import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;

/**
 * FileName: MybatisPlusInsertTests
 * Author:   TP
 * Description:
 */
@SpringBootTest
public class MybatisPlusInsertTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 插入1条数据,mp为我们提供了insert(T entity)方法
     * 该方法插入一条记录,返回影响行数
     * <p>
     * 注意⚠️:MP的默认策略为:如果实体的某个属性为null,那么新增和修改时,会忽略null值字段
     */
    @Test
    void testInsert() {
        User user = new User();
        user.setName("向东");
        user.setAge(58);
        user.setManagerId(2);
        user.setEmail("xb@163.com");
        user.setCreateTime(LocalDateTime.now());
        int rows = userMapper.insert(user);
        System.out.println("影响记录数:" + rows);
        System.out.println("新记录的主键ID:" + user.getId());
    }
}

MP为我们提供了insert(T entity)方法,该方法插入一条记录,返回影响行数,MP的默认策略为:如果实体的某个属性为null,那么新增和修改时,会忽略null值字段,如果你想获取新增的实体的ID,可以直接调用实体的getId方法获取。

查询
package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * FileName: MybatisPlusSelectTests
 * Author:   TP
 * Description: 查询类测试
 */
@SpringBootTest
class MybatisPlusSelectTests {

    @Autowired
    private UserMapper userMapper;

    // ========================= 非条件构造器查询 =========================

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user
     */
    @Test
    void testSelectList() {
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user WHERE id=?
     */
    @Test
    void testSelectById() {
        User user = userMapper.selectById(1);
        System.out.println(user);
    }

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user WHERE id IN ( ? , ? )
     */
    @Test
    void testSelectBatchIds() {
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 3));
        users.forEach(System.out::println);
    }

    /**
     * SELECT id,name,age,email,manager_id,create_time FROM user WHERE manager_id = ? AND name = ?
     */
    @Test
    void testSelectByMap() {
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("name", "向北");
        queryMap.put("manager_id", 2); // 注意map的key为表的字段名,value为要查的字段值
        List<User> users = userMapper.selectByMap(queryMap);
        users.forEach(System.out::println);
    }

    /**
     * Map查询,查询条件值为null的不会被忽略
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE manager_id = ? AND name = ? AND email IS NULL
     * ==> Parameters: 2(Integer), 向北(String)
     */
    @Test
    void testSelectByMap2() {
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("name", "向北");
        queryMap.put("manager_id", 2); // 注意map的key为表的字段名,value为要查的字段值
        queryMap.put("email", null);
        List<User> users = userMapper.selectByMap(queryMap);
        users.forEach(System.out::println);
    }

    // ========================= Wrapper条件构造器查询 =========================

    /**
     * 通过条件构造器查询
     * 名字为"张三",邮箱包含163,并且年龄<40
     * Preparing:  SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name = ? AND email LIKE ? AND age < ?)
     * Parameters: 张三(String), %163%(String), 40(Integer)
     */
    @Test
    void testSelectByWrapper() {
        // MP提供了2种创建QueryWrapper的方法:
        // QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper.eq("name", "张三").like("email", "163").lt("age", 40);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 名字中包含"雨",并且年龄大于等于20且小于等于40,并且email不为空
     * Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
     * Parameters: %雨%(String), 20(Integer), 40(Integer)
     */
    @Test
    void testSelectByWrapper2() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name", "雨").between("age", 20, 40).isNotNull("email");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 名字为王姓或者年龄大于等于25,按照年龄降序排列,年龄相同时按照id升序排列
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? OR age >= ?) ORDER BY age DESC,id ASC
     * ==> Parameters: 王%(String), 25(Integer)
     */
    @Test
    void testSelectByWrapper3() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").or().ge("age", 25).orderByDesc("age").orderByAsc("id");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 名字为王姓,并且(年龄<40或邮箱为空)
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? AND (age < ? OR email IS NOT NULL))
     * ==> Parameters: 王%(String), 40(Integer)
     */
    @Test
    void testSelectByWrapper4() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").and(qw -> qw.lt("age", 40).or().isNotNull("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 字为王姓,或者(年龄<40并且年龄>20并且邮箱不为空)
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? OR (age < ? AND age > ? AND email IS NOT NULL))
     * ==> Parameters: 王%(String), 40(Integer), 20(Integer)
     */
    @Test
    void testSelectByWrapper5() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王").
                or(qw -> qw.lt("age", 40).gt("age", 20).isNotNull("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * (年龄<40或者邮箱不为空)并且姓名为王姓
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE ((age < ? OR email IS NOT NULL) AND name LIKE ?)
     * ==> Parameters: 40(Integer), 王%(String)
     */
    @Test
    void testSelectByWrapper7() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.nested(wp -> wp.lt("age", 40).or().isNotNull("email")).likeRight("name", "王");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 年龄在(31,32,34,45)中的
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (age IN (?,?,?,?))
     * ==> Parameters: 31(Integer), 32(Integer), 34(Integer), 35(Integer)
     */
    @Test
    void testSelectByWrapper8() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("age", Arrays.asList(31, 32, 34, 35));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 返回满足条件的其中一条记录即可
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (age IN (?,?,?,?)) limit 1
     * ==> Parameters: 31(Integer), 32(Integer), 34(Integer), 35(Integer)
     */
    @Test
    void testSelectByWrapper9() {
        // MP提供了2种创建QueryWrapper的方法:
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("age", Arrays.asList(31, 32, 34, 35)).last("limit 1"); // last会无视优化,直接在sql后面拼接传入的语句
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 默认MP会查询实体全部字段,如果我们只想查询指定字段怎么办呢?
     * 查询指定列方式一:指定要查询的字段
     * ==>  Preparing: SELECT id,name,age FROM user WHERE (age < ?)
     * ==> Parameters: 40(Integer)
     */
    @Test
    void testSelectByWrapper10() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "name", "age").lt("age", 40);
        // .select(String... columns)可以放在前面,也可以放在后面,例如下面注释内容
        // queryWrapper.lt("age", 40).select("id", "name", "age");
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 默认MP会查询实体全部字段,如果我们只想查询指定字段怎么办呢?
     * 查询指定列方式二:动态指定要查询的字段,更有利于查询实体类中多个字段,不想像方式一那样一个一个写的情形
     * ==>  Preparing: SELECT id,name,age,manager_id FROM user
     */
    @Test
    void testSelectByWrapper11() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        // 排除create_time和email字段,即不查询create_time和email字段
        queryWrapper.select(User.class, info -> !info.getColumn().equals("create_time") && !info.getColumn().equals("email"));
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * 关于condition
     * 在QueryWrapper的各个查询方法中,一般都会有重载的方法,例如like
     * <p>
     * default Children like(R column, Object val) {
     * return this.like(true, column, val);
     * }
     * <p>
     * Children like(boolean condition, R column, Object val);
     * <p>
     * 其中有个重载的方法,前面需要传一个boolean类型的condition,它的含义是:如果  condition的值为true时,则添加后面的查询条件
     * 例如我们做页面的列表,用户可输入多个查询条件,有值的就添加上查询条件
     */
    @Test
    void testSelectByWrapper12() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        boolean condition = (int) (Math.random() * 100) % 2 == 1;
        System.out.println(condition);
        queryWrapper
                .lt("age", 40)
                .eq(condition, "name", "张三"); //如果condition为false时,不会添加查询条件:and name = "张三"
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }


    /**
     * 我们可以通过一个实体构造一个queryWrapper,MP默认会将实体属性中值不为空的作为查询条件,用and连接生成sql
     * ==>  Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE name=? AND age=?
     * ==> Parameters: 向东(String), 58(Integer)
     */
    @Test
    void testSelectByEntity() {
        User queryUser = new User();
        queryUser.setName("向东");
        queryUser.setAge(58);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(queryUser);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }


    /**
     * 调用queryWrapper.allEq(params)      时,params中的null值会被翻译成:字段名 is null
     * 调用queryWrapper.allEq(params,false)时,params中的null值会被忽略
     */
    @Test
    void testAllEq() {
        // name = "向东" and age is null
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Map<String, Object> params = new HashMap<>();
        params.put("name", "向东");
        params.put("age", null);
        queryWrapper.allEq(params);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);

        // name = "向东"
        QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
        Map<String, Object> params2 = new HashMap<>();
        params2.put("name", "向东");
        params2.put("age", null);
        queryWrapper2.allEq(params2, false);
        List<User> users2 = userMapper.selectList(queryWrapper2);
        users2.forEach(System.out::println);
    }

    /**
     * 查询返回一个map,map的key为数据库中的字段
     * 应用场景1:当我们查询的字段不是所有字段,只是部分字段,如果我们用实体封装,那么实体中中未查询的字段就为空或者默认值,不优雅,这个时候可以用map接收指定查询的字段
     * 应用场景2:当我们需要将数据库中的字段进行转换,或者使用函数时,这时候无法用实体封装查询结果,我们可以选择使用map
     */
    @Test
    void testSelectMaps() {
        // 应用场景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "向东");
        queryWrapper.select("name", "age"); // 只查询部分字段
        List<Map<String, Object>> result = userMapper.selectMaps(queryWrapper);
        result.forEach(System.out::println);

        //应用场景2
        QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2
                .select("avg(age) avg_age", "min(age) min_age", "max(age) max_age", "manager_id") // 计算查询
                .groupBy("manager_id").having("sum(age)<{0}", 50);
        List<Map<String, Object>> result2 = userMapper.selectMaps(queryWrapper2);
        result2.forEach(System.out::println);
    }

    /**
     * 根据Wrapper查询,查询全部记录
     * 注意:只返回结果第一个字段的值
     */
    @Test
    void testSelectObjects() {
        // 应用场景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "name").lt("age", 60);
        List<Object> objects = userMapper.selectObjs(queryWrapper); // 只会返回id,不会返回name  {1,2,3,4,5,6,7,8}
        objects.forEach(System.out::println);

    }

    /**
     * 根据查询条件进行统计总记录数
     */
    @Test
    void testSelectCount() {
        // 应用场景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 60);
        Integer count = userMapper.selectCount(queryWrapper);
        System.out.println(count);

    }

    /**
     * 查询1条结果,结果必须为1条或0条
     * 如果数据库存在多条会报错
     */
    @Test
    void testSelectOne() {
        // 应用场景1
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", 1);
        User user = userMapper.selectOne(queryWrapper);
        System.out.println(user);
    }

    // ========================= Lambda条件构造器查询 =========================

    /**
     * Lambda条件构造器查询能有效防止我们手写数据库字段写错问题
     */
    @Test
    void testSelectLambda1() {
        // 创建Lambda条件构造器的方式有3种:
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> lambdaQueryWrapper1 = Wrappers.lambdaQuery();
        LambdaQueryWrapper lambdaQueryWrapper2 = new QueryWrapper<User>().lambda();
        // User::getNamed代表要查询name这个列,lambdaQueryWrapper的好处是避免手写要查询字段字符串,如果手写的也数据库不一致造成报错问题
        lambdaQueryWrapper.eq(User::getName, "向东").lt(User::getAge, 80);
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        users.forEach(System.out::println);
    }

    @Test
    void testSelectLambda2() {
        // 创建Lambda条件构造器的方式有3种:
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> lambdaQueryWrapper1 = Wrappers.lambdaQuery();
        LambdaQueryWrapper lambdaQueryWrapper2 = new QueryWrapper<User>().lambda();
        // where name like %王% and (age < 80 or email is not null)
        lambdaQueryWrapper.likeRight(User::getName, "王").and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
        List<User> users = userMapper.selectList(lambdaQueryWrapper);
        users.forEach(System.out::println);
    }

    /**
     * LambdaQueryChainWrapper,这个可以再次简化写法,可以直接调用.list() 或者 .one() 返回查询结果
     */
    @Test
    void testSelectLambdaQueryChainWrapper() {
        // 创建Lambda条件构造器的方式有3种:
        LambdaQueryChainWrapper<User> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(userMapper);
        List<User> users = lambdaQueryChainWrapper
                .likeRight(User::getName, "王")
                .and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail))
                .list(); // 可以直接调用.list() 或者 .one() 返回查询结果
        users.forEach(System.out::println);
    }
}
修改
package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * FileName: MybatisPlusUpdateTests
 * Author:   TP
 * Description:
 */
@SpringBootTest
class MybatisPlusUpdateTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * UpdateById
     * 根据主键ID修改实体信息的方法
     * 此更新会忽略实体null值,不会更新实体中属性值为null对应的字段
     */
    @Test
    void testUpdateById() {
        User user = new User();
        user.setId(8);
        user.setEmail("xd@163.com");
        userMapper.updateById(user);
    }

    /**
     * 利用UpdateWrapper更新实体>>>>空参构造器:UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
     * UpdateWrapper的条件会出现在where条件中
     * 实体不为空的信息是要更新的字段
     */
    @Test
    void testUpdateByWrapper() {
        // UpdateWrapper的条件会出现在where条件中
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name", "李艺伟");
        updateWrapper.eq("age", 28);

        // 实体不为空的信息是要更新的字段
        User user = new User();
        user.setEmail("lyw@126.com");
        user.setManagerId(1);

        // 返回影响行数
        int rows = userMapper.update(user, updateWrapper);
        System.out.println("影响行数:" + rows);

    }

    /**
     * 利用UpdateWrapper更新实体>>>>带参构造器:UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
     * 这时候updateWrapper中会将whereUser这个实体中不为空的属性设置为where查询条件
     * 注意:这个时候whereUser实体的查询条件和后续updateWrapper调用查询方法时指定的查询条件会同时生效,如果存在相同查询条件,查询条件会出现2次
     */
    @Test
    void testUpdateByWrapper2() {
        // 通过实体构造查询条件
        User whereUser = new User();
        whereUser.setName("李艺伟");
        whereUser.setAge(28);
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
        // updateWrapper.eq("manager_id", 1); // 可以继续添加查询条件

        // 实体不为空的信息是要更新的字段
        User user = new User();
        user.setEmail("lyw@qq.com");
        user.setManagerId(2);

        // 返回影响行数
        int rows = userMapper.update(user, updateWrapper);
        System.out.println("影响行数:" + rows);

    }

    /**
     * 利用UpdateWrapper更新实体>>>>带参构造器:UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
     * 如果我们更新的字段很少,例如实体中我们只更新1、2个字段,如果像上面的更新还需要创建一个对象,这样有点不优雅
     * 又或者我们的需求是想把某个字段设置为null
     * 这时候我们可以使用UpdateWrapper的set方法,指定设置数据库表中具体哪个字段设置为什么值
     */
    @Test
    void testUpdateByWrapper3() {
        // 通过实体构造查询条件
        User whereUser = new User();
        whereUser.setName("李艺伟");
        whereUser.setAge(28);
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
        updateWrapper.set("age", 29).set("email", null);

        // 返回影响行数
        int rows = userMapper.update(null, updateWrapper);
        System.out.println("影响行数:" + rows);

    }

    /**
     * UpdateWrapper同样支持Lambda形式,用法类似LambdaQueryWrapper
     */
    @Test
    void testUpdateByWrapperLambda() {
        // 构造LambdaUpdateWrapper
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getName, "李艺伟").set(User::getAge, 30).set(User::getEmail, "lyw@163.com");
        // 返回影响行数
        int rows = userMapper.update(null, lambdaUpdateWrapper);
        System.out.println("影响行数:" + rows);
    }

    /**
     * 同理,UpdateWrapper同样支持LambdaChain形式
     * 用法类似LambdaQueryChainWrapper
     * 但其返回值不是影响行数,而是boolean值,标识更新成功或失败
     */
    @Test
    void testUpdateByWrapperLambdaChain() {
        // 构造LambdaUpdateWrapper
        LambdaUpdateChainWrapper<User> chainWrapper = new LambdaUpdateChainWrapper<>(userMapper);
        boolean result = chainWrapper
                .eq(User::getName, "李艺伟") // 设置查询条件
                .set(User::getAge, 31).set(User::getEmail, "lyw@chain.com") //设置要更新的内容
                .update(); //需要调用update(),执行更新
        System.out.println("更新是否成功:" + result);

    }
}
删除
package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * FileName: MybatisPlusDeleteTests
 * Author:   TP
 * Description:
 */
@SpringBootTest
class MybatisPlusDeleteTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 根据ID单个删除
     */
    @Test
    void testDeleteById() {
        int rows = userMapper.deleteById(8);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 根据ID批量删除
     */
    @Test
    void testDeleteBatchIds() {
        int rows = userMapper.deleteBatchIds(Arrays.asList(9, 10));
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 根据Map,map中的元素会作为查询条件且等值and连接
     */
    @Test
    void testDeleteByMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "向东");
        map.put("age", 31);
        map.put("manager_id", null);
        int rows = userMapper.deleteByMap(map);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 根据QueryWrapper条件构造器删除
     */
    @Test
    void testDeleteByWrapper() {
        QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper.eq("name", "向东");
        queryWrapper.eq("age", "31");
        int rows = userMapper.delete(queryWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 根据QueryWrapper条件构造器,通过指定一个实体删除
     */
    @Test
    void testDeleteByWrapper2() {
        User user = new User();
        user.setName("向东");
        user.setAge(31);
        QueryWrapper<User> queryWrapper = Wrappers.query(user);
        int rows = userMapper.delete(queryWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 根据LambdaQueryWrapper条件构造器删除
     */
    @Test
    void testDeleteByLambdaQueryWrapper() {
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
        // DELETE FROM user WHERE (name = ? OR age >= ?)
        lambdaQueryWrapper.eq(User::getName, "向东").or().ge(User::getAge, 60);
        int rows = userMapper.delete(lambdaQueryWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 根据LambdaQueryWrapper条件构造器,通过指定一个实体删除
     */
    @Test
    void testDeleteByLambdaQueryWrapper2() {
        User user = new User();
        user.setName("向东");
        user.setAge(50);
        LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery(user);
        int rows = userMapper.delete(lambdaQueryWrapper);
        System.out.println("影响记录数:" + rows);
    }
}

Mybatis-Plus分页

Mybatis-Plus为我们提供了非常方便的分页插件,用法:

package com.tp.demo.mp.config;

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

/**
 * FileName: MybatisPlusPageConfig
 * Author:   TP
 * Description: 使用mybatisPlus的分页插件
 * 只需要提供一个PaginationInnerInterceptor交给Spring管理即可
 */
@Configuration
public class MybatisPlusPageConfig {

    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor(){
        return new PaginationInnerInterceptor();
    }
}

测试:

package com.tp.demo.mp;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tp.demo.mp.entity.User;
import com.tp.demo.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
import java.util.Map;

/**
 * FileName: MybatisPlusPageTests
 * Author:   TP
 * Description: mybatisPlus分页测试
 */
@SpringBootTest
class MybatisPlusPageTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 分页,结果采用实体封装
     */
    @Test
    void testPage1() {
        Page<User> userPage = new Page<>(1, 10); //当前页为第1页,每页显示10条
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 80);
        Page<User> pageResult = userMapper.selectPage(userPage, queryWrapper);
        System.out.println("总页数:" + pageResult.getPages());
        System.out.println("总记录数:" + pageResult.getTotal());
        List<User> users = pageResult.getRecords();
        users.forEach(System.out::println);
    }

    /**
     * 分页,结果采用Map封装,key为列名或者列别名,value为值
     */
    @Test
    void testPage2() {
        Page<Map<String, Object>> userPage = new Page<>(1, 10); //当前页为第1页,每页显示10条
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lt("age", 80).select("name", "age");
        Page<Map<String, Object>> pageResult = userMapper.selectMapsPage(userPage, queryWrapper);
        System.out.println("总页数:" + pageResult.getPages());
        System.out.println("总记录数:" + pageResult.getTotal());
        List<Map<String, Object>> users = pageResult.getRecords();
        users.forEach(System.out::println);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341