• MyBatis-Puls一篇就够


    文章目录

    MyBatisPlus快速入门

    1.简介

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

    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
    • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

    4.框架结构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09UJlL5R-1656243050354)(img\1656143371956.png)]


    执行的流程是:通过BaseMapper扫描POJO实体类,通过反射分析表的字段,再分析调用的方法是增删还是改查,最终通过反射Mapper将生成实现类放到MyBatis容器中



    快速入门

    1.创建数据库及表

    创建表:

    #mybatis_plus
    CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
    USE `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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意是:由于mybatis_plus使用雪花算法,在主键自动生成时会很长,所以需要使用BIGINTG数据类型

    插入数据:

    INSERT INTO USER (id, NAME, age, email) VALUES
    (1, 'Jone', 18, 'test1@baomidou.com'),
    (2, 'Jack', 20, 'test2@baomidou.com'),
    (3, 'Tom', 28, 'test3@baomidou.com'),
    (4, 'Sandy', 21, 'test4@baomidou.com'),
    (5, 'Billie', 24, 'test5@baomidou.com');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6


    2.创建一个SpringBoot工程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0bYcRy87-1656243050354)(D:\typora笔记\mybatisplus\img\1656119275539.png)]

    注意是:由于在创建工程时,我们还没有添加mybatis_plus的场景启动器,所以下面需要我们手动添加

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.配置application.yml信息

    建议直接复制修改,防止写错

    #修改端口号
    server:
      port: 80
    
    spring:
      #配置数据源信息
      datasource:
        #配置数据源类型
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
        username: root
        password: root
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注意

    1、驱动类driver-class-name
    spring boot 2.0(内置jdbc5驱动),驱动类使用:
    driver-class-name: com.mysql.jdbc.Driver
    spring boot 2.1及以上(内置jdbc8驱动),驱动类使用:
    driver-class-name: com.mysql.cj.jdbc.Driver
    否则运行测试用例的时候会有 WARN 信息
    2、连接地址url
    MySQL5.7版本的url:
    jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
    MySQL8.0版本的url:
    jdbc:mysql://localhost:3306/mybatis_plus?
    serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
    否则运行测试用例报告如下错误:
    java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or
    represents more
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.创建POJO实体类

    由于mybatis是ORM框架,表示表和对象的映射关系

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.创建Mapper接口并继承BaseMapper接口

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    }
    
    • 1
    • 2
    • 3

    注意是:也可以在springboot启动类中,添加@MapperSacn扫描包的方式,但是不建议

    @SpringBootApplication
    // 扫描mapper接口所在的包
    @MapperScan("com.haikang.plus.mapper")
    public class MybatisPlusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisPlusApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    6.编写测试方法

    @SpringBootTest
    public class MyBatisPlusTest {
    
        // 注入UserMapper对象
        @Autowired
        public UserMapper userMapper;
    
        @Test
        void selectAllUser(){
            // selectList需要传入条件,传入null表示查询全部
            List<User> users = userMapper.selectList(null);
            users.forEach(user -> System.out.println(user));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    7.添加日志功能

    mybatis-plus:
      configuration:
        #mybatisplus日志配置
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4

    BseaMapper源码分析

    1.根据条件查询返回List集合

    源码:

    selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper)方法

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

    测试

        @Test
        void selectAllUser(){
            // selectList需要传入条件,传入null表示查询全部
            List<User> users = userMapper.selectList(null);
            users.forEach(user -> System.out.println(user));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.插入功能

    源码:

    insert方法

        /**
         * 插入一条记录
         *
         * @param entity 实体对象
         */
        int insert(T entity);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试

        // 插入数据
        @Test
        void insert(){
            User user = new User(null,"明天",21,"123@qq.com");
            int insert = userMapper.insert(user);
            System.out.println("result:"+insert);
            System.out.println("id:"+user.getId());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.删除功能

        /**
         * 根据 ID 删除
         *
         * @param id 主键ID
         */
        int deleteById(Serializable id);
    
        /**
         * 根据实体(ID)删除
         *
         * @param entity 实体对象
         * @since 3.4.4
         */
        int deleteById(T entity);
    
        /**
         * 根据 columnMap 条件,删除记录
         *
         * @param columnMap 表字段 map 对象
         */
        int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
        /**
         * 根据 entity 条件,删除记录
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
         */
        int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 删除(根据ID或实体 批量删除)
         *
         * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
         */
        int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    测试:

        // 删除功能
        @Test
        void delete(){
            // 根据ID进行删除
            // DELETE FROM user WHERE id=?
            int id = userMapper.deleteById(6);
            System.out.println(id);
    
            // 根据实体ID删除
            //  DELETE FROM user WHERE id=?
            long l = 23L;
            User user = new User(l,"6",6,"6");
            int userId = userMapper.deleteById(user);
            System.out.println(userId);
    
            // 根据collection集合批量删除
            // DELETE FROM user WHERE id IN ( ? , ? , ? , ? )
            List<Integer> listId = Arrays.asList(7,8,9,10);
            int batchIds = userMapper.deleteBatchIds(listId);
            System.out.println(batchIds);
    
            // 根据Map集合封装数据进行删除
            // DELETE FROM user WHERE name = ? AND age = ? AND email = ?
            Map<String,Object> map = new HashMap<>();
            map.put("name","海康");
            map.put("age",21);
            map.put("email","123@qq.com");
    
            int deleteByMap = userMapper.deleteByMap(map);
            System.out.println(deleteByMap);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4.修改功能

    源码:

        /**
         * 根据 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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试:

        // 测试功能
        @Test
        void update(){
            User user = new User();
            user.setId(8l);
            user.setName("海康");
            user.setAge(23);
            user.setEmail("123@qq.com");
    
            // UPDATE user SET name=?, age=?, email=? WHERE id=?
            int update = userMapper.updateById(user);
            System.out.println(update);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.查询功能

        /**
         * 根据 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 条件,查询一条记录
         * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
            List<T> ts = this.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(ts)) {
                if (ts.size() != 1) {
                    throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
                }
                return ts.get(0);
            }
            return null;
        }
    
        /**
         * 根据 Wrapper 条件,查询总记录数
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        Long 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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    测试

        // 查询功能
        @Test
        void select(){
            // 根据ID查询用户信息
            // SELECT id,name,age,email FROM user WHERE id=?
            User user = userMapper.selectById(1l);
            System.out.println(user);
    
            
            // 根据Map集合封装条件查询
            // SELECT id,name,age,email FROM user WHERE name = ? AND age = ? AND email = ?
            Map<String,Object> map = new HashMap<>();
            map.put("name","明天");
            map.put("age",21);
            map.put("email","123@qq.com");
            List<User> users = userMapper.selectByMap(map);
            System.out.println(users);
    
    
            // 根据多个ID查询多条数据
            // SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )
            List<Long> asList = Arrays.asList(1l, 2l, 3l );
            List<User> selectBatchIds = userMapper.selectBatchIds(asList);
            selectBatchIds.forEach(user1 -> System.out.println(user1));
    
            
            // 根据条件进行查询返回List集合,如果没有传入条件(null),表示查询全部
            // SELECT id,name,age,email FROM user
            List<User> selectList = userMapper.selectList(null);
            selectList.forEach(user2-> System.out.println(user2));
    
            // 查询总记录数,如果没有传入条件(null),表示查询所有记录数
            // SELECT COUNT( * ) FROM user
            Long aLong = userMapper.selectCount(null);
            System.out.println(aLong);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

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

    6.自定义功能

    例如:我们可以查看返回Map集合,用于JSON数据返回

    第一步:在UserMapper接口定义方法

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    
        // 自定义返回Map集合功能
        // 根据Id查询
        @MapKey("id")
        Map<String,Object> getUserById(@Param("id")Long id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第二步:定义UserMapper.xml文件

    <mapper namespace="com.haikang.plus.mapper.UserMapper">
    <!--
        // 自定义返回Map集合功能
        // 根据Id查询
        Map<String,Object> getUserById(@Param("id")Long id);
    -->
        <select id="getUserById" resultType="map">
            select * from user where id=#{id};
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第三步:在核心配置文件中指定maper.xml文件的位置

    mybatis-plus:
      configuration:
        #mybatisplus日志配置
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        #指定xml的位置
      mapper-locations: /mapper/**/**.xml  #也是默认位置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    mybatis-plus默认位置是:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-agYj5RcG-1656243259435)(img\1656130959680.png)]

    第四步:编写控制器测试

        // 自定义功能
        @Test
        void map(){
            // 根据ID查询用户信息,并且返回Map方式
            //  select * from user where id=?;
            Map<String, Object> map = userMapper.getUserById(1l);
            System.out.println(map);
            // 返回值:{name=Jone, id=1, age=18, email=test1@baomidou.com}
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    BaseMapper源码

    /**
     * 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);
    
        /**
         * 根据实体(ID)删除
         *
         * @param entity 实体对象
         * @since 3.4.4
         */
        int deleteById(T entity);
    
        /**
         * 根据 columnMap 条件,删除记录
         *
         * @param columnMap 表字段 map 对象
         */
        int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
        /**
         * 根据 entity 条件,删除记录
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
         */
        int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 删除(根据ID或实体 批量删除)
         *
         * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
         */
        int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> 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 条件,查询一条记录
         * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
            List<T> ts = this.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(ts)) {
                if (ts.size() != 1) {
                    throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
                }
                return ts.get(0);
            }
            return null;
        }
    
        /**
         * 根据 Wrapper 条件,判断是否存在记录
         *
         * @param queryWrapper 实体对象封装操作类
         * @return
         */
        default boolean exists(Wrapper<T> queryWrapper) {
            Long count = this.selectCount(queryWrapper);
            return null != count && count > 0;
        }
    
        /**
         * 根据 Wrapper 条件,查询总记录数
         *
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        Long 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)
         */
        <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 Wrapper 条件,查询全部记录(并翻页)
         *
         * @param page         分页查询条件
         * @param queryWrapper 实体对象封装操作类
         */
        <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162

    通用Service

    1.Service接口CRUD

    说明:

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

    A.IService接口

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

    所以我们可以自定义Serivce接口和实现类,继承和实现相关的接口和类

    第一步:定义UserSerivce继承Iservice接口

    /**
     * UserService继承IService模板提供的基础功能
     */
    public interface UserService extends IService<User> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第二步:定义UserServiceImpl实现类

    /**
     * ServiceImpl实现了IService,提供了IService中基础功能的实现
     * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
     */
     
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第三步:编写测试类

    @SpringBootTest
    public class MyBatisPlusServiceTest {
    
        @Autowired
        UserService userService;
    
        // 查询总记录数
        @Test
        void count(){
            // SELECT COUNT( * ) FROM user;
            long count = userService.count();
            System.out.println(count);
        }
    
        // 保存数据
        @Test
        void save(){
            // 保存一条记录
            // INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
            boolean save = userService.save(new User(9l, "湛江", 21, "123@qq.com"));
            System.out.println(save);
    
            // 批量保存多条数据
            // INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
            List<User> asList = Arrays.asList(new User(10l, "西安", 22, "123qq.com"),
                    new User(11l, "南宁", 22, "123qq.com"),
                    new User(12l, "新疆", 23, "123qq.com"));
            boolean batch = userService.saveBatch(asList);
            System.out.println(batch);
        }
    
        // 修改数据
        @Test
        void update(){
            // 修改一条数据
            // UPDATE user SET name=?, age=?, email=? WHERE id=?
            boolean update = userService.updateById(new User(12l, "新疆", 20, "123@qq.com"));
            System.out.println(update);
        }
    
        // 删除操作
        @Test
        void remove(){
            // DELETE FROM user WHERE id=?
            // 根据Id删除
            boolean remove = userService.removeById(12l);
            System.out.println(remove);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50


    这两个方法即有添加也有修改的功能,在没有Id时,表示添加,有Id时,表示修改



    `public boolean saveOrUpdate(T entity)`
    `public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize)`
    
    • 1
    • 2

    更多操作请求参照官方文档 CRUD 接口 | MyBatis-Plus (baomidou.com)



    ServiceImpl源码

    /**
     * IService 实现类( 泛型:M 是 mapper 对象,T 是实体 )
     *
     * @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;
        }
    
        protected Class<T> entityClass = currentModelClass();
    
        @Override
        public Class<T> getEntityClass() {
            return entityClass;
        }
    
        protected Class<M> mapperClass = currentMapperClass();
    
        /**
         * 判断数据库操作是否成功
         *
         * @param result 数据库操作返回影响条数
         * @return boolean
         * @deprecated 3.3.1
         */
        @Deprecated
        protected boolean retBool(Integer result) {
            return SqlHelper.retBool(result);
        }
    
        protected Class<M> currentMapperClass() {
            return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 0);
        }
    
        protected Class<T> currentModelClass() {
            return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 1);
        }
    
    
        /**
         * 批量操作 SqlSession
         *
         * @deprecated 3.3.0
         */
        @Deprecated
        protected SqlSession sqlSessionBatch() {
            return SqlHelper.sqlSessionBatch(entityClass);
        }
    
        /**
         * 释放sqlSession
         *
         * @param sqlSession session
         * @deprecated 3.3.0
         */
        @Deprecated
        protected void closeSqlSession(SqlSession sqlSession) {
            SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(entityClass));
        }
    
        /**
         * 获取 SqlStatement
         *
         * @param sqlMethod ignore
         * @return ignore
         * @see #getSqlStatement(SqlMethod)
         * @deprecated 3.4.0
         */
        @Deprecated
        protected String sqlStatement(SqlMethod sqlMethod) {
            return SqlHelper.table(entityClass).getSqlStatement(sqlMethod.getMethod());
        }
    
        /**
         * 批量插入
         *
         * @param entityList ignore
         * @param batchSize  ignore
         * @return ignore
         */
        @Transactional(rollbackFor = Exception.class)
        @Override
        public boolean saveBatch(Collection<T> entityList, int batchSize) {
            String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
            return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
        }
    
        /**
         * 获取mapperStatementId
         *
         * @param sqlMethod 方法名
         * @return 命名id
         * @since 3.4.0
         */
        protected String getSqlStatement(SqlMethod sqlMethod) {
            return SqlHelper.getSqlStatement(mapperClass, sqlMethod);
        }
    
        /**
         * TableId 注解存在更新记录,否插入一条记录
         *
         * @param entity 实体对象
         * @return boolean
         */
        @Transactional(rollbackFor = Exception.class)
        @Override
        public boolean saveOrUpdate(T entity) {
            if (null != entity) {
                TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
                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 = tableInfo.getPropertyValue(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) {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
            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!");
            return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {
                Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
                return StringUtils.checkValNull(idVal)
                    || CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
            }, (sqlSession, entity) -> {
                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                param.put(Constants.ENTITY, entity);
                sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
            });
        }
    
        @Transactional(rollbackFor = Exception.class)
        @Override
        public boolean updateBatchById(Collection<T> entityList, int batchSize) {
            String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);
            return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                param.put(Constants.ENTITY, entity);
                sqlSession.update(sqlStatement, param);
            });
        }
    
        @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 <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
            return SqlHelper.getObject(log, listObjs(queryWrapper, mapper));
        }
    
        /**
         * 执行批量操作
         *
         * @param consumer consumer
         * @since 3.3.0
         * @deprecated 3.3.1 后面我打算移除掉 {@link #executeBatch(Collection, int, BiConsumer)} }.
         */
        @Deprecated
        protected boolean executeBatch(Consumer<SqlSession> consumer) {
            return SqlHelper.executeBatch(this.entityClass, this.log, consumer);
        }
    
        /**
         * 执行批量操作
         *
         * @param list      数据集合
         * @param batchSize 批量大小
         * @param consumer  执行方法
         * @param <E>       泛型
         * @return 操作结果
         * @since 3.3.1
         */
        protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
            return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);
        }
    
        /**
         * 执行批量操作(默认批次提交数量{@link IService#DEFAULT_BATCH_SIZE})
         *
         * @param list     数据集合
         * @param consumer 执行方法
         * @param <E>      泛型
         * @return 操作结果
         * @since 3.3.1
         */
        protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {
            return executeBatch(list, DEFAULT_BATCH_SIZE, consumer);
        }
    
        @Override
        public boolean removeById(Serializable id) {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(getEntityClass());
            if (tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) {
                return removeById(id, true);
            }
            return SqlHelper.retBool(getBaseMapper().deleteById(id));
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public boolean removeByIds(Collection<?> list) {
            if (CollectionUtils.isEmpty(list)) {
                return false;
            }
            TableInfo tableInfo = TableInfoHelper.getTableInfo(getEntityClass());
            if (tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) {
                return removeBatchByIds(list, true);
            }
            return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
        }
    
        @Override
        public boolean removeById(Serializable id, boolean useFill) {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
            if (useFill && tableInfo.isWithLogicDelete()) {
                if (!entityClass.isAssignableFrom(id.getClass())) {
                    T instance = tableInfo.newInstance();
                    tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), id);
                    return removeById(instance);
                }
            }
            return SqlHelper.retBool(getBaseMapper().deleteById(id));
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public boolean removeBatchByIds(Collection<?> list, int batchSize) {
            TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
            return removeBatchByIds(list, batchSize, tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill());
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
            String sqlStatement = getSqlStatement(SqlMethod.DELETE_BY_ID);
            TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
            return executeBatch(list, batchSize, (sqlSession, e) -> {
                if (useFill && tableInfo.isWithLogicDelete()) {
                    if (entityClass.isAssignableFrom(e.getClass())) {
                        sqlSession.update(sqlStatement, e);
                    } else {
                        T instance = tableInfo.newInstance();
                        tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), e);
                        sqlSession.update(sqlStatement, instance);
                    }
                } else {
                    sqlSession.update(sqlStatement, e);
                }
            });
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278

    IService源码

    /**
     * 顶级 Service
     *
     * @author hubin
     * @since 2018-06-23
     */
    public interface IService<T> {
    
        /**
         * 默认批次提交数量
         */
        int DEFAULT_BATCH_SIZE = 1000;
    
        /**
         * 插入一条记录(选择字段,策略插入)
         *
         * @param entity 实体对象
         */
        default boolean save(T entity) {
            return SqlHelper.retBool(getBaseMapper().insert(entity));
        }
    
        /**
         * 插入(批量)
         *
         * @param entityList 实体对象集合
         */
        @Transactional(rollbackFor = Exception.class)
        default boolean saveBatch(Collection<T> entityList) {
            return saveBatch(entityList, DEFAULT_BATCH_SIZE);
        }
    
        /**
         * 插入(批量)
         *
         * @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, DEFAULT_BATCH_SIZE);
        }
    
        /**
         * 批量修改插入
         *
         * @param entityList 实体对象集合
         * @param batchSize  每次的数量
         */
        boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
    
        /**
         * 根据 ID 删除
         *
         * @param id 主键ID
         */
        default boolean removeById(Serializable id) {
            return SqlHelper.retBool(getBaseMapper().deleteById(id));
        }
    
        /**
         * 根据 ID 删除
         *
         * @param id      主键(类型必须与实体类型字段保持一致)
         * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除)
         * @return 删除结果
         * @since 3.5.0
         */
        default boolean removeById(Serializable id, boolean useFill) {
            throw new UnsupportedOperationException("不支持的方法!");
        }
    
        /**
         * 根据实体(ID)删除
         *
         * @param entity 实体
         * @since 3.4.4
         */
        default boolean removeById(T entity) {
            return SqlHelper.retBool(getBaseMapper().deleteById(entity));
        }
    
        /**
         * 根据 columnMap 条件,删除记录
         *
         * @param columnMap 表字段 map 对象
         */
        default boolean removeByMap(Map<String, Object> columnMap) {
            Assert.notEmpty(columnMap, "error: columnMap must not be empty");
            return SqlHelper.retBool(getBaseMapper().deleteByMap(columnMap));
        }
    
        /**
         * 根据 entity 条件,删除记录
         *
         * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
         */
        default boolean remove(Wrapper<T> queryWrapper) {
            return SqlHelper.retBool(getBaseMapper().delete(queryWrapper));
        }
    
        /**
         * 删除(根据ID 批量删除)
         *
         * @param list 主键ID或实体列表
         */
        default boolean removeByIds(Collection<?> list) {
            if (CollectionUtils.isEmpty(list)) {
                return false;
            }
            return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
        }
    
        /**
         * 批量删除
         *
         * @param list    主键ID或实体列表
         * @param useFill 是否填充(为true的情况,会将入参转换实体进行delete删除)
         * @return 删除结果
         * @since 3.5.0
         */
        @Transactional(rollbackFor = Exception.class)
        default boolean removeByIds(Collection<?> list, boolean useFill) {
            if (CollectionUtils.isEmpty(list)) {
                return false;
            }
            if (useFill) {
                return removeBatchByIds(list, true);
            }
            return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
        }
    
        /**
         * 批量删除(jdbc批量提交)
         *
         * @param list 主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)
         * @return 删除结果
         * @since 3.5.0
         */
        @Transactional(rollbackFor = Exception.class)
        default boolean removeBatchByIds(Collection<?> list) {
            return removeBatchByIds(list, DEFAULT_BATCH_SIZE);
        }
    
        /**
         * 批量删除(jdbc批量提交)
         *
         * @param list    主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)
         * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除)
         * @return 删除结果
         * @since 3.5.0
         */
        @Transactional(rollbackFor = Exception.class)
        default boolean removeBatchByIds(Collection<?> list, boolean useFill) {
            return removeBatchByIds(list, DEFAULT_BATCH_SIZE, useFill);
        }
    
        /**
         * 批量删除(jdbc批量提交)
         *
         * @param list      主键ID或实体列表
         * @param batchSize 批次大小
         * @return 删除结果
         * @since 3.5.0
         */
        default boolean removeBatchByIds(Collection<?> list, int batchSize) {
            throw new UnsupportedOperationException("不支持的方法!");
        }
    
        /**
         * 批量删除(jdbc批量提交)
         *
         * @param list      主键ID或实体列表
         * @param batchSize 批次大小
         * @param useFill   是否启用填充(为true的情况,会将入参转换实体进行delete删除)
         * @return 删除结果
         * @since 3.5.0
         */
        default boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
            throw new UnsupportedOperationException("不支持的方法!");
        }
    
        /**
         * 根据 ID 选择修改
         *
         * @param entity 实体对象
         */
        default boolean updateById(T entity) {
            return SqlHelper.retBool(getBaseMapper().updateById(entity));
        }
    
        /**
         * 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
         *
         * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
         */
        default boolean update(Wrapper<T> updateWrapper) {
            return update(null, updateWrapper);
        }
    
        /**
         * 根据 whereEntity 条件,更新记录
         *
         * @param entity        实体对象
         * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
         */
        default boolean update(T entity, Wrapper<T> updateWrapper) {
            return SqlHelper.retBool(getBaseMapper().update(entity, updateWrapper));
        }
    
        /**
         * 根据ID 批量更新
         *
         * @param entityList 实体对象集合
         */
        @Transactional(rollbackFor = Exception.class)
        default boolean updateBatchById(Collection<T> entityList) {
            return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
        }
    
        /**
         * 根据ID 批量更新
         *
         * @param entityList 实体对象集合
         * @param batchSize  更新批次数量
         */
        boolean updateBatchById(Collection<T> entityList, int batchSize);
    
        /**
         * TableId 注解存在更新记录,否插入一条记录
         *
         * @param entity 实体对象
         */
        boolean saveOrUpdate(T entity);
    
        /**
         * 根据 ID 查询
         *
         * @param id 主键ID
         */
        default T getById(Serializable id) {
            return getBaseMapper().selectById(id);
        }
    
        /**
         * 查询(根据ID 批量查询)
         *
         * @param idList 主键ID列表
         */
        default List<T> listByIds(Collection<? extends Serializable> idList) {
            return getBaseMapper().selectBatchIds(idList);
        }
    
        /**
         * 查询(根据 columnMap 条件)
         *
         * @param columnMap 表字段 map 对象
         */
        default List<T> listByMap(Map<String, Object> columnMap) {
            return getBaseMapper().selectByMap(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);
    
        /**
         * 查询总记录数
         *
         * @see Wrappers#emptyWrapper()
         */
        default long count() {
            return count(Wrappers.emptyWrapper());
        }
    
        /**
         * 根据 Wrapper 条件,查询总记录数
         *
         * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
         */
        default long count(Wrapper<T> queryWrapper) {
            return SqlHelper.retCount(getBaseMapper().selectCount(queryWrapper));
        }
    
        /**
         * 查询列表
         *
         * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
         */
        default List<T> list(Wrapper<T> queryWrapper) {
            return getBaseMapper().selectList(queryWrapper);
        }
    
        /**
         * 查询所有
         *
         * @see Wrappers#emptyWrapper()
         */
        default List<T> list() {
            return list(Wrappers.emptyWrapper());
        }
    
        /**
         * 翻页查询
         *
         * @param page         翻页对象
         * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
         */
        default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
            return getBaseMapper().selectPage(page, 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}
         */
        default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
            return getBaseMapper().selectMaps(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       转换函数
         */
        default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
            return getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
        }
    
        /**
         * 翻页查询
         *
         * @param page         翻页对象
         * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
         */
        default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
            return getBaseMapper().selectMapsPage(page, 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();
    
        /**
         * 获取 entity 的 class
         *
         * @return {@link Class<T>}
         */
        Class<T> getEntityClass();
    
        /**
         * 以下的方法使用介绍:
         *
         * 一. 名称介绍
         * 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());
        }
    
        /**
         * 链式查询 lambda 式
         * kotlin 使用
         *
         * @return KtQueryWrapper 的包装类
         */
        default KtQueryChainWrapper<T> ktQuery() {
            return ChainWrappers.ktQueryChain(getBaseMapper(), getEntityClass());
        }
    
        /**
         * 链式查询 lambda 式
         * kotlin 使用
         *
         * @return KtQueryWrapper 的包装类
         */
        default KtUpdateChainWrapper<T> ktUpdate() {
            return ChainWrappers.ktUpdateChain(getBaseMapper(), getEntityClass());
        }
    
        /**
         * 链式更改 普通
         *
         * @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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533

    常用注解


    ### 1.`@TableName`

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

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


    问题:若实体类类型的类名和要操作表的表名不一致时,会出现什么问题?

    如:要操作的表名是t_user,操作实体类是User

    我们将表user更名为t_user,测试查询功能 程序抛出异常,Table ‘mybatis_plus.user’ doesn’t exist,因为现在的表名为t_user,而默认操作 的表名和实体类型的类名一致,即user表

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ArevEfMA-1656243741230)(img\1656142412193.png)]

    解决方案:

    方式一:使用@TableName作用在实体类上

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    // 设置要操作的表名 
    @TableName("t_user")
    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    方式二:在全局配置文件中配置表名的前缀,将对所有操作实体生效

    mybatis-plus:
      #设置表名前缀
      global-config:
        db-config:
          table-prefix: t_
    
    • 1
    • 2
    • 3
    • 4
    • 5

    @TableName注解相关属性

    属性类型必须指定默认值描述
    valueString否“”表名
    schemaString否“”schema
    keepGlobalPrefixboolean否false是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)
    resultMapString否“”xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)
    autoResultMapboolean否false是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)
    excludePropertyString[]否{}需要排除的属性名 @since 3.3.1

    关于 autoResultMap 的说明:

    MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上),请注意以下内容:

    因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。

    而对于 typeHandler 属性,MyBatis 只支持写在 2 个地方:

    1. 定义在 resultMap 里,作用于查询结果的封装
    2. 定义在 insert 和 update 语句的 #{property} 中的 property 后面(例:#{property,typehandler=xxx.xxx.xxx}),并且只作用于当前 设置值

    除了以上两种直接指定 typeHandler 的形式,MyBatis 有一个全局扫描自定义 typeHandler 包的配置,原理是根据您的 property 类型去找其对应的 typeHandler 并使用。


    2.@TableId

    经过以上的测试,MyBatis-Plus在实现crud时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id,所以是默认就是id作为主键列,当我们表中主键不是id时【如是uid,并且此时表中主键也是uid】将会报错,因了解决这个问题需要在实体类上使用@TableId,标明那个字段作为主键


    主键名不是id问题

    若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主 键列吗? 我们实体类中的属性id改为uid,将表中的字段id也改为uid,测试添加功能

    程序抛出异常,Field ‘uid’ doesn’t have a default value,说明MyBatis-Plus没有将uid作为主键 赋值

    在这里插入图片描述


    通过@TableId解决问题

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


    案例:

    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        // 表明`uid`是主键,而默认`id`不是主键,防止报错
        @TableId
        private Long uid;
        private String name;
        private Integer age;
        private String email;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    @TableId的value属性

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

    注意是:当我们实体类主键名和表中主键名不一致时,可以使用@TableId注解中的value属性进行指定,表中的字段【因为是在mybatis-plus中第一步是:抽取属性名作为表中的字段进行查询的】

    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        // 通过value属性指定表中的ID
        @TableId(value = "uid")
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    @TableId的type属性

    type属性用来定义主键策略【就是用来定义主键是采用雪花算法【默认方式】还是使用自动递增方式】

    type属性值

    值描述
    idType.AssIGN_ID(默认值)基于雪花算法的策略生成数据id,与数据库id是否设置自增无关【注意点】
    idType.AUTO使用数据库的自增策略,注意是:该类型请确保数据库必须设置主键自增,否则直接报错
    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        // 通过value属性指定表中的ID
        // IdType.AUTO表示设置主键自增
        @TableId(value = "uid",type = IdType.AUTO)
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    配置全局主键策略
    mybatis-plus:
      #设置表名前缀
      global-config:
        db-config:
          #配置`mybatis-plus`的主键策略
          id-type: auto
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.@FableField

    @FableFidld注解是作用实体类属性名与表中的字段名不一致时,使用的

    经过以上的测试,我们可以发现,MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和 表中的字段名一致 如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?

    解决方式一:在mybatis-plus中默认是开启驼峰命名风格的

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

    解决方式二:使用@TableField注解

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

    案例:

    例如:在表中的字段名为user_name,而在实体中属性名name,此时就可以使用@TabelField注解解决

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7zOleUUA-1656243741231)(img\1656148471751.png)]


    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        // 通过value属性指定表中的ID
        // IdType.AUTO表示设置主键自增
        @TableId(value = "uid",type = IdType.AUTO)
        private Long id;
        @TableField("user_name")
        private String name;
        private Integer age;
        private String email;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    属性类型必须指定默认值描述
    valueString否“”数据库字段名
    existboolean否true是否为数据库表字段
    conditionString否“”字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考(opens new window)
    updateString否“”字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性)
    insertStrategyEnum否FieldStrategy.DEFAULT举例:NOT_NULL insert into table_a(column) values (#{columnProperty})
    updateStrategyEnum否FieldStrategy.DEFAULT举例:IGNORED update table_a set column=#{columnProperty}
    whereStrategyEnum否FieldStrategy.DEFAULT举例:NOT_EMPTY where column=#{columnProperty}
    fillEnum否FieldFill.DEFAULT字段自动填充策略
    selectboolean否true是否进行 select 查询
    keepGlobalFormatboolean否false是否保持使用全局的 format 进行处理
    jdbcTypeJdbcType否JdbcType.UNDEFINEDJDBC 类型 (该默认值不代表会按照该值生效)
    typeHandlerClass<? extends TypeHandler>否UnknownTypeHandler.class类型处理器 (该默认值不代表会按照该值生效)
    numericScaleString否“”指定小数点后保留的位数

    关于jdbcType和typeHandler以及numericScale的说明:

    numericScale只生效于 update 的 sql. jdbcType和typeHandler如果不配合@TableName#autoResultMap = true一起使用,也只生效于 update 的 sql. 对于typeHandler如果你的字段类型和 set 进去的类型为equals关系,则只需要让你的typeHandler让 Mybatis 加载到即可,不需要使用注解



    FieldStrategy(opens new window)

    值描述
    IGNORED忽略判断
    NOT_NULL非 NULL 判断
    NOT_EMPTY非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断)
    DEFAULT追随全局配置

    FieldFill

    值描述
    DEFAULT默认不处理
    INSERT插入时填充字段
    UPDATE更新时填充字段
    INSERT_UPDATE插入和更新时填充字段

    4.逻辑删除

    物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据

    逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库 中仍旧能看到此条数据记录

    使用场景:可以进行数据恢复

    注意是:逻辑删除是假的删除,只是修改了值,在查询时,也不能查询出逻辑删除中的数据

    实现逻辑删除步骤

    第一步:在数据库表中创建逻辑删除状态列,并设置默认值为0

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvGBEBgR-1656243741232)(img\1656148995255.png)]



    第二步:在实体类添加表示逻辑删除状态属性
    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        // 通过value属性指定表中的ID
        // IdType.AUTO表示设置主键自增
        @TableId(value = "id",type = IdType.AUTO)
        private Long id;
        @TableField("user_name")
        private String name;
        private Integer age;
        private String email;
        
        // 表示逻辑删除状态属性,默认是0,1表示删除
        @TableLogic
        private Integer isDeleted;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第三步:编写测试方法

        // 测试逻辑查询,可以看出逻辑删除后,状态码会变成1,所以不会被查询出来
        @Test
        void select(){
            // 当传入null时,表示查询所有信息
            // SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
            List<User> users = userMapper.selectList(null);
            users.forEach(user-> System.out.println(user));
        }
    
        // 测试逻辑删除
        @Test
        void delete(){
            // 根据主键删除
            // UPDATE t_user SET is_deleted=1 WHERE id IN ( ? , ? , ? , ? ) AND is_deleted=0
            List<Long> asList = Arrays.asList(8l, 9l, 10l, 11l);
            int ids = userMapper.deleteBatchIds(asList);
            System.out.println(ids);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试 :测试删除功能

    真正执行的是修改 UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0

    测试查询功能,被逻辑删除的数据默认不会被查询 SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0

    条件构造器和常用接口

    1.wrapper介绍【记住下图】

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-clGcrXQQ-1656244086257)(img\1656156220168.png)]

    层级关系

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

    注意是:在mybatis-plus中条件之间默认是使用and来连接的,如果需要使用或连接时,就需要使用到or方法



    2.QueryWrapper

    A.组装查询条件

    @SpringBootTest
    public class WrapperMyBatisPlus {
    
        @Autowired
        private UserMapper userMapper;
    
        // 使用条件查询数据
        @Test
        void select(){
            // 查询用户名包含`a`,年龄在20到30之间,网邮箱信息不为null的用户信息
            // SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 
            // AND (user_name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("user_name","a")
                    .between("age",20,30)
                    .isNotNull("email");
    
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(user-> System.out.println(user));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    B.组装排序条件

        // 排序查询
        @Test
        void orderSelect(){
            // 查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
            // SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.orderByDesc("age")
                    .orderByAsc("id");
    
            // 调用方法
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    C.组装删除条件

        // 组装删除条件
        @Test
        void delete(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            // 删除邮箱为空并且用户名为 大同 的用户信息
            // 由于添加是逻辑删除,所以sql语句如下 :
            // UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL AND user_name = ?)
            queryWrapper.isNull("email")
                    .eq("user_name","大同");
    
            //条件构造器也可以构建删除语句的条件
            int delete = userMapper.delete(queryWrapper);
    
            System.out.println(delete);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    D.条件的优先级【必须理解重点】

    例如1:将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改

    例如2: 将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r3EbdI7U-1656244086258)(D:\typora笔记\mybatisplus\img\1656160680935.png)]

    案例1

        @Test
        void test1(){
            // 将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
            // 使用QueryWrapper封装用户信息,用于查询,查询到再进行修改
            // UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (age > ? AND user_name LIKE ? OR email IS NULL)
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.gt("age",20)
                    .like("user_name","a")
                    .or()
                    .isNull("email");
    
            // 封装需要修改的信息
            User user = new User();
            user.setName("西藏");// 将用户名修改成西藏
            user.setEmail("123@qq.com");// 将邮箱修改成123@qq.com
            int result = userMapper.update(user, queryWrapper);
            System.out.println(result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    案例2

        @Test
        void test2(){
            //  将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
            //==>  Preparing: UPDATE t_user SET user_name=?, age=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
            //==> Parameters: 杭州(String), 18(Integer), %a%(String), 20(Integer)
            
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("user_name","a")
                    .and(i->i.gt("age",20)
                    .or()
                    .isNull("email"));
    
            // 封装要修改成的信息
            User user = new User();
            user.setName("杭州");
            user.setAge(18);
    
            int update = userMapper.update(user, queryWrapper);
    
            System.out.println(update);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ju9e3HHE-1656244086258)(img\1656161345042.png)]

    说明在mybatis-plus中如果需要优先执行,则可以使用and或or【原因是它们本质就是lambda表达式,在mybatis-plus中lambda优先执行】

    UPDATE t_user SET user_name=?, age=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))

    可以看出:有小括号的会优先执行该条件,在sql中会优先执行有小括号中的条件,小括号越多,优先级越高


    E.组装SELECT子句

    当有很多字段时,此方法可以设置只查询字段,如现在只需要查询`user-name`   `age`  `email`,只需要传入这个三个参数即可
    public QueryWrapper<T> select(String... columns) {}
    
    • 1
    • 2

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s5LxI33w-1656244086259)(D:\typora笔记\mybatisplus\img\1656162773011.png)]

        @Test
        void test3(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            // 现在只需要查询`user-name`  `age`  `email`
            // SELECT user_name,age,email FROM t_user WHERE is_deleted=0
            queryWrapper.select("user_name","age","email");
    
            List<User> list = userMapper.selectList(queryWrapper);
            list.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    F.子查询

    子查询使用是``

    #例:查询id小于等于9的用户信息
    SELECT * FROM t_user WHERE id in (
    	SELECT id FROM WHERE id<=9
    );
    
    • 1
    • 2
    • 3
    • 4

    案例

        @Test
        void test4(){
            /**
             * #例:查询id小于等于9的用户信息
             * SELECT * FROM t_user WHERE id in (
             * 	SELECT id FROM WHERE id<=9
             * );
             */
    
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            // 第一个参数:id是子查询返回的字段,第二个参数该值来至于该sql语句
            // SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (id IN (select id from t_user where id<=9))
            queryWrapper.inSql("id","select id from t_user where id<=9");
    
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意是:QueryWrapper即可完成查询功能的条件封装,也可以完成修改功能的条件封装,可以在修改功能中,还需要创建对应实体类封装修改信息,可以直接UpdateWrapper可以直接调用方法设置修改那些字段,减去创建对应实体类封装修改信息的步骤


    3.@UpdateWrapper

    例如2: 将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改

    使用@UpdateWrapper简化创建对应 实体类封装修改信息的步骤

    案例:

        @Test
        void test5(){
            //例如2: 将用户名中包含有b并且(年龄大于20或邮箱为null)的用户信息修改
            UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
            updateWrapper.like("user_name","b")
                    .and(i->i.gt("age",20)
                    .or()
                    .isNull("email"));
    
            // 设置修改的字段
            updateWrapper.set("email","123@qq.com");//表示将email字段修改为123@qq.com
            updateWrapper.set("user_name","洛朗");//表示user_name字段修改为洛朗
    
            // 由于可以直接设置修改的字段,所以在实体类传入null,从而减去创建操作对应实体类封装数据步骤
            /**
             * ==>  Preparing: UPDATE t_user SET email=?,user_name=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
             * ==> Parameters: 123@qq.com(String), 洛朗(String), %b%(String), 20(Integer)
             */
            userMapper.update(null,updateWrapper);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    模拟开发中组装条件的情况
    例如:判断用户传入的`user_name`不能为空字符串或`null`或空白符
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kdkjaL56-1656244086259)(img\1656204011016.png)]

        @Test
        void test6(){
            String user_name = "";
            Integer ageBegin = 18;
            Integer ageEnd = 108;
    
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    
            // 判断user_name 不为空字符串或null或空白符
            /*
            ==>  Preparing: SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (age >= ? AND age <= ?)
            ==> Parameters: 18(Integer), 108(Integer)
             */
            if(StringUtils.isNotBlank(user_name)){
                queryWrapper.like("user_name",user_name);
            }
    
            // 判断是否为空
            if (ageBegin!=null){
                queryWrapper.ge("age",ageBegin);
            }
    
            // 判断是否为空
            if (ageEnd!=null){
                queryWrapper.le("age",ageEnd);
            }
    
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(System.out::println);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    condition

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

        @Test
        void test7(){
    
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            String user_name = "";
            Integer ageBegin = 18;
            Integer ageEnd = 108;
    
            /**
             * ==>  Preparing: SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (age >= ? AND age <= ?)
             * ==> Parameters: 18(Integer), 108(Integer)
             */
            queryWrapper.like(StringUtils.isNotBlank(user_name),"user_name",user_name)
                    .ge(ageBegin!=null,"age",ageBegin)
                    .le(ageEnd!=null,"age",ageEnd);
    
            List<User> users = userMapper.selectList(queryWrapper);
            users.forEach(System.out::println);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.LambdaQueryWrapper

    LambdaQueryWrapper该可以使用函数式编程,可以防止字段名写错,引起报错问题

    可以防止字段名写错,引起报错问题public final LambdaQueryWrapper<T> select(SFunction<T, ?>... columns) {}
    如:可以在`(SFunction<T, ?>... columns)`中传入,相关的字段名,可以防止字段名写错,引起报错问题
    
    • 1
    • 2
        @Test
        void test8(){
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            String user_name = "";
            Integer ageBegin = 18;
            Integer ageEnd = 108;
    
            /**
             * ==>  Preparing: SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (age >= ? AND age <= ?)
             * ==> Parameters: 18(Integer), 108(Integer)
             */
            lambdaQueryWrapper.like(StringUtils.isNotBlank(user_name),User::getName,user_name)
                    .ge(ageBegin!=null,User::getAge,ageBegin)
                    .le(ageEnd!=null,User::getAge,ageEnd);
    
            List<User> users = userMapper.selectList(lambdaQueryWrapper);
            users.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    5.LambdaUpdateWrapper

    LambdaUpdateWrapper该可以使用函数式编程,可以防止字段名写错,引起报错问题


        @Test
        void test9(){
            //例如2: 将用户名中包含有b并且(年龄大于20或邮箱为null)的用户信息修改
            /**
             * ==>  Preparing: SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
             * ==> Parameters: %b%(String), 20(Integer)
             */
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.like(User::getName,"b")
                    .and(i->i.gt(User::getAge,20)
                    .or()
                    .isNull(User::getEmail));
    
            List<User> users = userMapper.selectList(lambdaUpdateWrapper);
            users.forEach(System.out::println);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    LambdaQueryWrapper和LambdaUpdateWrapper可以在(SFunction<T, ?>... columns)中传入,相关的字段名,可以防止字段名写错,引起报错问题


    /**
     * Lambda 语法使用 Wrapper
     *
     * @author hubin miemie HCL
     * @since 2017-05-26
     */
    @SuppressWarnings("serial")
    public class LambdaQueryWrapper<T> extends AbstractLambdaWrapper<T, LambdaQueryWrapper<T>>
        implements Query<LambdaQueryWrapper<T>, T, SFunction<T, ?>> {
    
        /**
         * 查询字段
         */
        private SharedString sqlSelect = new SharedString();
    
        public LambdaQueryWrapper() {
            this((T) null);
        }
    
        public LambdaQueryWrapper(T entity) {
            super.setEntity(entity);
            super.initNeed();
        }
    
        public LambdaQueryWrapper(Class<T> entityClass) {
            super.setEntityClass(entityClass);
            super.initNeed();
        }
    
        LambdaQueryWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,
                           Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
                           SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
            super.setEntity(entity);
            super.setEntityClass(entityClass);
            this.paramNameSeq = paramNameSeq;
            this.paramNameValuePairs = paramNameValuePairs;
            this.expression = mergeSegments;
            this.sqlSelect = sqlSelect;
            this.paramAlias = paramAlias;
            this.lastSql = lastSql;
            this.sqlComment = sqlComment;
            this.sqlFirst = sqlFirst;
        }
    
        /**
         * SELECT 部分 SQL 设置
         *
         * @param columns 查询字段
         */
        @SafeVarargs
        @Override
        public final LambdaQueryWrapper<T> select(SFunction<T, ?>... columns) {
            if (ArrayUtils.isNotEmpty(columns)) {
                this.sqlSelect.setStringValue(columnsToString(false, columns));
            }
            return typedThis;
        }
    
        /**
         * 过滤查询的字段信息(主键除外!)
         * <p>例1: 只要 java 字段名以 "test" 开头的             -> select(i -&gt; i.getProperty().startsWith("test"))</p>
         * <p>例2: 只要 java 字段属性是 CharSequence 类型的     -> select(TableFieldInfo::isCharSequence)</p>
         * <p>例3: 只要 java 字段没有填充策略的                 -> select(i -&gt; i.getFieldFill() == FieldFill.DEFAULT)</p>
         * <p>例4: 要全部字段                                   -> select(i -&gt; true)</p>
         * <p>例5: 只要主键字段                                 -> select(i -&gt; false)</p>
         *
         * @param predicate 过滤方式
         * @return this
         */
        @Override
        public LambdaQueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
            if (entityClass == null) {
                entityClass = getEntityClass();
            } else {
                setEntityClass(entityClass);
            }
            Assert.notNull(entityClass, "entityClass can not be null");
            this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));
            return typedThis;
        }
    
        @Override
        public String getSqlSelect() {
            return sqlSelect.getStringValue();
        }
    
        /**
         * 用于生成嵌套 sql
         * <p>故 sqlSelect 不向下传递</p>
         */
        @Override
        protected LambdaQueryWrapper<T> instance() {
            return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,
                new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
        }
    
        @Override
        public void clear() {
            super.clear();
            sqlSelect.toNull();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    插件

    ctlr+p可以查看当前方法需要参数

    1.分页插件

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


    实现分页需要两步:

    第一步:添加配置类
    @Configuration
    public class MyBatisPlusConfig {
    
        // 配置mybatis-plus拦截器
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 创建分页拦截器,并指定数据库类型,如使用是mysql
            PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
            // 添加分页拦截器
            interceptor.addInnerInterceptor(innerInterceptor);
    
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第二步:测试

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fY5FR0XM-1656244599643)(img\1656209520569.png)]

    @SpringBootTest
    public class MyBatisPlusPage {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void test1(){
            // 设置分页参数
            Page<User> page = new Page<>(1,5);
            userMapper.selectPage(page,null);
    
            // 获取分页数据
            //  SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?
            List<User> records = page.getRecords();// 当前页数据
            records.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.hasNext());
            System.out.println("是否有下一页:"+page.hasNext());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.自定义分页

    场景:当我们需要在自定义的方法,使用到mybatis-plus的分页插件时,就需要自定义分页方法,

    自定义分页方法:必须保证第一个参数是mybatis-plus所提供的分页对象

    第一步:在自定义UserMapper接口中定义方法

    案例: 通过年龄查询用户信息并分页

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    
    
        /**
         *  根据年龄查询用户信息,并分页
         * @param page 分页对象,xml中可以从里面进行聚会,传递参数Page自动分页,必须放在第一位参数
         * @param age 年龄
         * @return
         */
        Page<User> selectPageVo(@Param("page")Page<User> page,@Param("age") Integer age);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们可以参考官方编写的方法

        /**
         * 根据 entity 条件,查询全部记录(并翻页)
         *
         * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
         * @param queryWrapper 实体对象封装操作类(可以为 null)
         */
        <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
        /**
         * 根据 Wrapper 条件,查询全部记录(并翻页)
         *
         * @param page         分页查询条件
         * @param queryWrapper 实体对象封装操作类
         */
        <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    第三步:定义UserMapper.xml文件
    <!--
    Page<User> selectPageVo(@Param("page")Page<User> page,@Param("age") Integer age);
    -->
    
        <select id="selectPageVo" resultType="User">
            select * from t_user where age>#{age}  // 一定不能加分号,注意
        </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    第四步:测试
        @Test
        void test2(){
            // 设置分页参数
            Page<User> page = new Page<>(1,5);
            // 查询年龄大于20的用户信息,并实现分页
            // select * from t_user where age>? LIMIT ?
            userMapper.selectPageVo(page,20);
            // 获取分页数据
            //  SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?
            List<User> records = page.getRecords();// 当前页数据
            records.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.hasNext());
            System.out.println("是否有下一页:"+page.hasNext());
    
            /**
             * ==>  Preparing: SELECT COUNT(*) AS total FROM t_user WHERE age > ?
             * ==> Parameters: 20(Integer)
             * <==    Columns: total
             * <==        Row: 12
             * <==      Total: 1
             * ==>  Preparing: select * from t_user where age>? LIMIT ?
             * ==> Parameters: 20(Integer), 5(Long)
             * <==    Columns: id, user_name, age, email, is_deleted
             * <==        Row: 3, Tom, 28, test3@baomidou.com, 0
             * <==        Row: 4, 西藏, 21, 123@qq.com, 0
             * <==        Row: 5, 洛朗, 24, 123@qq.com, 0
             * <==        Row: 8, 海康, 23, 123@qq.com, 1
             * <==        Row: 9, 湛江, 21, 123@qq.com, 1
             * <==      Total: 5
             * Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fff46bf]
             * User(id=3, name=null, age=28, email=test3@baomidou.com, isDeleted=0)
             * User(id=4, name=null, age=21, email=123@qq.com, isDeleted=0)
             * User(id=5, name=null, age=24, email=123@qq.com, isDeleted=0)
             * User(id=8, name=null, age=23, email=123@qq.com, isDeleted=1)
             * User(id=9, name=null, age=21, email=123@qq.com, isDeleted=1)
             * 当前页:1
             * 每页显示的条数:5
             * 总记录数:12
             * 总页数:3
             * 是否有上一页:true
             * 是否有下一页:true
             */
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    IPage源码

    /**
     * 分页 Page 对象接口
     */
    public interface IPage<T> extends Serializable {
    
        /**
         * 获取排序信息,排序的字段和正反序
         *
         * @return 排序信息
         */
        List<OrderItem> orders();
    
        /**
         * 自动优化 COUNT SQL【 默认:true 】
         *
         * @return true 是 / false 否
         */
        default boolean optimizeCountSql() {
            return true;
        }
    
        /**
         * {@link com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor#isOptimizeJoin()}
         * 两个参数都为 true 才会进行sql处理
         *
         * @return true 是 / false 否
         * @since 3.4.4 @2021-09-13
         */
        default boolean optimizeJoinOfCountSql() {
            return true;
        }
    
        /**
         * 进行 count 查询 【 默认: true 】
         *
         * @return true 是 / false 否
         */
        default boolean searchCount() {
            return true;
        }
    
        /**
         * 计算当前分页偏移量
         */
        default long offset() {
            long current = getCurrent();
            if (current <= 1L) {
                return 0L;
            }
            return Math.max((current - 1) * getSize(), 0L);
        }
    
        /**
         * 最大每页分页数限制,优先级高于分页插件内的 maxLimit
         *
         * @since 3.4.0 @2020-07-17
         */
        default Long maxLimit() {
            return null;
        }
    
        /**
         * 当前分页总页数
         */
        default long getPages() {
            if (getSize() == 0) {
                return 0L;
            }
            long pages = getTotal() / getSize();
            if (getTotal() % getSize() != 0) {
                pages++;
            }
            return pages;
        }
    
        /**
         * 内部什么也不干
         * <p>只是为了 json 反序列化时不报错</p>
         */
        default IPage<T> setPages(long pages) {
            // to do nothing
            return this;
        }
    
        /**
         * 分页记录列表
         *
         * @return 分页对象记录列表
         */
        List<T> getRecords();
    
        /**
         * 设置分页记录列表
         */
        IPage<T> setRecords(List<T> records);
    
        /**
         * 当前满足条件总行数
         *
         * @return 总条数
         */
        long getTotal();
    
        /**
         * 设置当前满足条件总行数
         */
        IPage<T> setTotal(long total);
    
        /**
         * 获取每页显示条数
         *
         * @return 每页显示条数
         */
        long getSize();
    
        /**
         * 设置每页显示条数
         */
        IPage<T> setSize(long size);
    
        /**
         * 当前页
         *
         * @return 当前页
         */
        long getCurrent();
    
        /**
         * 设置当前页
         */
        IPage<T> setCurrent(long current);
    
        /**
         * IPage 的泛型转换
         *
         * @param mapper 转换函数
         * @param <R>    转换后的泛型
         * @return 转换泛型后的 IPage
         */
        @SuppressWarnings("unchecked")
        default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
            List<R> collect = this.getRecords().stream().map(mapper).collect(toList());
            return ((IPage<R>) this).setRecords(collect);
        }
    
        /**
         * 老分页插件不支持
         * <p>
         * MappedStatement 的 id
         *
         * @return id
         * @since 3.4.0 @2020-06-19
         */
        default String countId() {
            return null;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158

    Page源码

    /**
     * 简单分页模型
     */
    public class Page<T> implements IPage<T> {
    
        private static final long serialVersionUID = 8545996863226528798L;
    
        /**
         * 查询数据列表
         */
        protected List<T> records = Collections.emptyList();
    
        /**
         * 总数
         */
        protected long total = 0;
        /**
         * 每页显示条数,默认 10
         */
        protected long size = 10;
    
        /**
         * 当前页
         */
        protected long current = 1;
    
        /**
         * 排序字段信息
         */
        @Setter
        protected List<OrderItem> orders = new ArrayList<>();
    
        /**
         * 自动优化 COUNT SQL
         */
        protected boolean optimizeCountSql = true;
        /**
         * 是否进行 count 查询
         */
        protected boolean searchCount = true;
        /**
         * {@link #optimizeJoinOfCountSql()}
         */
        @Setter
        protected boolean optimizeJoinOfCountSql = true;
        /**
         * countId
         */
        @Setter
        protected String countId;
        /**
         * countId
         */
        @Setter
        protected Long maxLimit;
    
        public Page() {
        }
    
        /**
         * 分页构造函数
         *
         * @param current 当前页
         * @param size    每页显示条数
         */
        public Page(long current, long size) {
            this(current, size, 0);
        }
    
        public Page(long current, long size, long total) {
            this(current, size, total, true);
        }
    
        public Page(long current, long size, boolean searchCount) {
            this(current, size, 0, searchCount);
        }
    
        public Page(long current, long size, long total, boolean searchCount) {
            if (current > 1) {
                this.current = current;
            }
            this.size = size;
            this.total = total;
            this.searchCount = searchCount;
        }
    
        /**
         * 是否存在上一页
         *
         * @return true / false
         */
        public boolean hasPrevious() {
            return this.current > 1;
        }
    
        /**
         * 是否存在下一页
         *
         * @return true / false
         */
        public boolean hasNext() {
            return this.current < this.getPages();
        }
    
        @Override
        public List<T> getRecords() {
            return this.records;
        }
    
        @Override
        public Page<T> setRecords(List<T> records) {
            this.records = records;
            return this;
        }
    
        @Override
        public long getTotal() {
            return this.total;
        }
    
        @Override
        public Page<T> setTotal(long total) {
            this.total = total;
            return this;
        }
    
        @Override
        public long getSize() {
            return this.size;
        }
    
        @Override
        public Page<T> setSize(long size) {
            this.size = size;
            return this;
        }
    
        @Override
        public long getCurrent() {
            return this.current;
        }
    
        @Override
        public Page<T> setCurrent(long current) {
            this.current = current;
            return this;
        }
    
        @Override
        public String countId() {
            return this.countId;
        }
    
        @Override
        public Long maxLimit() {
            return this.maxLimit;
        }
    
        /**
         * 查找 order 中正序排序的字段数组
         *
         * @param filter 过滤器
         * @return 返回正序排列的字段数组
         */
        private String[] mapOrderToArray(Predicate<OrderItem> filter) {
            List<String> columns = new ArrayList<>(orders.size());
            orders.forEach(i -> {
                if (filter.test(i)) {
                    columns.add(i.getColumn());
                }
            });
            return columns.toArray(new String[0]);
        }
    
        /**
         * 移除符合条件的条件
         *
         * @param filter 条件判断
         */
        private void removeOrder(Predicate<OrderItem> filter) {
            for (int i = orders.size() - 1; i >= 0; i--) {
                if (filter.test(orders.get(i))) {
                    orders.remove(i);
                }
            }
        }
    
        /**
         * 添加新的排序条件,构造条件可以使用工厂:{@link OrderItem#build(String, boolean)}
         *
         * @param items 条件
         * @return 返回分页参数本身
         */
        public Page<T> addOrder(OrderItem... items) {
            orders.addAll(Arrays.asList(items));
            return this;
        }
    
        /**
         * 添加新的排序条件,构造条件可以使用工厂:{@link OrderItem#build(String, boolean)}
         *
         * @param items 条件
         * @return 返回分页参数本身
         */
        public Page<T> addOrder(List<OrderItem> items) {
            orders.addAll(items);
            return this;
        }
    
        @Override
        public List<OrderItem> orders() {
            return this.orders;
        }
    
        @Override
        public boolean optimizeCountSql() {
            return optimizeCountSql;
        }
    
        public static <T> Page<T> of(long current, long size, long total, boolean searchCount) {
            return new Page<>(current, size, total, searchCount);
        }
    
        @Override
        public boolean optimizeJoinOfCountSql() {
            return optimizeJoinOfCountSql;
        }
    
        public Page<T> setSearchCount(boolean searchCount) {
            this.searchCount = searchCount;
            return this;
        }
    
        public Page<T> setOptimizeCountSql(boolean optimizeCountSql) {
            this.optimizeCountSql = optimizeCountSql;
            return this;
        }
    
        @Override
        public long getPages() {
            // 解决 github issues/3208
            return IPage.super.getPages();
        }
    
        /* --------------- 以下为静态构造方式 --------------- */
        public static <T> Page<T> of(long current, long size) {
            return of(current, size, 0);
        }
    
        public static <T> Page<T> of(long current, long size, long total) {
            return of(current, size, total, true);
        }
    
        public static <T> Page<T> of(long current, long size, boolean searchCount) {
            return of(current, size, 0, searchCount);
        }
    
        @Override
        public boolean searchCount() {
            if (total < 0) {
                return false;
            }
            return searchCount;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266

    乐观锁和悲观锁

    1.简介

    悲观锁:当一个用户获取到锁,这个用户操作完成后,释放锁后,其他用户获取到锁时,才能进行操作,就是就是当一个获取到锁后操作业务,其他用户只能等待

    乐观锁:就是通过版本号操作,多个用户可以同时进行操作,当版本不同时,则修改会失败

    场景:

    一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。

    乐观锁和悲观锁:

    上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过 了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证 最终的价格是120元。

    2.模拟修改冲突

    第一步:数据库中增加商品表

    CREATE TABLE t_product
    (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
    price INT(11) DEFAULT 0 COMMENT '价格',
    VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
    PRIMARY KEY (id)
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    第二步:添加数据

    INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
    
    • 1

    第三步:创建实体类

    @Data
    @TableName("t_product")
    public class Product {
            private Long id;
            private String name;
            private Integer price;
            private Integer version;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第四步:编写ProductMapper接口

    @Mapper
    public interface ProductMapper extends BaseMapper<Product> {
    }
    
    • 1
    • 2
    • 3

    第五步:测试

        @Test
        void test3(){
            //1、小李
            Product p1 = productMapper.selectById(1L);
            System.out.println("小李取出的价格:" + p1.getPrice());
            //2、小王
            Product p2 = productMapper.selectById(1L);
            System.out.println("小王取出的价格:" + p2.getPrice());
    
            //3、小李将价格加了50元,存入了数据库
            p1.setPrice(p1.getPrice() + 50);
            int result1 = productMapper.updateById(p1);
            System.out.println("小李修改结果:" + result1);
            //4、小王将商品减了30元,存入了数据库
            p2.setPrice(p2.getPrice() - 30);
            int result2 = productMapper.updateById(p2);
            System.out.println("小王修改结果:" + result2);
            //最后的结果
            Product p3 = productMapper.selectById(1L);
            //价格覆盖,最后的结果:70,所以在第三次查询最终的价格为:70
            System.out.println("最后的结果:" + p3.getPrice());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.乐观锁实现流程【重点】

    第一步:在数据库中添加version字段

    取出记录时,获取当前version

    SELECT id,`name`,price,`version` FROM product WHERE id=1
    
    • 1

    更新时,version+1,如果where语句中的version版本号不对,则更新失败

    UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND
    `version`=1
    
    • 1
    • 2

    说明:在Mybatis-plus中要想乐观锁生效,在操作前,第一步:必须先查询获取version版本号,第二步:再进行业务操作

    第二步:在实现类中的version字段上添加@Version注解
    @Data
    @TableName("t_product")
    public class Product {
            private Long id;
            private String name;
            private Integer price;
            // 表示版本号
            @Version
            private Integer version;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    第三步:创建一个配置类,添加乐观锁插件
    @Configuration
    public class MyBatisPlusConfig {
    
        // 配置mybatis-plus拦截器
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 添加乐观锁插件
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    第四步:测试
        @Test
        public void testConcurrentVersionUpdate() {
            //小李取数据
            Product p1 = productMapper.selectById(1L);
            //小王取数据
            Product p2 = productMapper.selectById(1L);
            //小李修改 + 50
            p1.setPrice(p1.getPrice() + 50);
            int result1 = productMapper.updateById(p1);
            System.out.println("小李修改的结果:" + result1);
            //小王修改 - 30
            p2.setPrice(p2.getPrice() - 30);
            int result2 = productMapper.updateById(p2);
            System.out.println("小王修改的结果:" + result2);
            if(result2 == 0){
                //失败重试,重新获取version并更新
                p2 = productMapper.selectById(1L);
                p2.setPrice(p2.getPrice() - 30);
                result2 = productMapper.updateById(p2);
            }
            System.out.println("小王修改重试的结果:" + result2);
            //老板看价格
            Product p3 = productMapper.selectById(1L);
            System.out.println("老板看价格:" + p3.getPrice());
            // 老板看价格:120
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    通用枚举

    场景:

    表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用mybatis-plus的通用枚举来实现

    第一步:在数据库中添加字段sex


    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VEjMssvL-1656244750333)(img\1656216256020.png)]


    第二步:创建枚举类,并且使用@EnumValue注解,那个字段添加到数据库中


    @Getter//由于是枚举所以,只需要提交get方法,获取即可
    public enum SexEnum {
    
        // 创建枚举对象
        MALE(0,"男"),
        FEMALE(1,"女");
    
        @EnumValue
        private Integer sex;
        private String sexName;
    
        // 提供有参构造器
         SexEnum(Integer sex, String sexName) {
            this.sex = sex;
            this.sexName = sexName;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    第三步:在实体类中添加枚举字段

    public class User {
        // 注意是:由于在表中使用的bigint类型,所以需要使用long类型
        // 通过value属性指定表中的ID
        // IdType.AUTO表示设置主键自增
        @TableId(value = "id",type = IdType.AUTO)
        private Long id;
        @TableField("user_name")
        private String name;
        private Integer age;
        private String email;
    
        // 表示逻辑删除状态属性,默认是0,1表示删除
        @TableLogic
        private Integer isDeleted;
        // 添加枚举属性
        private SexEnum sex;
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    第四步:配置扫描通用枚举

    mybatis-plus:
      type-enums-package: com.haikang.plus.myenum
    
    • 1
    • 2

    第五步:测试

    @SpringBootTest
    public class EnumTest {
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void test1(){
            User user = new User();
            user.setName("大朗");
            user.setId(20l);
            user.setAge(23);
            user.setSex(SexEnum.MALE);
    
            /**
             * ==>  Preparing: INSERT INTO t_user ( id, user_name, age, sex ) VALUES ( ?, ?, ?, ? )
             * ==> Parameters: 20(Long), 大朗(String), 23(Integer), 0(Integer)
             * <==    Updates: 1
             */
    
            int result = userMapper.insert(user);
            System.out.println("结果为:"+result);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    代码生成器

    第一步:添加依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
            </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第二步:创建类

    public class FastAutoGeneratorTest {
    	public static void main(String[] args) {
    
            FastAutoGenerator.create("url", "username", "password")
            .globalConfig(builder -> {
                builder.author("haikang") // 设置作者
                    .enableSwagger() // 开启 swagger 模式
                    .fileOverride() // 覆盖已生成文件
                    .outputDir("D://mybatis_plus"); // 指定输出目录
            })
            .packageConfig(builder -> {
                builder.parent("com.haikang.mybatis") // 设置父包名
                    .moduleName("haikang") // 设置父包模块名
                    .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus")); // 设置mapperXml生成路径
            })
            .strategyConfig(builder -> {
                builder.addInclude("t_simple") // 设置需要生成的表名
                    .addTablePrefix("t_", "c_"); // 设置过滤表前缀
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
            .execute();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    多数据源

    特性:

    • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
    • 支持数据库敏感配置信息 加密 ENC()。
    • 支持每个数据库独立初始化表结构schema和数据库database。
    • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
    • 支持 自定义注解 ,需继承DS(3.2.0+)。
    • 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
    • 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
    • 提供 自定义数据源来源 方案(如全从数据库加载)。
    • 提供项目启动后 动态增加移除数据源 方案。
    • 提供Mybatis环境下的 纯读写分离 方案。
    • 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
    • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
    • 提供 基于seata的分布式事务方案。
    • 提供 本地多数据源事务方案。

    场景:

    适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景,其他场景类似 场景说明: 我们创建两个库,分别为:mybatis_plus(以前的库不动)与mybatis_plus_1(新建),将 mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例 分别获取用户数据与商品数据,如果获取到说明多库模拟成功

    1.创建数据库及

    创建数据库mybatis_plus_1和表product

    CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
    use `mybatis_plus_1`;
    CREATE TABLE product
    (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
    price INT(11) DEFAULT 0 COMMENT '价格',
    version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
    PRIMARY KEY (id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
    
    #删除mybatis_plus库product表
    use mybatis_plus;
    DROP TABLE IF EXISTS product;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.引入依赖

            mybatis-plus依赖
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.1</version>
            </dependency>
    
    
            多数据源依赖
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>3.5.0</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.配置多数据源

    注意是:如果在项目中有之前的数据源的配置,想要测试该功能

    注释掉之前的数据库连接,添加新配置

    #配置多数据源信息
    spring:
      datasource:
        dynamic:
          primary: master #设置默认的数据源或者数据源组,默认值即为master
          strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
          datasource:
            master:
              url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
              username: root
              password: root
              driver-class-name: com.mysql.jdbc.Driver
            slave_1:
              url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=false
              username: root
              password: root
              driver-class-name: com.mysql.jdbc.Driver
            #......省略
            #以上会配置一个默认库master,一个组slave下有两个子库slave_1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.创建对应的Mapper接口和xml文件

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    }
    
    
    @Mapper
    public interface ProductMapper extends BaseMapper<Product> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    <?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.haikang.datasource.mapper.UserMapper">
    
    </mapper>
    
    
    <?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.haikang.datasource.mapper.ProductMapper">
    
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5.创建对应的Service接口和实现类

    并且指定该组件使用@DS(指定使用那个数据源)

    使用 @DS 切换数据源。

    @DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。

    注解结果
    没有@DS默认数据源
    @DS(“dsName”)dsName可以为组名也可以为具体某个库的名称
    public interface UserService extends IService<User> {
    }
    
    
    @Service
    @DS("master")
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }
    
    
    public interface ProductService extends IService<Product> {
    }
    
    @Service
    @DS("slave_1")
    public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6.测试

    @SpringBootTest
    class MybatisPlusDatasourceApplicationTests {
    
        @Autowired
        private UserService userService;
        @Autowired
        private ProductService productService;
    
        @Test
        void contextLoads() {
    
            /**
             * ==>  Preparing: SELECT id,user_name,age,email,sex,is_deleted FROM t_user WHERE id=?
             * ==> Parameters: 1(Integer)
             * <==    Columns: id, user_name, age, email, sex, is_deleted
             * <==        Row: 1, Jone, 18, test1@baomidou.com, 0, 0
             * <==      Total: 1
             */
            System.out.println(userService.getById(1));
    
            /**
             * ==>  Preparing: SELECT id,name,price,version FROM product WHERE id=?
             * ==> Parameters: 1(Integer)
             * <==    Columns: id, name, price, version
             * <==        Row: 1, 外星人笔记本, 100, 0
             * <==      Total: 1
             */
            System.out.println(productService.getById(1));
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32



    MyBatisX快速开发插件

    第一步:创建SpringBoot工程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAygYq0K-1656244970222)(D:\typora笔记\mybatisplus\img\1656233065158.png)]


    注意是:由于在创建SpringBoot工程时,我们没有添加mybatis-plus场景启动器,所以需要我们手动添加


           mybatis-plus场景启动器
           <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.1</version>
            </dependency>
            多数据源场景启动器
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>3.5.0</version>
            </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第二步:配置相关的数据源配置

    #修改端口号
    server:
      port: 80
    
    #配置数据源信息
    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
        username: root
        password: root
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第三步:下载MybatisX

    安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。



    在这里插入图片描述


    第四步:在IDEA连接数据库生成对应文件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BquHQP7a-1656244970223)(D:\typora笔记\mybatisplus\img\1656233884595.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OvFBjoHO-1656244970224)(D:\typora笔记\mybatisplus\img\1656233927506.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIhwGmld-1656244970224)(D:\typora笔记\mybatisplus\img\1656234034622.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0m8CNyEO-1656244970224)(D:\typora笔记\mybatisplus\img\1656234710865.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H1rXFgkz-1656244970225)(D:\typora笔记\mybatisplus\img\1656234722602.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4a4R9tZG-1656244970225)(D:\typora笔记\mybatisplus\img\1656234767208.png)]



    第五步:编写方法进行测试


    编写方法名后,alt+enter自动生成方法返回值及xml文件



    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6DBGNnA-1656244970225)(D:\typora笔记\mybatisplus\img\1656235040555.png)]


    A.添加方法测试

    在这里插入图片描述

        // 添加方法
        int insertSelective(User user);
    
    • 1
    • 2
        <insert id="insertSelective">
            insert into t_user
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="id != null">id,</if>
                <if test="userName != null">user_name,</if>
                <if test="age != null">age,</if>
                <if test="email != null">email,</if>
                <if test="sex != null">sex,</if>
                <if test="isDeleted != null">is_deleted,</if>
            </trim>
            values
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="id != null">#{id,jdbcType=BIGINT},</if>
                <if test="userName != null">#{userName,jdbcType=VARCHAR},</if>
                <if test="age != null">#{age,jdbcType=INTEGER},</if>
                <if test="email != null">#{email,jdbcType=VARCHAR},</if>
                <if test="sex != null">#{sex,jdbcType=INTEGER},</if>
                <if test="isDeleted != null">#{isDeleted,jdbcType=INTEGER},</if>
            </trim>
        </insert>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    编写测试方法

    @SpringBootTest
    class SpringbootMybatisXApplicationTests {
    
        @Autowired
        UserMapper userMapper;
    
        @Test
        void insert() {
            User user = new User(21l,"西京",22,"123@qq.com",1,0);
            int selective = userMapper.insertSelective(user);
            /**
             * ==>  Preparing: insert into t_user ( id, user_name, age, email, sex, is_deleted ) values ( ?, ?, ?, ?, ?, ? )
             * ==> Parameters: 21(Long), 西京(String), 22(Integer), 123@qq.com(String), 1(Integer), 0(Integer)
             * <==    Updates: 1
             */
            System.out.println(selective);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    注意是:如果要继续添加条件使用And,根据条件查询则要By

    B.修改用户信息

    在这里插入图片描述

        // 修改方法//updateUserNameAndAgeAndEmailById根据方法名可以得知:
        // 需要修改用户名,年龄,邮箱信息,是根据用户ID进行修改的
        int updateUserNameAndAgeAndEmailById(@Param("userName") String userName, @Param("age") Integer age, @Param("email") String email, @Param("id") Long id);
    
    
    • 1
    • 2
    • 3
    • 4
        <update id="updateUserNameAndAgeAndEmailById">
            update t_user
            set user_name = #{userName,jdbcType=VARCHAR},
                age       = #{age,jdbcType=NUMERIC},
                email     = #{email,jdbcType=VARCHAR}
            where id = #{id,jdbcType=NUMERIC}
        </update>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试

        @Test
        void update(){
            // 修改用户信息
            /**
             * ==>  Preparing: update t_user set user_name = ?, age = ?, email = ? where id = ?
             * ==> Parameters: 安吉(String), 19(Integer), 123@qq.com(String), 1(Long)
             * <==    Updates: 1
             */
            int id = userMapper.updateUserNameAndAgeAndEmailById("安吉", 19, "123@qq.com", 1l);
            System.out.println(id);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    C.查询用户信息

    在这里插入图片描述

        // 查询用户信息//selectByUserNameAndAge根据方法名可以得知
        // 需要根据用户名和年龄查询用户信息
        List<User> selectByUserNameAndAge(@Param("userName") String userName, @Param("age") Integer age);
    
    
    • 1
    • 2
    • 3
    • 4
        <select id="selectByUserNameAndAge" resultMap="BaseResultMap">
            select
            <include refid="Base_Column_List"/>
            from t_user
            where
            user_name = #{userName,jdbcType=VARCHAR}
            AND age = #{age,jdbcType=NUMERIC}
        </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试:

        @Test
        void select(){
            // 查询用户信息,根据用户名和年龄
            /**
             * ==>  Preparing: select id,user_name,age, email,sex,is_deleted from t_user where user_name = ? AND age = ?
             * ==> Parameters: 海康(String), 23(Integer)
             */
            List<User> list = userMapper.selectByUserNameAndAge("海康", 23);
            list.forEach(System.out::println);
            // User(id=8, userName=海康, age=23, email=123@qq.com, sex=0, isDeleted=1)
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    D.删除用户信息

    在这里插入图片描述

        // 删除用户信息//deleteByUserNameAndEmail根据方法名可以得知
        // 根据用户名和邮箱删除用户信息
        int deleteByUserNameAndEmail(@Param("userName") String userName, @Param("email") String email);
    
    • 1
    • 2
    • 3
        <delete id="deleteByUserNameAndEmail">
            delete
            from t_user
            where user_name = #{userName,jdbcType=VARCHAR}
              AND email = #{email,jdbcType=VARCHAR}
        </delete>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试

        @Test
        void delete(){
            // 根据用户名和年龄删除用户信息
            /**
             * ==>  Preparing: delete from t_user where user_name = ? AND email = ?
             * ==> Parameters: 安吉(String), 123@qq.com(String)
             */
            int delete = userMapper.deleteByUserNameAndEmail("安吉", "123@qq.com");
            System.out.println(delete);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    基于PHP+MySQL蛋糕甜点销售网站的设计与开发
    Ps:消除锯齿与修边
    我这两年的CSDN博客创作经历
    接口测试工具之Postman详解
    C# 如何读取dxf档案
    [MRCTF2020]你传你呢
    2019银川icpc K 思维,悬线法dp
    如何从存档服务器上完全删除PDM用户
    MLOps专栏文章汇总
    Linux crash调试(一)
  • 原文地址:https://blog.csdn.net/weixin_47267628/article/details/125473306