MyBatisPlus学习笔记(SpringBoot版)

MyBatisPlus学习笔记(SpringBoot版)

      • 一、MyBatis-Plus简介
        • 1、简介
        • 2、特性
        • 3、支持数据库
        • 4、框架结构
        • 5、代码及文档地址
      • 二、入门案例
        • 1、开发环境
        • 2、创建数据库及表
          • 2.1 创建表
          • 2.2 添加数据
        • 3、创建Spring Boot工程
          • 3.1 初始化工程
          • 3.2 引入依赖
          • 3.3 idea中安装lombok插件
        • 4、编写代码
          • 4.1 配置application.yml
          • 4.2 启动类
          • 4.3 添加实体
          • 4.4 添加UserMapper
          • 4.5 测试
          • 4.6 查看日志
      • 三、基本CRUD
        • 1、BaseMapper
        • 2、插入
        • 3、删除
          • 3.1 通过id删除记录
          • 3.2 通过id批量删除记录
          • 3.3 通过map条件删除记录
        • 4、修改
        • 5、查询
          • 5.1 根据id查询用户信息
          • 5.2 根据多个id查询多个用户信息
          • 5.3 通过map条件查询用户信息
          • 5.4 查询所有数据
          • 5.5 自定义mapper查询
        • 6、通用Service
          • 6.1 IService
            • 6.1.1 IService源码:
            • 6.1.2 ServiceImpl源码:
          • 6.2 创建Service接口和实现类
          • 6.3 测试查询记录数
          • 6.4 测试批量插入
      • 四、常用注解
        • 1、@TableName
        • 2、@TableId
        • 3、@TableField
        • 4、@TableLogic
            • 4.1 逻辑删除
            • 4.2 实现逻辑删除
            • 4.3 实体类中添加逻辑删除属性
            • 4.4 测试
      • 五、条件构造器和常用接口
        • 1、wapper介绍
        • 2、QueryWrapper(查询和删除)
            • 2.1、组装查询条件
            • 2.2、组装排序条件
            • 2.3、组装删除条件
            • 2.4、条件的优先级
            • 2.5、组装select子句
            • 2.6、实现子查询
        • 3、UpdateWrapper(修改)
        • 4、condition
        • 5、LambdaQueryWrapper
        • 6、LambdaUpdateWrapper
      • 六、插件
        • 1、分页插件
            • 1.1、添加配置类
            • 1.2、测试
        • 2、xml自定义分页
            • 2.1、UserMapper中定义接口方法
            • 2.2、UserMapper.xml中编写SQL
            • 2.3、测试

一、MyBatis-Plus简介

1、简介

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

愿景
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

在这里插入图片描述

2、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

3、支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。

MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss
,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb,informix,TDengine,redshift

达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库,优炫数据库

4、框架结构

在这里插入图片描述

5、代码及文档地址

官方地址: https://www.baomidou.com/

代码发布地址:
Github: https://github.com/baomidou/mybatis-plus
Gitee: https://gitee.com/baomidou/mybatis-plus

文档发布地址: https://www.baomidou.com/pages/24112f/

二、入门案例

1、开发环境

IDE:idea 2020.3
JDK:JDK8+
构建工具:maven 3.6.3
MySQL版本:MySQL 8.0.28
Spring Boot:2.6.6
MyBatis-Plus:3.2.0

2、创建数据库及表

2.1 创建表
CREATE DATABASE `study_mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `study_mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.2 添加数据
INSERT INTO user (id, name, age, email) VALUES
(001, 'Mp01', 21, 'Mp01@baomidou.com'),
(002, 'Mp02', 20, 'Mp02@baomidou.com'),
(003, 'Mp03', 25, 'Mp03@baomidou.com'),
(004, 'Mp04', 21, 'Mp04@baomidou.com'),
(005, 'Mp05', 24, 'Mp05@baomidou.com');

3、创建Spring Boot工程

3.1 初始化工程

使用 Spring Initializr 快速初始化一个 Spring Boot 工程
在这里插入图片描述
在这里插入图片描述

3.2 引入依赖

完整pom.xml

<?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.6.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tigerhhzz</groupId>
    <artifactId>springboot-mybatisplus-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-mybatisplus-demo</name>
    <description>Demo project for Spring Boot MP</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- 包含spirng Mvc ,tomcat的包包含requestMapping restController 等注解 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!--mysql数据库 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!-- mybatis版本必须与druid版本兼容,否则无法创建DataSource -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>

        <!-- 引入freemarker模板引擎供mp生成代码-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!--mp代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.3.0</version>
        </dependency>
        <!-- hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.3</version>
        </dependency>
        <!-- lombok注解-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 日志打印-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!-- 单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.3 idea中安装lombok插件

在这里插入图片描述

4、编写代码

4.1 配置application.yml
server:
  port: 8082
  servlet:
    context-path: /

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    #  test-mybatis
    url: jdbc:mysql://127.0.0.1:3306/study_mybatis_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 123456
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    serialization:
      write-dates-as-timestamps: false

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: full
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:mapper/**/*Mapper.xml
4.2 启动类
package com.tigerhhzz.springbootmybatisplusdemo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author tigerhhzz
 * @date 2023/4/28 9:16
 */
@Slf4j
@SpringBootApplication
public class SpringbootMybatisplusDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisplusDemoApplication.class, args);
        log.info("SpringbootMybatisplusDemoApplication启动成功~~~~^…^~~~~^…^~~~~~^…^~~~~");
    }
}

4.3 添加实体
package com.tigerhhzz.springbootmybatisplusdemo.domain;

import lombok.Data;

/**
 * @author tigerhhzz
 * @date 2023/5/4 16:09
 */
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;

}

User类编译之后的结果:
在这里插入图片描述

4.4 添加UserMapper

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的 实体类型

package com.tigerhhzz.springbootmybatisplusdemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;

/**
 * @author tigerhhzz
 */
@Repository
public interface UserMapper extends BaseMapper<User> {
}
4.5 测试

IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确 的执行。
为了避免报错,可以在mapper接口上添加 @Repository 注解

    @Autowired
    UserMapper userMapper;


    @Test
    public void testSelectList(){
    //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
4.6 查看日志

在这里插入图片描述

三、基本CRUD

1、BaseMapper

MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,我们可以直接使用,接口如 下:

/*
 * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/*

               :`
                    .:,
                     :::,,.
             ::      `::::::
             ::`    `,:,` .:`
             `:: `::::::::.:`      `:';,`
              ::::,     .:::`   `@++++++++:
               ``        :::`  @+++++++++++#
                         :::, #++++++++++++++`
                 ,:      `::::::;'##++++++++++
                 .@#@;`   ::::::::::::::::::::;
                  #@####@, :::::::::::::::+#;::.
                  @@######+@:::::::::::::.  #@:;
           ,      @@########':::::::::::: .#''':`
           ;##@@@+:##########@::::::::::: @#;.,:.
            #@@@######++++#####'::::::::: .##+,:#`
            @@@@@#####+++++'#####+::::::::` ,`::@#:`
            `@@@@#####++++++'#####+#':::::::::::@.
             @@@@######+++++''#######+##';::::;':,`
              @@@@#####+++++'''#######++++++++++`
               #@@#####++++++''########++++++++'
               `#@######+++++''+########+++++++;
                `@@#####+++++''##########++++++,
                 @@######+++++'##########+++++#`
                @@@@#####+++++############++++;
              ;#@@@@@####++++##############+++,
             @@@@@@@@@@@###@###############++'
           @#@@@@@@@@@@@@###################+:
        `@#@@@@@@@@@@@@@@###################'`
      :@#@@@@@@@@@@@@@@@@@##################,
      ,@@@@@@@@@@@@@@@@@@@@################;
       ,#@@@@@@@@@@@@@@@@@@@##############+`
        .#@@@@@@@@@@@@@@@@@@#############@,
          @@@@@@@@@@@@@@@@@@@###########@,
           :#@@@@@@@@@@@@@@@@##########@,
            `##@@@@@@@@@@@@@@@########+,
              `+@@@@@@@@@@@@@@@#####@:`
                `:@@@@@@@@@@@@@@##@;.
                   `,'@@@@##@@@+;,`
                        ``...``

 _ _     /_ _ _/_. ____  /    _
/ / //_//_//_|/ /_\  /_///_/_\      Talk is cheap. Show me the code.
     _/             /
 */

/**
 * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
 * <p>这个 Mapper 支持 id 泛型</p>
 *
 * @author hubin
 * @since 2016-01-23
 */
public interface BaseMapper<T> extends Mapper<T> {

    /**
     * 插入一条记录
     *
     * @param entity 实体对象
     */
    int insert(T entity);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    int deleteById(Serializable id);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

    /**
     * 删除(根据ID 批量删除)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    /**
     * 根据 ID 修改
     *
     * @param entity 实体对象
     */
    int updateById(@Param(Constants.ENTITY) T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     */
    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}

2、插入

    @Test
    public void testInsert(){
        User user = new User();
        user.setName("Mp06");
        user.setAge(23);
        user.setEmail("Mp06@baomidou.com");

        int result = userMapper.insert(user);
        System.out.println("受影响行数:"+result);

        System.out.println("id自动获取:"+user.getId());
    }

最终执行的结果,所获取的id为1475754982694199298
这是因为MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id
在这里插入图片描述

查看数据库:
在这里插入图片描述

3、删除

3.1 通过id删除记录
    @Test
    public void testDeleteById(){
        //通过id删除用户信息
        //DELETE FROM user WHERE id=?
        int result = userMapper.deleteById(1654046614353891329L);
        System.out.println("受影响行数:"+result);
    }
3.2 通过id批量删除记录
    @Test
    public void testDeleteBatchIds(){
        //通过多个id批量删除
        //DELETE FROM user WHERE id IN ( ? , ? , ? )
        List<Long> idList = Arrays.asList(1L, 2L, 3L);
        int result = userMapper.deleteBatchIds(idList);
        System.out.println("受影响行数:"+result);
    }
3.3 通过map条件删除记录
    @Test
    public void testDeleteByMap(){
        //根据map集合中所设置的条件删除记录
        //DELETE FROM user WHERE name = ? AND age = ?
        Map<String, Object> map = new HashMap<>();
        map.put("age", 23);
        map.put("name", "张三");
        int result = userMapper.deleteByMap(map);
        System.out.println("受影响行数:"+result);
    }

4、修改

    @Test
    public void testUpdateById(){
        User user = new User();
        user.setId(2L);
        user.setAge(32);
        //UPDATE user SET name=?, age=? WHERE id=?
        int result = userMapper.updateById(user);
        System.out.println("受影响行数:"+result);
    }

5、查询

5.1 根据id查询用户信息
@Test
public void testSelectById(){
	//根据id查询用户信息
	//SELECT id,name,age,email FROM user WHERE id=?
	User user = userMapper.selectById(4L);
	System.out.println(user);
}
5.2 根据多个id查询多个用户信息
    @Test
    public void testSelectBatchIds() {
        //根据多个id查询多个用户信息
        //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
        List<Long> idList = Arrays.asList(4L, 5L);
        List<User> list = userMapper.selectBatchIds(idList)
    }
5.3 通过map条件查询用户信息
    @Test
    public void testSelectByMap(){
        //通过map条件查询用户信息
        //SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
        Map<String, Object> map = new HashMap<>();
        map.put("age", 22);
        map.put("name", "Mp02");
        List<User> list = userMapper.selectByMap(map);
        list.forEach(System.out::println);
    }
5.4 查询所有数据
    @Test
    public void testSelectList(){
        //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

通过观察BaseMapper中的方法,大多方法中都有Wrapper类型的形参,此为条件构造器,可针
对于SQL语句设置不同的条件,若没有条件,则可以为该形参赋值null,即查询(删除/修改)所 有数据。

5.5 自定义mapper查询

UserMapper 中添加selectMapById方法

package com.tigerhhzz.springbootmybatisplusdemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;
import org.springframework.stereotype.Repository;

import java.util.Map;

/**
 * @author tigerhhzz
 */
@Repository
public interface UserMapper extends BaseMapper<User> {

    /**
     * 根据id查询用户信息为map集合
     */

    Map<String,Object> selectMapById(Long id);

}

新建UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tigerhhzz.springbootmybatisplusdemo.mapper.UserMapper">

<!-- Map<String,Object> selectMapById(Long id);-->

    <select id="selectMapById" resultType="map">
        select id,name,age,email from user where id= #{id}
    </select>

</mapper>

测试方法:

    //自定义map查询
    @Test
    public void selectMapById(){
        //通过map条件查询用户信息
        Map<String, Object> map = userMapper.selectMapById(1L);
        System.out.println(map);
    }

结果输出:

在这里插入图片描述

6、通用Service

说明:
通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删 除
list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆, 泛型 T 为任意实体对象 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类

官网地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3

6.1 IService

MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑
详情查看源码IService和ServiceImpl

6.1.1 IService源码:
/*
 * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.baomidou.mybatisplus.extension.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 顶级 Service
 *
 * @author hubin
 * @since 2018-06-23
 */
public interface IService<T> {

    /**
     * 插入一条记录(选择字段,策略插入)
     *
     * @param entity 实体对象
     */
    boolean save(T entity);

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, 1000);
    }

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     */
    boolean saveBatch(Collection<T> entityList, int batchSize);

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, 1000);
    }

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  每次的数量
     */
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    boolean removeById(Serializable id);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    boolean removeByMap(Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    boolean remove(Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID 批量删除)
     *
     * @param idList 主键ID列表
     */
    boolean removeByIds(Collection<? extends Serializable> idList);

    /**
     * 根据 ID 选择修改
     *
     * @param entity 实体对象
     */
    boolean updateById(T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    boolean update(T entity, Wrapper<T> updateWrapper);

    /**
     * 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
     *
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean update(Wrapper<T> updateWrapper) {
        return update(null, updateWrapper);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, 1000);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     * @param batchSize  更新批次数量
     */
    boolean updateBatchById(Collection<T> entityList, int batchSize);

    /**
     * TableId 注解存在更新记录,否插入一条记录
     *
     * @param entity 实体对象
     */
    boolean saveOrUpdate(T entity);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T getById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表
     */
    List<T> listByIds(Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> listByMap(Map<String, Object> columnMap);

    /**
     * 根据 Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default T getOne(Wrapper<T> queryWrapper) {
        return getOne(queryWrapper, true);
    }

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param throwEx      有多个 result 是否抛出异常
     */
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    int count(Wrapper<T> queryWrapper);

    /**
     * 查询总记录数
     *
     * @see Wrappers#emptyWrapper()
     */
    default int count() {
        return count(Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    List<T> list(Wrapper<T> queryWrapper);

    /**
     * 查询所有
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<T> list() {
        return list(Wrappers.emptyWrapper());
    }

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper);

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default <E extends IPage<T>> E page(E page) {
        return page(page, Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);

    /**
     * 查询所有列表
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<Map<String, Object>> listMaps() {
        return listMaps(Wrappers.emptyWrapper());
    }

    /**
     * 查询全部记录
     */
    default List<Object> listObjs() {
        return listObjs(Function.identity());
    }

    /**
     * 查询全部记录
     *
     * @param mapper 转换函数
     */
    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return listObjs(Wrappers.emptyWrapper(), mapper);
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return listObjs(queryWrapper, Function.identity());
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper);

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
        return pageMaps(page, Wrappers.emptyWrapper());
    }

    /**
     * 获取对应 entity 的 BaseMapper
     *
     * @return BaseMapper
     */
    BaseMapper<T> getBaseMapper();

    /**
     * 以下的方法使用介绍:
     *
     * 一. 名称介绍
     * 1. 方法名带有 query 的为对数据的查询操作, 方法名带有 update 的为对数据的修改操作
     * 2. 方法名带有 lambda 的为内部方法入参 column 支持函数式的
     *
     * 二. 支持介绍
     * 1. 方法名带有 query 的支持以 {@link ChainQuery} 内部的方法名结尾进行数据查询操作
     * 2. 方法名带有 update 的支持以 {@link ChainUpdate} 内部的方法名为结尾进行数据修改操作
     *
     * 三. 使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
     * 1. 根据条件获取一条数据: `query().eq("column", value).one()`
     * 2. 根据条件删除一条数据: `update().eq("column", value).remove()`
     *
     */

    /**
     * 链式查询 普通
     *
     * @return QueryWrapper 的包装类
     */
    default QueryChainWrapper<T> query() {
        return ChainWrappers.queryChain(getBaseMapper());
    }

    /**
     * 链式查询 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaQueryWrapper 的包装类
     */
    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(getBaseMapper());
    }

    /**
     * 链式更改 普通
     *
     * @return UpdateWrapper 的包装类
     */
    default UpdateChainWrapper<T> update() {
        return ChainWrappers.updateChain(getBaseMapper());
    }

    /**
     * 链式更改 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaUpdateWrapper 的包装类
     */
    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(getBaseMapper());
    }

    /**
     * <p>
     * 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
     * 此次修改主要是减少了此项业务代码的代码量(存在性验证之后的saveOrUpdate操作)
     * </p>
     *
     * @param entity 实体对象
     */
    default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
        return update(entity, updateWrapper) || saveOrUpdate(entity);
    }
}

6.1.2 ServiceImpl源码:
/*
 * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.baomidou.mybatisplus.extension.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * IService 实现类( 泛型:M 是 mapper 对象,T 是实体 , PK 是主键泛型 )
 *
 * @author hubin
 * @since 2018-06-23
 */
@SuppressWarnings("unchecked")
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {

    protected Log log = LogFactory.getLog(getClass());

    @Autowired
    protected M baseMapper;

    @Override
    public M getBaseMapper() {
        return baseMapper;
    }

    /**
     * 判断数据库操作是否成功
     *
     * @param result 数据库操作返回影响条数
     * @return boolean
     */
    protected boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(getClass(), 1);
    }

    /**
     * 批量操作 SqlSession
     *
     * @deprecated 3.3.0
     */
    @Deprecated
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(currentModelClass());
    }

    /**
     * 释放sqlSession
     *
     * @param sqlSession session
     * @deprecated 3.3.0
     */
    @Deprecated
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
    }

    /**
     * 获取 SqlStatement
     *
     * @param sqlMethod ignore
     * @return ignore
     */
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    @Override
    public boolean save(T entity) {
        return retBool(baseMapper.insert(entity));
    }

    /**
     * 批量插入
     *
     * @param entityList ignore
     * @param batchSize  ignore
     * @return ignore
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
        int size = entityList.size();
        executeBatch(sqlSession -> {
            int i = 1;
            for (T entity : entityList) {
                sqlSession.insert(sqlStatement, entity);
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
        return true;
    }

    /**
     * TableId 注解存在更新记录,否插入一条记录
     *
     * @param entity 实体对象
     * @return boolean
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveOrUpdate(T entity) {
        if (null != entity) {
            Class<?> cls = entity.getClass();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
            Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
            String keyProperty = tableInfo.getKeyProperty();
            Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
            Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
            return StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal)) ? save(entity) : updateById(entity);
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        Assert.notEmpty(entityList, "error: entityList must not be empty");
        Class<?> cls = currentModelClass();
        TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
        String keyProperty = tableInfo.getKeyProperty();
        Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
        int size = entityList.size();
        executeBatch(sqlSession -> {
            int i = 1;
            for (T entity : entityList) {
                Object idVal = ReflectionKit.getMethodValue(cls, entity, keyProperty);
                if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {
                    sqlSession.insert(sqlStatement(SqlMethod.INSERT_ONE), entity);
                } else {
                    MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                    param.put(Constants.ENTITY, entity);
                    sqlSession.update(sqlStatement(SqlMethod.UPDATE_BY_ID), param);
                }
                // 不知道以后会不会有人说更新失败了还要执行插入 😂😂😂
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
        return true;
    }

    @Override
    public boolean removeById(Serializable id) {
        return SqlHelper.retBool(baseMapper.deleteById(id));
    }

    @Override
    public boolean removeByMap(Map<String, Object> columnMap) {
        Assert.notEmpty(columnMap, "error: columnMap must not be empty");
        return SqlHelper.retBool(baseMapper.deleteByMap(columnMap));
    }

    @Override
    public boolean remove(Wrapper<T> wrapper) {
        return SqlHelper.retBool(baseMapper.delete(wrapper));
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            return false;
        }
        return SqlHelper.retBool(baseMapper.deleteBatchIds(idList));
    }

    @Override
    public boolean updateById(T entity) {
        return retBool(baseMapper.updateById(entity));
    }

    @Override
    public boolean update(T entity, Wrapper<T> updateWrapper) {
        return retBool(baseMapper.update(entity, updateWrapper));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        Assert.notEmpty(entityList, "error: entityList must not be empty");
        String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
        int size = entityList.size();
        executeBatch(sqlSession -> {
            int i = 1;
            for (T anEntityList : entityList) {
                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                param.put(Constants.ENTITY, anEntityList);
                sqlSession.update(sqlStatement, param);
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
        return true;
    }

    @Override
    public T getById(Serializable id) {
        return baseMapper.selectById(id);
    }

    @Override
    public List<T> listByIds(Collection<? extends Serializable> idList) {
        return baseMapper.selectBatchIds(idList);
    }

    @Override
    public List<T> listByMap(Map<String, Object> columnMap) {
        return baseMapper.selectByMap(columnMap);
    }

    @Override
    public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
        if (throwEx) {
            return baseMapper.selectOne(queryWrapper);
        }
        return SqlHelper.getObject(log, baseMapper.selectList(queryWrapper));
    }

    @Override
    public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
        return SqlHelper.getObject(log, baseMapper.selectMaps(queryWrapper));
    }

    @Override
    public int count(Wrapper<T> queryWrapper) {
        return SqlHelper.retCount(baseMapper.selectCount(queryWrapper));
    }

    @Override
    public List<T> list(Wrapper<T> queryWrapper) {
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        return baseMapper.selectPage(page, queryWrapper);
    }

    @Override
    public List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
        return baseMapper.selectMaps(queryWrapper);
    }

    @Override
    public <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return baseMapper.selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }

    @Override
    public <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
        return baseMapper.selectMapsPage(page, queryWrapper);
    }

    @Override
    public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return SqlHelper.getObject(log, listObjs(queryWrapper, mapper));
    }

    /**
     * 执行批量操作
     *
     * @param fun fun
     * @since 3.3.0
     */
    protected void executeBatch(Consumer<SqlSession> fun) {
        Class<T> tClass = currentModelClass();
        SqlHelper.clearCache(tClass);
        SqlSessionFactory sqlSessionFactory = SqlHelper.sqlSessionFactory(tClass);
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            fun.accept(sqlSession);
            sqlSession.commit();
        } catch (Throwable t) {
            sqlSession.rollback();
            Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
            if (unwrapped instanceof RuntimeException) {
                MyBatisExceptionTranslator myBatisExceptionTranslator
                    = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
                throw Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException) unwrapped));
            }
            throw ExceptionUtils.mpe(unwrapped);
        } finally {
            sqlSession.close();
        }
    }
}
6.2 创建Service接口和实现类

UserService

package com.tigerhhzz.springbootmybatisplusdemo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;

/**
 * @author tigerhhzz
 * @date 2023/5/5 10:51
 */
public interface UserService extends IService<User> {
}

UserServiceImpl

package com.tigerhhzz.springbootmybatisplusdemo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;
import com.tigerhhzz.springbootmybatisplusdemo.mapper.UserMapper;
import com.tigerhhzz.springbootmybatisplusdemo.service.UserService;

/**
 * @author tigerhhzz
 * @date 2023/5/5 10:53
 */
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

6.3 测试查询记录数

创建测试类和测试方法

package com.tigerhhzz.springbootmybatisplusdemo;

import com.tigerhhzz.springbootmybatisplusdemo.service.UserService;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @author tigerhhzz
 * @date 2023/5/5 11:10
 */
@SpringBootTest
@MapperScan("com.tigerhhzz.springbootmybatisplusdemo.mapper")
public class MyBatisPlusServiceTest {


    @Autowired
    UserService userService;

    @Test
    public void testGetCount(){
        long count = userService.count();
        System.out.println("总记录数:" + count);
    }
}

6.4 测试批量插入
    @Test
    public void testSaveBatch() {
        // SQL长度有限制,海量数据插入单条SQL无法实行,
        // 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
        ArrayList<User> users = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            User user = new User();
            user.setName("tiger" + i);
            user.setAge(20 + i);
            users.add(user);
        }
        //SQL:INSERT INTO t_user ( username, age ) VALUES ( ?, ? )
        userService.saveBatch(users);
    }

四、常用注解

1、@TableName

经过以上的测试,在使用MyBatis-Plus实现基本的CRUD时,我们并没有指定要操作的表,只是在
Mapper接口继承BaseMapper时,设置了泛型User,而操作的表为user表

由此得出结论,MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决
定,且默认操作的表名和实体类型的类名一致

  1. 问题
    若实体类类型的类名和要操作的表的表名不一致,会出现什么问题?
    我们将表user更名为t_user,测试查询功能
    程序抛出异常,Table ‘mybatis_plus.user’ doesn’t exist,因为现在的表名为t_user,而默认操作
    的表名和实体类型的类名一致,即user表
    在这里插入图片描述

  2. 通过@TableName解决问题

在实体类类型上添加@TableName(“t_user”),标识实体类对应的表,即可成功执行SQL语句

package com.tigerhhzz.springbootmybatisplusdemo.domain;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author tigerhhzz
 * @date 2023/5/4 16:09
 */
@Data
//设置实体类所对应的表名
@TableName("t_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;

}
  1. 通过全局配置解决问题

在开发的过程中,我们经常遇到以上的问题,即实体类所对应的表都有固定的前缀,例如t_或tbl_
此时,可以使用MyBatis-Plus提供的全局配置,为实体类所对应的表名设置默认的前缀,那么就
不需要在每个实体类上通过@TableName标识实体类对应的表

mybatis-plus:
	configuration:
		# 配置MyBatis日志
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
	global-config:
		db-config:
			# 配置MyBatis-Plus操作表的默认前缀
			table-prefix: t_

2、@TableId

经过以上的测试,MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认 基于雪花算法的策略生成id

  1. 问题

若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主 键列吗?

我们实体类中的属性id改为uid,将表中的字段id也改为uid,测试添加功能 程序抛出异常,Field ‘uid’ doesn’t have a default value,说明MyBatis-Plus没有将uid作为主键 赋值

在这里插入图片描述

  1. 通过@TableId解决问题

在实体类中uid属性上通过@TableId将其标识为主键,即可成功执行SQL语句

package com.tigerhhzz.springbootmybatisplusdemo.domain;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author tigerhhzz
 * @date 2023/5/4 16:09
 */
@Data
//设置实体类所对应的表名
//@TableName("t_user")
public class User {

    @TableId
    private Long uid;

    private String name;

    private Integer age;

    private String email;

}
  1. @TableId的value属性

若实体类中主键对应的属性为id,而表中表示主键的字段为uid,此时若只在属性id上添加注解 @TableId,则抛出异常Unknown column ‘id’ in ‘field list’,即MyBatis-Plus仍然会将id作为表的主键操作,而表中表示主键的是字段uid 此时需要通过@TableId注解的value属性,指定表中的主键字段,@TableId(“uid”) 或者 @TableId(value=“uid”)

package com.tigerhhzz.springbootmybatisplusdemo.domain;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author tigerhhzz
 * @date 2023/5/4 16:09
 */
@Data
//设置实体类所对应的表名
//@TableName("t_user")
public class User {

    //将属性所对应的字段指定为主键
    @TableId("uid")
    private Long id;

    private String name;

    private Integer age;

    private String email;

}
  1. @TableId的type属性

type属性用来定义主键策略


/**
 * @author tigerhhzz
 * @date 2023/5/4 16:09
 */
@Data
//设置实体类所对应的表名
//@TableName("t_user")
public class User {

    //将属性所对应的字段指定为主键
    @TableId(value = "uid",type = IdType.AUTO)
    private Long id;

    private String name;

    private Integer age;

    private String email;

}

常用的主键策略:

描述
IdType.ASSIGN_ID(默认)基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
IdType.AUTO使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效

配置全局主键策略:

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: full
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  global-config:
    #MybatisPlus的全局配置
    db-config:
      # 逻辑不删除的值
      logic-not-delete-value: 1
      # 逻辑删除
      logic-delete-value: 0
      # 配置MyBatis-Plus操作表的默认前缀
      table-prefix: t_
      # 配置MyBatis-Plus的主键策略
      id-type: auto

3、@TableField

经过以上的测试,我们可以发现,MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和 表中的字段名一致

如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?

  1. 情况1

若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格 例如实体类属性userName,表中字段user_name
此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格 相当于在MyBatis中配置

  1. 情况2

若实体类中的属性和表中的字段不满足情况1 例如实体类属性name,表中字段username
此时需要在实体类属性上使用@TableField(“username”)设置属性所对应的字段名

4、@TableLogic

4.1 逻辑删除

物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库 中仍旧能看到此条数据记录
使用场景:可以进行数据恢复

4.2 实现逻辑删除

step1:数据库中创建逻辑删除状态列,设置默认值为1

在这里插入图片描述

4.3 实体类中添加逻辑删除属性
package com.tigerhhzz.springbootmybatisplusdemo.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author tigerhhzz
 * @date 2023/5/4 16:09
 */
@Data
//设置实体类所对应的表名
//@TableName("t_user")
public class User {

    //将属性所对应的字段指定为主键
    @TableId(value = "uid",type = IdType.AUTO)
    private Long id;

    private String name;

    private Integer age;

    private String email;

    @TableLogic
    private Integer isDeleted;

}

4.4 测试

测试删除功能,真正执行的是修改

UPDATE t_user SET is_deleted=0 WHERE uid IN ( ? , ? , ? ) AND is_deleted=1 

测试查询功能,被逻辑删除的数据默认不会被查询

SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 

五、条件构造器和常用接口

1、wapper介绍

在这里插入图片描述

  • Wrapper : 条件构造抽象类,最顶端父类
    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询条件封装
      • UpdateWrapper : Update 条件封装
      • AbstractLambdaWrapper : 使用Lambda 语法
        • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper

2、QueryWrapper(查询和删除)

2.1、组装查询条件
package com.tigerhhzz.springbootmybatisplusdemo;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;
import com.tigerhhzz.springbootmybatisplusdemo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

/**
 * @author tigerhhzz
 * @date 2023/5/8 14:17
 */
@SpringBootTest
@MapperScan("com.tigerhhzz.springbootmybatisplusdemo.mapper")
public class MybatisPlusWrapTest {

    @Autowired
    UserMapper userMapper;

    @Test
    public void test01(){
        //查询用户名包含m,年龄在21到23之间,邮箱不为空的用户信息
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","m")
                .between("age",21,23)
                .isNotNull("email");

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);

    }

}

查询结果:

SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)

在这里插入图片描述

2.2、组装排序条件
    @Test
    public void test02(){
        //查询用户,按照年龄的降序排序,若年龄相同,则按照id升序排序
        // SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 ORDER BY age DESC , uid ASC 
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("age")
                .orderByAsc("uid");

        List<User> userList = userMapper.selectList(queryWrapper);
        userList.forEach(System.out::println);

    }

查询结果:

SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 ORDER BY age DESC , uid ASC

在这里插入图片描述

2.3、组装删除条件

因为添加了逻辑删除,此方法其实执行的是修改语句update

    @Test
    public void test03(){
        //删除email为空的用户
        //UPDATE t_user SET is_deleted=0 WHERE is_deleted=1 AND (email IS NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("email");
        //条件构造器也可以构建删除语句的条件
        int result = userMapper.delete(queryWrapper);
        System.out.println("受影响的行数:" + result);
    }

结果:
在这里插入图片描述

2.4、条件的优先级
    @Test
    public void test04() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //将(年龄大于20并且用户名中包含有m)或邮箱为null的用户信息修改
        //UPDATE t_user SET age=?, email=? WHERE is_deleted=1 AND (name LIKE ? AND age > ? OR email IS NULL) 
        queryWrapper
                .like("name", "m")
                .gt("age", 20)
                .or()
                .isNull("email");
        User user = new User();
        user.setAge(18);
        user.setEmail("test04@baomidou.com");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("受影响的行数:" + result);
    }

结果:
在这里插入图片描述

    @Test
    public void test0401() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        //UPDATE t_user SET age=?, email=? WHERE is_deleted=1 AND (name LIKE ? AND ( (age > ? OR email IS NULL) )) 
        //lambda表达式内的逻辑优先运算
        queryWrapper.like("name","m")
                .and(i->i.gt("age",20).or().isNull("email"));
        User user = new User();
        user.setAge(18);
        user.setEmail("test0401@baomidou.com");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("受影响的行数:" + result);

    }

结果:
在这里插入图片描述

2.5、组装select子句
    @Test
    public void test05() {
        //查询用户信息的username和age字段
        //SELECT name,age FROM t_user WHERE is_deleted=1 
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name", "age");
        //selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }

结果:
在这里插入图片描述

2.6、实现子查询
    @Test
    public void test06() {
        //查询id小于等于6的用户信息
        //SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 AND (uid IN (select uid from t_user where uid <= 6)) 
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.inSql("uid", "select uid from t_user where uid <= 6");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

结果:
在这里插入图片描述

3、UpdateWrapper(修改)

    @Test
    public void test07() {
        //将(年龄大于20或邮箱为null)并且用户名中包含有m的用户信息修改
        //组装set子句以及修改条件
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        //lambda表达式内的逻辑优先运算
        updateWrapper
                .set("age", 19)
                .set("email", "test07@baomidou.com")
                .like("name", "m")
                .and(i -> i.gt("age", 20).or().isNull("email"));
        //这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
        //UPDATE t_user SET age=?,email=? WHERE is_deleted=1 AND (name LIKE ? AND ( (age > ? OR email IS NULL) ))

        int result = userMapper.update(null, updateWrapper);
        System.out.println(result);

    }

结果:
在这里插入图片描述

4、condition

在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因
此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若 没有选择则一定不能组装,以免影响SQL执行的结果

思路一:

@Test
public void test08() {
//定义查询条件,有可能为null(用户未输入或未选择)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)
构成
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=
? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

思路一:

上面的实现方案没有问题,但是代码比较复杂,我们可以使用带condition参数的重载方法构建查 询条件,简化代码的编写

    @Test
    public void test08UseCondition() {
        //定义查询条件,有可能为null(用户未输入或未选择)
        String username = "aaa";
        Integer ageBegin = 10;
        Integer ageEnd = 24;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成
                queryWrapper.like(StringUtils.isNotBlank(username), "name", "g")
                        .and(i->i.ge(ageBegin != null, "age", ageBegin).le(ageEnd != null, "age", ageEnd));
        //SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 AND (name LIKE ? AND ( (age >= ? AND age <= ?) ))
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

结果:
在这里插入图片描述

5、LambdaQueryWrapper

    @Test
    public void test09() {
        //定义查询条件,有可能为null(用户未输入)
        String username = "g";
        Integer ageBegin = 10;
        Integer ageEnd = 24;
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        //避免使用字符串表示字段,防止运行时错误
        queryWrapper
                .like(StringUtils.isNotBlank(username), User::getName, username)
                .ge(ageBegin != null, User::getAge, ageBegin)
                .le(ageEnd != null, User::getAge, ageEnd);
        //SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 AND (name LIKE ? AND age >= ? AND age <= ?)
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(System.out::println);
    }

6、LambdaUpdateWrapper

    @Test
    public void test10() {
        //将(年龄大于20或邮箱为null)并且用户名中包含有m的用户信息修改
        //组装set子句以及修改条件
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        //lambda表达式内的逻辑优先运算
        updateWrapper
                .set(User::getAge, 19)
                .set(User::getEmail, "test10@baomidou.com")
                .like(User::getName, "m")
                .and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));
        //这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
        //UPDATE t_user SET age=?,email=? WHERE is_deleted=1 AND (name LIKE ? AND ( (age > ? OR email IS NULL) ))

        int result = userMapper.update(null, updateWrapper);
        System.out.println(result);

    }

结果:
在这里插入图片描述

六、插件

1、分页插件

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

1.1、添加配置类
package com.tigerhhzz.springbootmybatisplusdemo.config;

import com.baomidou.mybatisplus.annotation.DbType;

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

@Configuration
@MapperScan("com.tigerhhzz.springbootmybatisplusdemo.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}
1.2、测试
package com.tigerhhzz.springbootmybatisplusdemo;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;
import com.tigerhhzz.springbootmybatisplusdemo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

/**
 * @author tigerhhzz
 * @date 2023/5/9 8:41
 */
@SpringBootTest
@MapperScan("com.tigerhhzz.springbootmybatisplusdemo.mapper")
public class MybatisPlusPluginTest {

    @Autowired
    UserMapper userMapper;

    @Test
    public void testPage(){
        //设置分页参数
        Page<User> page = new Page<>(2, 3);
        userMapper.selectPage(page, null);
        //获取分页数据 SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=1 LIMIT ?,?
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());
    }


}

结果:
在这里插入图片描述

2、xml自定义分页

2.1、UserMapper中定义接口方法
package com.tigerhhzz.springbootmybatisplusdemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tigerhhzz.springbootmybatisplusdemo.domain.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.Map;

/**
 * @author tigerhhzz
 */
@Repository
public interface UserMapper extends BaseMapper<User> {

    /**
     * 根据id查询用户信息为map集合
     */

    Map<String,Object> selectMapById(Long id);

    /**
     * 通过年龄查询用户信息并分页
     * @param page mybatis-plus所提供的分页对象,必须位于第一个参数的位置
     * @param age
     * @return
     */
    Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);

}
2.2、UserMapper.xml中编写SQL
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tigerhhzz.springbootmybatisplusdemo.mapper.UserMapper">

<!-- Map<String,Object> selectMapById(Long id);-->

    <select id="selectMapById" resultType="map">
        select id,name,age,email from user where id= #{id}
    </select>

    <select id="selectPageVo" resultType="User">
        select uid as id,name,age,email from t_user where age > #{age}

    </select>


</mapper>
2.3、测试
    @Test
    public void testPageVo(){
        Page<User> page = new Page<>(1,3);

        Page<User> pageVo = userMapper.selectPageVo(page, 20);

        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());
    }

结果:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

史上最烂 spring web 原理分析

盗引下篇spring web spring web、spring web 与 tomcat、映射器与适配器、参数解析器与类型转换器、返回值处理器与消息转换器、异常处理器、ControllerAdvice、spring web 工作流程。 版本 jdk&#xff1a;8spring&#xff1a;5.3.20spring boot&#xff1a;2.7.0 1 spring…

python调用海康sdk报错问题

sdk参考&#xff1a; (68条消息) Python调用海康威视网络相机_调用海康SDK_python 海康威视_有一点点麻瓜的博客-CSDN博客https://blog.csdn.net/yinweizhehd/article/details/118722052 报错1&#xff1a; 生成解决方案的时候&#xff0c;显示LNK2001&#xff1a;无法解析的…

如果你访问了某个网站,又不想让人知道怎么办?

问大家一个问题&#xff1a;如果你访问了某个网站&#xff0c;又不想让人知道怎么办&#xff1f; 你可能会说&#xff0c;把浏览器浏览历史记录清除&#xff0c;或者直接用无痕模式。 如果你只能想到这一层&#xff0c;那只能说图young&#xff01; 这么说吧&#xff0c;理论…

基于RK3588的8K智能摄像机方案设计

设计了一款基于石墨烯散热的8 K智能摄像头&#xff0c;主控采用瑞芯微RK3588&#xff0c;传感器采用索尼IMX435&#xff0c; 通过HDMI2.1将传感器采集到的图像发送到8 K显示器&#xff0c;实现端到端的8 K呈现&#xff0c;为了确保摄像头性能稳定&#xff0c;本 设计采用石墨烯…

计算机网络安全--期末

计算机网络安全绪论 计算机网络实体是什么 计算机网络中的关键设备&#xff0c;包括各类计算机、网络和通讯设备、存储数据的媒体、传输路线…等 典型的安全威胁有哪些 ★ ⋆ \bigstar\star ★⋆ 窃听(敏感信息被窃听)重传(被获取在传过来)伪造(伪造信息发送&#xff09;篡…

《花雕学AI》30:ChatGPT的资料来源比例排名前20名是什么?

引言&#xff1a;ChatGPT是一款由OpenAI开发的人工智能聊天机器人&#xff0c;它可以回答各种问题&#xff0c;并生成创意内容&#xff0c;如诗歌、故事、代码等。 ChatGPT的核心技术是基于GPT-3.5和GPT-4的大型语言模型&#xff0c;它可以利用从网路上收集的大量文本资料来进行…

MySQL执行顺序

MySQL执行顺序 MySQL语句的执行顺序也是在面试过程中经常问到的问题&#xff0c;并且熟悉执行顺序也有助于SQL语句的编写。 SELECT FROM JOIN ON WHERE GROUP BY HAVING ORDER BY LIMIT执行顺序如下&#xff1a; FROM ON JOIN WHERE GROUP BY # (开始使用别名) SUM # SUM等…

备战2个月,四轮面试拿下字节offer...

背景 菜 J 一枚&#xff0c;本硕都是计算机&#xff08;普通二本&#xff09;&#xff0c;2021 届应届硕士&#xff0c;软件测试方向。个人也比较喜欢看书&#xff0c;技术书之类的都有看&#xff0c;最后下面也会推荐一些经典书籍。 先说一下春招结果&#xff1a;拿下了四个…

vmware 安装Kylin-Desktop-V10-SP1-General-Release-2203-X86_64.iso

下载 官网&#xff1a;国产操作系统、银河麒麟、中标麒麟、开放麒麟、星光麒麟——麒麟软件官方网站 (kylinos.cn) 点击桌面操作系统 选择No1 点击申请试用 填写相关信息&#xff0c;点击立即提交&#xff0c;就会获取到下载连接&#xff0c; 点击下载按钮等待下载完成即可 安…

Go有序map:orderedmap

有序映射 与传统的无序映射&#xff08;Map&#xff09;不同&#xff0c;orderedmap包中的有序映射&#xff08;OrderedMap&#xff09;可以记录键值对的插入顺序。orderedmap提供了一些有用的API&#xff0c;用来存储、删除、查询和遍历键值对。 获取OrderedMap 你可以通过Ord…

编译安装最新的Linux系统内核

现在还有不少机器是CentOS8 Stream系统&#xff0c;虽然上了贼船&#xff0c;不影响用就是了。8的编译和7大同小异&#xff0c;只是踩了更多的坑在这里记录一下&#xff0c;或许会帮到看到的朋友。 安装编译环境 CentOS8安装必要的包 yum groupinstall "Development Too…

2022年NOC大赛编程马拉松赛道复赛图形化高年级A卷-正式卷,包含答案

目录 单选题: 多选题: 编程题: 下载打印文档做题: 2022年NOC大赛编程马拉松赛道复赛图形化高年级A卷-正式卷,包含答案 单选题:<

《Netty》从零开始学netty源码(五十三)之PoolThreadCache的功能

allocateNormal 在前面分析PoolArena的分配内存的方法中&#xff0c;每次分配都是先从本地线程缓存中分配&#xff0c;本地线程缓存PoolThreadCache的分配方法如下&#xff1a; 分配过程主要有两步&#xff1a; 从PoolThreadCache的缓存数组中获取相应大小的缓存cache将需要…

桌面虚拟化的优势

启用基于云的虚拟桌面基础架构 &#xff08;VDI&#xff09; OpenText™ Exceed TurboX™ &#xff08;ETX&#xff09; 长期以来一直是虚拟化在 Linux 主机上运行的图形要求苛刻的软件的黄金标准。ETX 最新版本&#xff08;12.5&#xff09;增加了许多Microsoft Windows功能&…

智能座舱的“宏大蓝图”和“残酷现实”

配图来自Canva可画 2023年上海车展各大车企发布新车、新配置和新战略好不热闹&#xff0c;“智能驾驶”、“智能座舱”等关键词频频出现&#xff0c;智能化已然成为车企技术比拼的关键。 Unity中国发布最新智能座舱解决方案&#xff0c;可为车企提供成熟、可量产落地的HMI&…

什么是点对点传输?什么是点对多传输

点对点技术&#xff08;peer-to-peer&#xff0c; 简称P2P&#xff09;又称对等互联网络技术&#xff0c;是一种网络新技术&#xff0c;依赖网络中参与者的计算能力和带宽&#xff0c;而不是把依赖都聚集在较少的几台服务器上。P2P网络通常用于通过Ad Hoc连接来连接节点。这类网…

深度学习 - 46.DIN 深度兴趣网络

目录 一.引言 二.摘要 ABSTRACT 三.介绍 INTRODUCTION 1.CTR 在广告系统的作用 2.传统 MLP 存在的问题 3.DIN 的改进 四.近期工作 RELATEDWORK 1.传统推荐算法 2.用户行为抽取 五.背景 BACKGROUD 六.深度兴趣网络 DEEP INTEREST NETWORK 1.特征表示 Feature Repres…

triton 疑难手册

config.pbtxt 配置参数手册 backend或platform参数用于指示nvidia triton用对应的backend加载模型参数,它的使用示例如下: name: "xxx" platform: "pytorch_libtorch"max_batch_size: 8 input [ {name: "input0"data_type: TYPE_UINT8dims: …

ansible常用命令

目录 1、列出默认清单文件中的所有受管主机 2. 列出自定义清单文件中的所有受管主机&#xff08;自定义清单文件&#xff1a;inventory&#xff09; 3、运行playbook 4、创建需要输入文件密码的加密的文件 5、创建用密码文件的加密的文件 6、查看加密的文件内容 7、向已有…

助力工业物联网,工业大数据之ODS层构建:申明分区代码及测试【十】

文章目录 知识点13&#xff1a;ODS层构建&#xff1a;申明分区代码及测试知识点14&#xff1a;ODS层与DWD层区别知识点15&#xff1a;DWD层构建&#xff1a;需求分析知识点16&#xff1a;DWD层构建&#xff1a;建库实现测试知识点17&#xff1a;DWD层构建&#xff1a;建表实现测…