zoukankan      html  css  js  c++  java
  • MyBatis-Plus 基本用法

    MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生,官方文档地址:https://mp.baomidou.com/guide/

    前期准备

    数据库脚本

    DROP TABLE IF EXISTS `tb_user`;
    CREATE TABLE `tb_user` (
    	`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    	`user_name` VARCHAR ( 20 ) NOT NULL COMMENT '用户名',
    	`password` VARCHAR ( 20 ) NOT NULL COMMENT '密码',
    	`name` VARCHAR ( 30 ) COMMENT '姓名',
    	`age` INT COMMENT '年龄',
    	`email` VARCHAR ( 50 ) COMMENT '邮箱'
    ) COMMENT '测试表';
    
    
    INSERT INTO `tb_user`(`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES 
    (1, 'zhangsan', '123456', '张三', 18, 'test1@itcast.cn'),
    (2, 'lisi', '123456', '李四', '20', 'test2@itcast.cn'),
    (3, 'wangwu', '123456', '王五', '28', 'test3@itcast.cn'),			
    (4, 'zhaoliu', '123456', '赵六', '21', 'test4@itcast.cn'),			
    (5, 'sunqi', '123456', '孙七', '24', 'test5@itcast.cn');
    

    入门案例

    本文在Spring Boot框架的基础上介绍MyBatis-Plus的关联映射。

    • pom.xml文件
    <dependencies>
        <!-- MySql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    
        <!-- MyBatis-Plus-->
        <!-- 不需要引入 MyBatis 依赖,MyBatis-Plus 已经集成了 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
    
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    
        <!-- junit5 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
    • application.properties配置文件
    # 数据库连接配置
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/mp_learning?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
    spring.datasource.username=root
    spring.datasource.password=123456
    
    # 打印 sql 语句的日志配置
    logging.level.com.example.mapper=trace
    
    # MyBatis-Plus 配置
    # Mapper 接口所对应的 XML 映射文件位置,默认值是 classpath*:/mapper/**/*.xml
    mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
    # 别名包扫描路径,通过该属性可以给包中的类注册别名,默认值为 null
    mybatis-plus.type-aliases-package=com.example.entity
    # 是否开启驼峰命名规则映射,默认为 true
    mybatis-plus.configuration.map-underscore-to-camel-case=true
    # 开启 MyBatis 的二级缓存,默认为 true
    mybatis-plus.configuration.cache-enabled=true
    # 主键生成策略,默认为 assign_id(雪花算法)
    mybatis-plus.global-config.db-config.id-type=assign_id
    
    • 实体类
    package com.example.entity;
    
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    // 实体类名与表名匹配不一致,使用 @TableName 指定数据库表名
    @TableName("tb_user")  
    public class User {
        private Long id;
        private String userName;
        private String password;
        private String name;
        private Integer age;
        private String email;
    }
    
    • mapper 接口
    package com.example.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.example.entity.User;
    
    // BaseMapper<T> 是 MyBatis-Plus 提供的通用 Mapper
    public interface UserMapper extends BaseMapper<User> {
    }
    

    mapper 接口需要注册到 Spring 容器中,在启动类中添加@MapperScan注解扫描 mapper 所在包:

    @MapperScan("com.example.mapper")  // 扫描 mapper 接口所在包
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
    • 测试
    @SpringBootTest
    public class UserMapperTest {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        public void testSelect() {
            // 使用通用 Mapper 的 selectList() 方法查询所有记录
            List<User> userList = userMapper.selectList(null);
            Assertions.assertEquals(5, userList.size());
            userList.forEach(System.out::println);
        }
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email FROM tb_user
    ==> Parameters: 
    <==    Columns: id, user_name, password, name, age, email
    <==        Row: 1, zhangsan, 123456, 张三, 18, test1@itcast.cn
    <==        Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==        Row: 4, zhaoliu, 123456, 赵六, 21, test4@itcast.cn
    <==        Row: 5, sunqi, 123456, 孙七, 24, test5@itcast.cn
    <==      Total: 5    
    User(id=1, userName=zhangsan, password=123456, name=张三, age=18, email=test1@itcast.cn)
    User(id=2, userName=lisi, password=123456, name=李四, age=20, email=test2@itcast.cn)
    User(id=3, userName=wangwu, password=123456, name=王五, age=28, email=test3@itcast.cn)
    User(id=4, userName=zhaoliu, password=123456, name=赵六, age=21, email=test4@itcast.cn)
    User(id=5, userName=sunqi, password=123456, name=孙七, age=24, email=test5@itcast.cn)
    

    配置和注解

    配置选项

    MyBatis-Plus中有大量配置,其中一部分是 MyBatis 原生的配置,另一部分是 MP 自身的配置,具体配置选项请查看 https://mp.baomidou.com/config/

    基本配置

    • 配置mapper.xml映射文件位置
    # Mapper 接口所对应的 XML 映射文件位置,默认值是 classpath*:/mapper/**/*.xml
    mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
    

    MyBatis-Plus 在 MyBatis 基础上只做增强,不做改变,所以 Mapper 接口仍可自定义方法。如果存在自定义方法,接口对应的 XML 文件也需要有相关实现,该配置就是告诉 Mapper 接口其对应的 XML 映射文件位置。

    在 UserMapper 接口中自定义方法:

    // BaseMapper<T> 是 MyBatis-Plus 提供的通用 Mapper
    public interface UserMapper extends BaseMapper<User> {
        // 根据 id 查询数据
        User findById(long id);
    }
    

    UserMapper.xml映射文件中实现相应方法的 sql:

    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.example.mapper.UserMapper">
        <select id="findById" resultType="com.example.entity.User">
            select id, user_name, password, name, age, email
            from tb_user
            where id = #{id}
        </select>
    </mapper>
    

    findById()方法进行测试:

    @Test
    public void testFindById() {
        User user = userMapper.findById(1L);
        System.out.println(user);
    }
    
    ==>  Preparing: select id, user_name, password, name, age, email from tb_user where id = ?
    ==> Parameters: 1(Long)
    <==    Columns: id, user_name, password, name, age, email
    <==        Row: 1, zhangsan, 123456, 张三, 18, test1@itcast.cn
    <==      Total: 1
    User(id=1, userName=zhangsan, password=123456, name=张三, age=18, email=test1@itcast.cn)   
    
    • 配置别名包扫描路径
    # 别名包扫描路径,通过该属性可以给包中的类注册别名,默认为 null
    mybatis-plus.type-aliases-package=com.example.entity
    

    MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 映射文件中可以直接使用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)。

    <mapper namespace="com.example.mapper.UserMapper">
        <!-- resultType 可以直接配置别名,不需要使用全限定的类名 -->
        <select id="findById" resultType="User">
            select id, user_name, password, name, age, email
            from tb_user
            where id = #{id}
        </select>
    </mapper>
    

    进阶配置

    • 开启驼峰命名规则映射
    # 是否开启驼峰命名规则映射,默认为 true
    mybatis-plus.configuration.map-underscore-to-camel-case=true
    

    开启自动驼峰命名规则映射,即将数据库列A_COLUMN映射到 Java 属性aColumn

    • 开启 MyBatis 的二级缓存
    # 开启 MyBatis 的二级缓存,默认为 true
    mybatis-plus.configuration.cache-enabled=true
    

    DB 策略配置

    • 全局主键生成策略
    # 插入记录时的主键生成策略,默认值是 assign_id(雪花算法)
    # auto: 数据库主键 ID 自增
    # assign_uuid: uuid 随机生成主键 ID
    # input: 用户输入主键 ID
    # none: 在全局配置中等于 input
    mybatis-plus.global-config.db-config.id-type=assign_id
    

    该策略定义在 IdType 枚举类中,定义如下:

    public enum IdType {
        AUTO(0),   // 数据库主键 ID 自增
        
        NONE(1),   // 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
        
        INPUT(2),  // 用户输入 ID
        
        /* 以下 3 种类型、只有当插入对象 ID 为空,才自动填充。 */
        ASSIGN_ID(3),  // 使用雪花算法生成主键 ID,主键类型为 number 或 string
        
        ASSIGN_UUID(4),  // UUID 随机生成主键 ID,主键类型为 string
    }
    

    注意:雪花算法生成 id 的字符串长度为 19,如果想使用 number 类型存储,那么实体类主键属性类型应该为 Long,数据库表主键字段类型应该为BIGINT(20);UUID 随机生成 id 的字符串长度为 32,所以数据库表主键字段类型应该设置为VARCHAR(32)

    • 表名前缀
    # 设置表名前缀,全局配置后可省略 @TableName 注解配置,默认值为 null
    mybatis-plus.global-config.db-config.table-prefix=tb_
    

    常用注解

    MyBatis-Plus注解使用详情请查看 https://mp.baomidou.com/guide/annotation.html,此处仅介绍一些常用的注解及其属性。

    @TableName

    该注解前面的入门案例也使用过了,当实体类名与表名不匹配时(默认使用驼峰命名方式),可以使用该注解的 value 属性指定表名:

    // 实体类名与表名匹配不一致,使用 @TableName 指定数据库表名
    @TableName(value = "tb_user")  
    public class User {
        //...
    }
    

    @TableName注解的其它属性不常用,这里就不多介绍。

    @TableId

    该注解是用来描述主键的。默认情况下,MyBatis-Plus会将实体类中名为 id 的属性作为主键标识,如果没有 id 属性,则需要使用@TableId注解指定实体类中的主键标识。

    @TableId注解中拥有两个属性:

    public @interface TableId {
        // 指定数据库表字段名(默认为驼峰命名方式,该值可无)
        String value() default "";  
    	// 主键 ID 的生成策略,默认为 NONE,即跟随全局主键生成策略
        IdType type() default IdType.NONE;  
    }
    
    • value:默认的实体类属性名和数据库表字段名映射是驼峰命名方式,如果映射不匹配,则可以使用该属性指定数据库字段名。
    • type:默认的主键生成方式是跟随全局主键生成策略,如果不想使用全局策略,则可以使用该属性指定策略,具体策略定义查看前面的 IdType 枚举类。

    具体使用如下:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    // 实体类名与表名匹配不一致,使用 @TableName 指定数据库表名
    @TableName(value = "tb_user")  
    public class User {
        @TableId(type = IdType.AUTO)  // 主键自增策略
        private Long id;
        
        private String userName;
        private String password;
        private String name;
        private Integer age;
        private String email;
    }
    

    @TableField

    @TableId是用来描述主键字段,而@TableField是用来描述非主键字段。该注解的属性很多,这里仅介绍一些常用的:

    • value:指定数据库表字段名,和前两个注解的 value 属性用法一样。如果实体类属性名和数据库表字段名匹配不一致(默认为驼峰命名方式),则可以使用该属性指定数据库表字段名。
    • exist:用于排除实体类中的非表字段。默认值为 true,表示该实体类属性为对应表字段。常用于多表之间的联系,例如一个老师对应多个学生,在 Teacher 类中需要一个 Student 类的集合,就可以将该属性设置为 false,排除 Teacher 类中非表字段List<Student> students

    具体使用如下:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    // 实体类名与表名匹配不一致,使用 @TableName 指定数据库表名
    @TableName(value = "tb_user")
    public class User {
        @TableId(type = IdType.AUTO)  // 主键自增策略
        private Long id;
    
        private String userName;
        private String password;
        private String name;
        private Integer age;
    
        @TableField(value = "email")  // 解决字段名匹配不一致
        private String mail;
    
        @TableField(exist = false)  // 排除实体类中的非表字段
        private String address;  
    }
    

    @TableField注解中还拥有 insertStrategy、updateStrategy 和 whereStrategy 三个属性来分别指定字段在 insert 语句、set 语句和 where 条件中所采取的策略。这三个属性指定的字段策略都定义在 FieldStrategy 枚举类中:

    public enum FieldStrategy {
        IGNORED,    // 忽略判断,无论字段是否为 NULL,都会加入 sql 语句中
       
        NOT_NULL,   // 非 NULL 判断, 即值为 NULL 的字段不加入 sql 语句
    
        NOT_EMPTY,  // 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断)
    
        DEFAULT,    // 默认值,在注解中表示跟随全局策略,在全局配置中表示 NOT_NULL
    
        NEVER       // 不加入 sql
    }
    

    下面介绍这三个属性的具体使用:

    • 插入策略
    /** 
     * 字段验证策略之 insert: 当 insert 操作时,该字段拼接 insert 语句时的策略,默认为全局策略 NOT_NULL
     * IGNORED: 直接拼接 insert into table_a(column) values (#{columnProperty});
     * NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) 
     *           values (<if test="columnProperty != null">#{columnProperty}</if>)
     * NOT_EMPTY: insert into table_a (<if test="columnProperty != null and 
     *			  columnProperty!=''">column</if>) values (<if test="columnProperty != null
     *            and columnProperty!=''">#{columnProperty}</if>)
     */
    FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;
    
    • 更新策略
    /**
     * 字段验证策略之 update: 当更新操作时,该字段拼接 set 语句时的策略,默认为全局策略 NOT_NULL
     * IGNORED: 直接拼接 update table_a set column=#{columnProperty}, 
     *          属性为null/空string都会被set进去
     * NOT_NULL: update table_a set <if test="columnProperty != null">
     *           column=#{columnProperty}</if>
     * NOT_EMPTY: update table_a set <if test="columnProperty != null and 
     *            columnProperty!=''">column=#{columnProperty}</if>
     */
    FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;
    
    • 查询策略
    /**
     * 字段验证策略之 where: 表示该字段在拼接 where 条件时的策略,默认为全局策略 NOT_NULL
     * IGNORED: 直接拼接 column=#{columnProperty}
     * NOT_NULL: <if test="columnProperty != null">column=#{columnProperty}</if>
     * NOT_EMPTY: <if test="columnProperty != null and columnProperty!=''">
     *            column=#{columnProperty}</if>
     */
    FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;
    

    由上面可知,在默认配置下使用MyBatis-Plus提供的 CURD 方法,字段策略都为NOT_NULL,即值为 null 的字段不加入 sql 语句。


    通用 Mapper 方法

    自定义 Mapper 通过继承 BaseMapper 就可以获取到各种各样的单表操作,下面我们会介绍这些通用的 CURD。

    插入操作

    insert()方法是 BaseMapepr 唯一提供的插入方法:

    /**
     * 插入一条记录,默认插入策略是 NOT_NULL,即值为 null 的字段不加入 sql 语句
     *
     * @param entity 实体对象
     */
    int insert(T entity);
    

    insert()方法进行测试:

    @Test
    public void testInsert() {
        User user = new User();
        user.setUserName("caocao");
        user.setPassword("1234567");
        user.setName("曹操");
        // 返回的 result 是受影响的行数,并不是自增后的 id
        int insert = userMapper.insert(user);
        Assertions.assertEquals(1, insert);
        // 自增的 id 会回填到对象中
        System.out.println(user.getId());
    }
    
    ==>  Preparing: INSERT INTO tb_user ( user_name, password, name ) VALUES ( ?, ?, ? )
    ==> Parameters: caocao(String), 1234567(String), 曹操(String)
    <==    Updates: 1
    6    
    

    由上述可以看出,MyBatis-Plus 的默认插入策略是值为 null 的字段不加入 sql 语句中。


    更新操作

    BaseMapper 提供了以下两个更新方法:

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

    updateById

    /**
     * 根据 ID 更新记录,更新不为 null 的字段
     *
     * @param entity 实体对象
     */
    int updateById(@Param(Constants.ENTITY) T entity);
    

    testUpdateById()方法进行测试:

    @Test
    public void testUpdateById() {
        User user = new User();
        user.setId(6L);  // 主键
        user.setAge(21); // 更新的字段
    
        // 根据 ID 更新,更新不为 null 的字段
        Assertions.assertEquals(1, userMapper.updateById(user));
    }
    
    ==>  Preparing: UPDATE tb_user SET age=? WHERE id=?
    ==> Parameters: 21(Integer), 6(Long)
    <==    Updates: 1
    

    update

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

    update()方法是通过条件构造器构造条件,条件构造器可以使用以下两种:

    // 设置 where 的查询条件
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    // 设置 where 的查询条件和 set 的更新字段
    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    

    下面使用上述两种条件构造器对update()方法进行测试,测试结果是一样的:

    @Test
    public void testUpdate() {
        User user = new User();
        user.setAge(22);   // 更新的字段
    
        QueryWrapper<User> wrapper = new QueryWrapper<>(); 
        wrapper.eq("id", 6);   // 查询的条件
    	
        Assertions.assertEquals(1, userMapper.update(user, wrapper));  // 更新
    }
    
    @Test
    public void testUpdate() {
        // 查询的条件以及更新的字段
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.eq("id", 6).set("age", 22);
    	// 更新,由于 warpper 已经设置了更新字段,所以 entity 参数设置为 null
        Assertions.assertEquals(1, userMapper.update(null, wrapper));
    }
    
    ==>  Preparing: UPDATE tb_user SET age=? WHERE (id = ?)
    ==> Parameters: 22(Integer), 6(Integer)
    <==    Updates: 1
    

    上面的 QueryWrapper 和 UpdateWrapper 是通过自己写表的字段名进行条件构造的,容易发生拼写错误,所以推荐使用 Lambda 条件构造器。Lambda 条件构造器的条件是通过调用实体类中的属性方法来构造,如果方法名称写错会出现错误提示,相对而言更容易纠正。下面使用 Lambda 条件构造器重写上述两个方法:

    @Test
    public void testUpdate2() {
        User user = new User();
        user.setAge(23);   // 更新的字段
    
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId, 6);   // 查询的条件
    
        Assertions.assertEquals(1, userMapper.update(user, wrapper));  // 更新
    }
    
    @Test
    public void testUpdate2() {
        // 查询的条件以及更新的字段
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getId, 6).set(User::getAge, 23);
        // 更新,由于 warpper 已经设置了更新字段,所以 entity 参数设置为 null
        Assertions.assertEquals(1, userMapper.update(null, wrapper));
    }
    
    ==>  Preparing: UPDATE tb_user SET age=? WHERE (id = ?)
    ==> Parameters: 23(Integer), 6(Integer)
    <==    Updates: 1
    

    删除操作

    BaseMapper 提供了以下四个通用删除方法:

    /**
     * 根据 ID 删除记录
     *
     * @param id 主键ID
     */
    int deleteById(Serializable id);
    
    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
    /**
     * 根据 Wrapper 条件,删除记录
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
    
    /**
     * 删除(根据 ID 批量删除)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    

    deleteById

    /**
     * 根据 ID 删除记录
     *
     * @param id 主键 ID
     */
    int deleteById(Serializable id);
    
    @Test
    public void testDeleteById() {
        // 执行删除操作
        Assertions.assertEquals(1, userMapper.deleteById(6L));
    }
    
    ==>  Preparing: DELETE FROM tb_user WHERE id=?
    ==> Parameters: 6(Long)
    <==    Updates: 1
    

    deleteByMap

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
    @Test
    public void testDeleteByMap() {
        // columnMap 存储查询条件,key 存储的是表字段名不是实体类属性名
        Map<String, Object> columnMap = new HashMap<>();
        columnMap.put("age", 20);
        columnMap.put("name", "张三");
    
        // 执行删除操作,预期结果为 0
        Assertions.assertEquals(0, userMapper.deleteByMap(columnMap));
    }
    
    ==>  Preparing: DELETE FROM tb_user WHERE name = ? AND age = ?
    ==> Parameters: 张三(String), 20(Integer)
    <==    Updates: 0
    

    delete

    /**
     * 根据 Wrapper 条件,删除记录
     *
     * @param wrapper 实体对象封装操作类(可以为 null)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
    
    @Test
    public void testDelete() {
        User user = new User();
        user.setAge(20);
        user.setName("张三");
        // 将实体对象进行包装,会将实体类的非 null 属性作为查询参数
        QueryWrapper<User> wrapper = new QueryWrapper<>(user);
    
        // 执行删除操作,预期结果为 0
        Assertions.assertEquals(0, userMapper.delete(wrapper));
    }
    
    ==>  Preparing: DELETE FROM tb_user WHERE name = ? AND age = ?
    ==> Parameters: 张三(String), 20(Integer)
    <==    Updates: 0
    

    deleteBatchIds

    /**
     * 删除(根据 ID 批量删除)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
    @Test
    public void testDeleteBatchIds() {
        List<Long> list = Arrays.asList(1L, 10L, 20L);
        // 根据 id 集合批量进行删除
        Assertions.assertEquals(1, userMapper.deleteBatchIds(list));
    }
    
    ==>  Preparing: DELETE FROM tb_user WHERE id IN ( ? , ? , ? )
    ==> Parameters: 1(Long), 10(Long), 20(Long)
    <==    Updates: 1
    

    普通查询操作

    MyBatis-Plus 提供了多种单表查询操作,包括根据 id 查询、批量查询、查询单条数据、查询列表等操作。

    /**
     * 根据 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);
    
    /**
     * 根据 Wrapper 条件,查询一条记录,如果结果超过一条会报错
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @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);
    

    selectById

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);
    
    @Test
    public void testSelectById() {
        // 根据 id 查询数据
        User user = userMapper.selectById(2L);
        System.out.println(user);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE id=?
    ==> Parameters: 2(Long)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
    <==      Total: 1
    User(id=2, userName=lisi, password=123456, name=李四, age=20, mail=test2@itcast.cn, address=null)    
    

    selectBatchIds

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
    
    @Test
    public void testSelectBatchIds() {
        // 根据 id 集合批量查询 
        List<User> users = userMapper.selectBatchIds(Arrays.asList(2L, 3L, 10L));
        users.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE id IN ( ? , ? , ? )
    ==> Parameters: 2(Long), 3(Long), 10(Long)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==      Total: 2
    User(id=2, userName=lisi, password=123456, name=李四, age=20, mail=test2@itcast.cn, address=null)
    User(id=3, userName=wangwu, password=123456, name=王五, age=28, mail=test3@itcast.cn, address=null)    
    

    selectByMap

    /**
     * 查询(根据 columnMap 条件查询)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
    
    @Test
    public void testSelectByMap() {
        // columnMap 存储查询条件,key 存储的是表字段名不是实体类属性名
        Map<String, Object> columnMap = new HashMap<>();
        columnMap.put("user_name", "lisi");
        columnMap.put("age", 20);
    
        // 根据 Map 进行查询
        List<User> users = userMapper.selectByMap(columnMap);
        users.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE user_name = ? AND age = ?
    ==> Parameters: lisi(String), 20(Integer)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
    <==      Total: 1
    User(id=2, userName=lisi, password=123456, name=李四, age=20, mail=test2@itcast.cn, address=null)    
    

    selectOne

    /**
     * 根据 Wrapper 条件,查询一条记录,如果结果超过一条会报错
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectOne() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getName, "李四");
        // 根据条件查询一条数据,如果结果超过一条会报错
        User user = userMapper.selectOne(wrapper);
        System.out.println(user);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (name = ?)
    ==> Parameters: 李四(String)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 2, lisi, 123456, 李四, 20, test2@itcast.cn
    <==      Total: 1
    User(id=2, userName=lisi, password=123456, name=李四, age=20, mail=test2@itcast.cn, address=null)    
    

    selectCount

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectCount() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 23);  // 年龄大于 23 岁
        // 根据条件查询数据总数
        Assertions.assertEquals(2, userMapper.selectCount(wrapper));
    }
    
    ==>  Preparing: SELECT COUNT( 1 ) FROM tb_user WHERE (age > ?)
    ==> Parameters: 23(Integer)
    <==    Columns: COUNT( 1 )
    <==        Row: 2
    <==      Total: 1
    

    selectList

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectList() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 23);  // 年龄大于 23 岁
        // 根据条件查询查询
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (age > ?)
    ==> Parameters: 23(Integer)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==        Row: 5, sunqi, 123456, 孙七, 24, test5@itcast.cn
    <==      Total: 2
    User(id=3, userName=wangwu, password=123456, name=王五, age=28, mail=test3@itcast.cn, address=null)
    User(id=5, userName=sunqi, password=123456, name=孙七, age=24, mail=test5@itcast.cn, address=null)    
    

    selectMaps

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectMaps() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 23);  // 年龄大于 23 岁
        // 根据条件查询查询
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (age > ?)
    ==> Parameters: 23(Integer)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==        Row: 5, sunqi, 123456, 孙七, 24, test5@itcast.cn
    <==      Total: 2
    {password=123456, mail=test3@itcast.cn, user_name=wangwu, name=王五, id=3, age=28}
    {password=123456, mail=test5@itcast.cn, user_name=sunqi, name=孙七, id=5, age=24}    
    

    selectObjs

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值,如果只返回一列时可以使用该方法</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectObjs() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(User::getName).gt(User::getAge, 23);
        // 根据条件查询查询
        List<Object> objs = userMapper.selectObjs(wrapper);
        objs.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT name FROM tb_user WHERE (age > ?)
    ==> Parameters: 23(Integer)
    <==    Columns: name
    <==        Row: 王五
    <==        Row: 孙七
    <==      Total: 2
    王五
    孙七    
    

    分页查询操作

    MyBatis-Plus 提供了以下两种分页查询方式:

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

    配置分页插件

    MyBatis-Plus 的分页查询需要配置分页插件:

    package com.example.config;
    
    import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MybatisPlusConfig {
    
        @Bean
        public PaginationInterceptor paginationInterceptor() {  // 配置分页插件
            PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
            // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
            // paginationInterceptor.setOverflow(false);
            // 设置最大单页限制数量,默认 500 条,-1 不受限制
            // paginationInterceptor.setLimit(500);
            // 开启 count 的 join 优化,只针对部分 left join
            paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
            return paginationInterceptor;
        }
    }
    

    selectPage

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectPage() {
        // 年龄大于 20 岁,并按照年龄从小到大排序
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 20).orderByAsc(User::getAge);  
    
        // 设置分页条件,第一个参数是当前页(从 1 开始),第二个参数每页最大显示记录数
        Page<User> page = new Page<>(2, 2);
    
        // 这里需要注意分页查询返回的对象 userIPage 与传入的对象 page 是同一个对象
        IPage<User> userIPage = userMapper.selectPage(page, wrapper);
    
        System.out.println("总页数:" + userIPage.getPages());
        System.out.println("总记录数:" + userIPage.getTotal());
        System.out.println("每页显示最大记录数:" + userIPage.getSize());
        System.out.println("当前页:" + userIPage.getCurrent());
        // 返回当前页的记录
        userIPage.getRecords().forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT COUNT(1) FROM tb_user WHERE (age > ?)
    ==> Parameters: 20(Integer)
    <==    Columns: COUNT(1)
    <==        Row: 3
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (age > ?) ORDER BY age ASC LIMIT ?,?
    ==> Parameters: 20(Integer), 2(Long), 2(Long)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==      Total: 1
    总页数:2
    总记录数:3
    每页显示最大记录数:2
    当前页:2
    User(id=3, userName=wangwu, password=123456, name=王五, age=28, mail=test3@itcast.cn, address=null)
    

    selectMapsPage

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     */
    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
    @Test
    public void testSelectMapsPage() {
        // 年龄大于 20 岁,并按照年龄从小到大排序
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(User::getAge, 20).orderByAsc(User::getAge);
    
        // 设置分页条件,第一个参数是当前页(从 1 开始),第二个参数每页最大显示记录数
        Page<Map<String, Object>> page = new Page<>(2, 2);
    
        // 这里需要注意分页查询返回的对象 userIPage 与传入的对象 page 是同一个对象
        IPage<Map<String, Object>> userIPage = userMapper.selectMapsPage(page, wrapper);
    
        System.out.println("总页数:" + userIPage.getPages());
        System.out.println("总记录数:" + userIPage.getTotal());
        System.out.println("每页显示最大记录数:" + userIPage.getSize());
        System.out.println("当前页:" + userIPage.getCurrent());
        // 返回当前页的记录
        userIPage.getRecords().forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT COUNT(1) FROM tb_user WHERE (age > ?)
    ==> Parameters: 20(Integer)
    <==    Columns: COUNT(1)
    <==        Row: 3
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (age > ?) ORDER BY age ASC LIMIT ?,?
    ==> Parameters: 20(Integer), 2(Long), 2(Long)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==      Total: 1
    总页数:2
    总记录数:3
    每页显示最大记录数:2
    当前页:2
    {password=123456, mail=test3@itcast.cn, user_name=wangwu, name=王五, id=3, age=28}    
    

    自定义分页方法

    在 UserMapper 接口定义分页方法:

    public interface UserMapper extends BaseMapper<User> {
        // 第一个参数必须为 Page 对象,MyBatis-Plus 会自动对查询语句加上分页处理
        // 注意:分页返回的对象与传入的对象是同一个
        IPage<User> selectUserPage(Page<User> page, Integer age);
    }
    

    UserMapper.xml文件中编写对应的 sql 语句:

    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.example.mapper.UserMapper">
        <!-- MyBatis-Plus 会自动对查询语句加上分页处理,所以 SQL 语句中不用添加 limit 语句 -->
        <select id="selectUserPage" resultType="com.example.entity.User">
            select id,user_name,password,name,age,email AS mail
            from tb_user
            where age > #{age} order by age
        </select>
    </mapper>
    

    selectUserPage()方法进行测试:

    @Test
    public void testSelectUserPage() {
        // 设置分页条件,第一个参数是当前页(从 1 开始),第二个参数每页最大显示记录数
        Page<User> page = new Page<>(2, 2);
        // 这里需要注意分页查询返回的对象 userIPage 与传入的对象 page 是同一个对象
        IPage<User> userIPage = userMapper.selectUserPage(page, 20);
    
        System.out.println("总页数:" + userIPage.getPages());
        System.out.println("总记录数:" + userIPage.getTotal());
        System.out.println("每页显示最大记录数:" + userIPage.getSize());
        System.out.println("当前页:" + userIPage.getCurrent());
        // 返回当前页的记录
        userIPage.getRecords().forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT COUNT(1) FROM tb_user WHERE (age > ?)
    ==> Parameters: 20(Integer)
    <==    Columns: COUNT(1)
    <==        Row: 3
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (age > ?) ORDER BY age ASC LIMIT ?,?
    ==> Parameters: 20(Integer), 2(Long), 2(Long)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==      Total: 1
    总页数:2
    总记录数:3
    每页显示最大记录数:2
    当前页:2
    User(id=3, userName=wangwu, password=123456, name=王五, age=28, mail=test3@itcast.cn, address=null)
    

    通用 Service 方法

    MyBatis-Plus 不但提供了通用 Mapper 方法,还提供了通用 Service 方法。通用 Service 的方法可以查看官方文档:https://mp.baomidou.com/guide/crud-interface.html#service-crud-接口,查询、更新、插入和删除的用法和通用 Mapper 类似,这里只是简单介绍。

    入门案例

    • 创建一个 Service 层接口
    package com.example.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.example.entity.User;
    
    // IService<T> 是 MyBatis-Plus 提供的通用 Service 接口,定义了常用的对 T 数据表的操作
    public interface UserService extends IService<User> {
    }
    
    • 实现 Service 接口
    package com.example.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.entity.User;
    import com.example.mapper.UserMapper;
    import com.example.service.UserService;
    import org.springframework.stereotype.Service;
    
    // ServiceImpl<M extends BaseMapper<T>, T> 接口是对 IService<T> 接口的实现
    // 第一个泛型 M 指定继承了 BaseMapper 接口的子接口
    // 第二个泛型 T 指定实体类
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }
    
    • 测试
    @SpringBootTest
    public class UserServiceImplTest {
    
        @Autowired
        private UserService userService;
    
        @Test
        public void testSaveOrUpdate() {
            User user = new User();
            user.setId(10000L);     // 设置一个不存在的主键值
            user.setUserName("wangba");
            user.setPassword("123456");
            user.setName("王八");
            user.setAge(29);
            user.setMail("update@email");
           
    		// saveOrUpdate():该方法首先会根据实体类主键字段查询相关记录,如果记录存在
            //                则执行更新操作,如果记录不存在则执行插入操作
            Assertions.assertEquals(true, userService.saveOrUpdate(user));
            // 插入操作后,主键会回填
            System.out.println(user.getId());
        }
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE id=?
    ==> Parameters: 10000(Long)
    <==      Total: 0
    ==>  Preparing: INSERT INTO tb_user ( user_name, password, name, age, email ) VALUES ( ?, ?, ?, ?, ? )
    ==> Parameters: wangba(String), 123456(String), 王八(String), 29(Integer), update@email(String)
    <==    Updates: 1
    7    
    

    CURD 接口

    Save

    // 插入一条记录(选择字段,策略插入)
    boolean save(T entity);
    // 插入(批量)
    boolean saveBatch(Collection<T> entityList);
    // 插入(批量),batchSize 是插入批次数量
    boolean saveBatch(Collection<T> entityList, int batchSize);
    

    SaveOrUpdate

    // 首先根据主键查询相关记录,如果记录存在则执行更新操作,如果不存在则执行插入操作
    boolean saveOrUpdate(T entity);
    // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
    boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
    // 批量修改插入
    boolean saveOrUpdateBatch(Collection<T> entityList);
    // 批量修改插入,batchSize 是插入批次数量
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
    

    Remove

    // 根据 entity 条件,删除记录
    boolean remove(Wrapper<T> queryWrapper);
    // 根据 ID 删除
    boolean removeById(Serializable id);
    // 根据 columnMap 条件,删除记录
    boolean removeByMap(Map<String, Object> columnMap);
    // 删除(根据ID 批量删除)
    boolean removeByIds(Collection<? extends Serializable> idList);
    

    Update

    // 根据 UpdateWrapper 条件,更新记录,需要设置sqlset
    boolean update(Wrapper<T> updateWrapper);
    // 根据 whereEntity 条件,更新记录
    boolean update(T entity, Wrapper<T> updateWrapper);
    // 根据 ID 选择修改
    boolean updateById(T entity);
    // 根据ID 批量更新
    boolean updateBatchById(Collection<T> entityList);
    // 根据ID 批量更新,batchSize 是更新批次数量
    boolean updateBatchById(Collection<T> entityList, int batchSize);
    

    Get

    // 根据 ID 查询
    T getById(Serializable id);
    // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
    T getOne(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);
    // 根据 Wrapper,查询一条记录
    Map<String, Object> getMap(Wrapper<T> queryWrapper);
    // 根据 Wrapper,查询一条记录
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
    

    List

    // 查询所有
    List<T> list();
    // 查询列表
    List<T> list(Wrapper<T> queryWrapper);
    // 查询(根据ID 批量查询)
    Collection<T> listByIds(Collection<? extends Serializable> idList);
    // 查询(根据 columnMap 条件)
    Collection<T> listByMap(Map<String, Object> columnMap);
    // 查询所有列表
    List<Map<String, Object>> listMaps();
    // 查询列表
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
    // 查询全部记录
    List<Object> listObjs();
    // 查询全部记录
    <V> List<V> listObjs(Function<? super Object, V> mapper);
    // 根据 Wrapper 条件,查询全部记录
    List<Object> listObjs(Wrapper<T> queryWrapper);
    // 根据 Wrapper 条件,查询全部记录
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
    

    Page

    // 无条件分页查询
    IPage<T> page(IPage<T> page);
    // 条件分页查询
    IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
    // 无条件分页查询
    IPage<Map<String, Object>> pageMaps(IPage<T> page);
    // 条件分页查询
    IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
    

    Count

    // 查询总记录数
    int count();
    // 根据 Wrapper 条件,查询总记录数
    int count(Wrapper<T> queryWrapper);
    

    条件构造器

    前面的通用 Mapper 方法中有使用到条件构造器,此部分会详细介绍条件构造器的条件函数及其使用。更为详细的介绍查看官方文档 https://mp.baomidou.com/guide/wrapper.html

    条件函数

    allEq

    // params: key为数据库字段名,value为字段值
    // null2IsNull: 为 true 则在 value 值为 null 时会在 sql 中调用 isNull 方法,默认为 true
    //              为 false 则忽略为 null 的 value 值
    // condition:布尔值,表示该条件是否加入最后生成的 sql 中,默认为 true
    allEq(Map<R, V> params)   
    allEq(Map<R, V> params, boolean null2IsNull)
    allEq(boolean condition, Map<R, V> params, boolean null2IsNull) 
    
    // 例子1:
    allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null
    // 例子2:    
    allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'    
    
    // 比起前面多增加了一个 fiter 参数,该参数是一个过滤器,过滤掉不符合要求的 key-value 对
    allEq(BiPredicate<R, V> filter, Map<R, V> params)
    allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
    allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
        
    // 例子1: 
    allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})--->name = '老王' and age is null
    // 例子 2:     
    allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)--->name = '老王'  
    
    @Test
    public void testAllEq() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        Map<String, Object> params = new HashMap<>();
        params.put("name", "李四");
        params.put("age", null);
        // allEq(condition, filter, params, null2IsNull)
        // condition 为 true,该条件才会加入最后生成的 sql 中
        // filter 使用 lambda 表达式过滤 name 属性不能加入 sql 语句
        // null2IsNull 为 true,为 null 的 value 值会调用 isNull 方法判断
        wrapper.allEq(params.get("name") != null, (k, v) -> ! k.equals("name"), params, true);
    
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE (age IS NULL)
    ==> Parameters: 
    <==      Total: 0
    

    由上可以看出,当 null2IsNull 为 true,不能忽略为 null 的 value 值,而是调用 isNull 方法判断。

    eq 和 ne

    // 等于 "="
    ne(R column, Object val)
    ne(boolean condition, R column, Object val)
        
    // 例子
    eq("name", "老王")--->name = '老王'
    
    // 不等于 "<>"("!=")
    ne(R column, Object val)
    ne(boolean condition, R column, Object val)
    
    // 例子
    ne("name", "老王")--->name != '老王'    
    

    gt、ge、lt 和 le

    // 大于 ">"
    gt(R column, Object val)
    gt(boolean condition, R column, Object val)
    
    // 例子
    gt("age", 18)--->age > 18    
    
    // 大于等于 ">="
    ge(R column, Object val)
    ge(boolean condition, R column, Object val)
    
    // 例子
    ge("age", 18)--->age >= 18    
    
    // 小于 "<"
    lt(R column, Object val)
    lt(boolean condition, R column, Object val)
    
    // 例子
    lt("age", 18)--->age < 18    
    
    // 小于等于 "<="
    le(R column, Object val)
    le(boolean condition, R column, Object val)
    
    // 例子
    le("age", 18)--->age <= 18    
    

    between 和 notBetween

    // BETWEEN 值1 AND 值2
    between(R column, Object val1, Object val2)
    between(boolean condition, R column, Object val1, Object val2)
    
    // 例子
    notBetween("age", 18, 30)--->age not between 18 and 30    
    
    // NOT BETWEEN 值1 AND 值2
    notBetween(R column, Object val1, Object val2)
    notBetween(boolean condition, R column, Object val1, Object val2)
    
    // 例子
    notBetween("age", 18, 30)--->age not between 18 and 30    
    

    Like、notLike、likeLeft 和 likeRight

    // LIKE '%值%'
    like(R column, Object val)
    like(boolean condition, R column, Object val)
    
    // 例子
    like("name", "王")--->name like '%王%'   
    
    // NOT LIKE '%值%'
    notLike(R column, Object val)
    notLike(boolean condition, R column, Object val)
    
    // 例子   
    notLike("name", "王")--->name not like '%王%'   
    
    // LIKE '%值'
    likeLeft(R column, Object val)
    likeLeft(boolean condition, R column, Object val)
    
    // 例子
    likeLeft("name", "王")--->name like '%王'    
    
    // LIKE '值%'
    likeRight(R column, Object val)
    likeRight(boolean condition, R column, Object val)
    
    // 例子
    likeRight("name", "王")--->name like '王%'    
    

    isNull 和 isNotNull

    // 字段 IS NULL
    isNull(R column)
    isNull(boolean condition, R column)
    
    // 例子
    isNull("name")--->name is null    
    
    // 字段 IS NOT NULL
    isNotNull(R column)
    isNotNull(boolean condition, R column)
    
    // 例子
    isNotNull("name")--->name is not null    
    

    in、notIn、inSql 和 notInSql

    // 字段 IN (value.get(0), value.get(1), ...)
    in(R column, Collection<?> value)
    in(boolean condition, R column, Collection<?> value)
    
    // 例子
    in("age",{1,2,3})--->age in (1,2,3)    
    
    // 字段 IN (v0, v1, ...)  
    in(R column, Object... values)
    in(boolean condition, R column, Object... values)
    
    // 例子
    in("age", 1, 2, 3)--->age in (1,2,3)    
    
    // 字段 NOT IN (value.get(0), value.get(1), ...)
    notIn(R column, Collection<?> value)
    notIn(boolean condition, R column, Collection<?> value)
    
    // 例子
    notIn("age",{1,2,3})--->age not in (1,2,3)
    
    // 字段 NOT IN (v0, v1, ...)   
    notIn(R column, Object... values)
    notIn(boolean condition, R column, Object... values)
    
    // 例子
    notIn("age", 1, 2, 3)--->age not in (1,2,3)    
    
    // 字段 IN(sql语句)
    inSql(R column, String inValue)
    inSql(boolean condition, R column, String inValue)
    
    // 例子1
    inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
    // 例子2
    inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)    
    
    // 字段 NOT IN ( sql语句 )
    notInSql(R column, String inValue)
    notInSql(boolean condition, R column, String inValue)
    
    // 例子1
    notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)
    // 例子2    
    notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)    
    

    groupBy 和 having

    // 分组:GROUP BY 字段, ...
    groupBy(R... columns)
    groupBy(boolean condition, R... columns)
    
    // 例子
    groupBy("id", "name")--->group by id,name
    
    // HAVING ( sql语句 )
    having(String sqlHaving, Object... params)
    having(boolean condition, String sqlHaving, Object... params)
    
    // 例子1
    having("sum(age) > 10")--->having sum(age) > 10
    // 例子2    
    having("sum(age) > {0}", 11)--->having sum(age) > 11
    

    orderBy、orderByAsc 和 orderByDesc

    // 排序:ORDER BY 字段, ...
    orderBy(boolean condition, boolean isAsc, R... columns)
    
    // 例子
    orderBy(true, true, "id", "name")--->order by id ASC,name ASC    
    
    // 排序:ORDER BY 字段, ... ASC
    orderByAsc(R... columns)
    orderByAsc(boolean condition, R... columns)
    
    // 例子
    orderByAsc("id", "name")--->order by id ASC,name ASC  
    
    // 排序:ORDER BY 字段, ... DESC
    orderByDesc(R... columns)
    orderByDesc(boolean condition, R... columns)
    
    // 例子
    orderByDesc("id", "name")--->order by id DESC,name DESC
    

    or、and 和 nested

    // 拼接 OR 关键字
    or()
    or(boolean condition)
    
    // 例子
    // 主动调用 or 表示紧接着下一个方法不是用 and 连接!(不调用 or 则默认为使用 and 连接)    
    eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'
     
    // OR 嵌套    
    or(Consumer<Param> consumer)
    or(boolean condition, Consumer<Param> consumer) 
    
    // 例子    
    or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')
    
    // AND 嵌套
    and(Consumer<Param> consumer)
    and(boolean condition, Consumer<Param> consumer)
    
    // 例子
    and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')    
    
    // 正常嵌套,不带 AND 或者 OR
    nested(Consumer<Param> consumer)
    nested(boolean condition, Consumer<Param> consumer)
    
    // 例子
    nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')  
    

    apply 和 last

    // 拼接 sql
    apply(String applySql, Object... params)
    apply(boolean condition, String applySql, Object... params)
    
    // 例子1
    apply("id = 1")--->id = 1
    // 例子2
    apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
    // 例子3,例子2中的拼接方式存在 sql 注入的风险,所以可以动态入参,使用 {index}
    apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")    
    
    // 无视优化规则直接拼接到 sql 的最后
    // 只能调用一次,多次调用以最后一次为准,有 sql 注入的风险,请谨慎使用
    last(String lastSql)
    last(boolean condition, String lastSql)
    
    // 例子
    last("limit 1")   
    

    exists 和 notExists

    // 拼接 EXISTS (sql语句)
    exists(String existsSql)
    exists(boolean condition, String existsSql)
    
    // 例子
    exists("select id from table where age = 1")--->exists (select id from table where age = 1)    
    
    // 拼接 NOT EXISTS (sql语句)
    notExists(String notExistsSql)
    notExists(boolean condition, String notExistsSql)
    
    // 例子
    notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)    
    

    select

    // QueryWrapper 特有的条件函数,设置 SELECT 字段(查询返回的字段)
    select(String... sqlSelect)
    select(Predicate<TableFieldInfo> predicate)
    select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
     
    // 例子1
    select("id", "name", "age")
    // 例子2,使用 Lambda 表达式过滤查询字段(主键除外)  
    select(i -> i.getProperty().startsWith("test"))  
    

    set、setSql

    // UpdateWrapper 特有的条件函数,设置 SET 字段(更新字段)
    set(String column, Object val)
    set(boolean condition, String column, Object val)
    
    // 例子1
    set("name", "老李头")
    // 例子2
    set("name", "")--->数据库字段值变为空字符串
    // 例子3 
    set("name", null)--->数据库字段值变为null   
    
    // UpdateWrapper 特有的条件函数,设置 SET 部分 SQL(更新字段)
    setSql(String sql)
        
    // 例子
    setSql("name = '老李头'")    
    

    QueryWarpper 构造器

    QueryWarpper 构造器可用于构造查询条件和设置查询返回的字段,其有三个构造方法:

    public QueryWrapper() {
        this(null);
    }
    
    // 将实体类传入 Wrapper,会将实体类的非 null 属性作为查询参数
    public QueryWrapper(T entity) {
        super.setEntity(entity);
        super.initNeed();
    }
    
    // 将实体类传入 Wrapper,并设置返回的查询字段
    public QueryWrapper(T entity, String... columns) {
        super.setEntity(entity);
        super.initNeed();
        this.select(columns);
    }
    

    下面会演示这三个构造方法的使用:

    • 使用QueryWrapper()构造方法
    @Test
    public void testQueryWrapper() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 查询 user_name 以 w 开头,年龄大于 25 的用户
        wrapper.likeRight("user_name", "w").gt("age", 25);
    
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,user_name,password,name,age,email AS mail FROM tb_user WHERE ==> Parameters: w%(String), 25(Integer)
    <==    Columns: id, user_name, password, name, age, mail
    <==        Row: 3, wangwu, 123456, 王五, 28, test3@itcast.cn
    <==      Total: 1
    User(id=3, userName=wangwu, password=123456, name=王五, age=28, mail=test3@itcast.cn, address=null)    
    
    • 使用QueryWrapper(T entity)构造方法
    @Test
    public void testQueryWrapper2() {
        User user = new User();
        user.setAge(28);
        user.setMail("test3@itcast.cn");
    
        // 将实体类传入 Wrapper,会将实体类的非 null 属性作为查询参数
        QueryWrapper<User> wrapper = new QueryWrapper<>(user);
        wrapper.select("name");    // 设置查询的返回字段为 name
    
        List<Object> objectList = userMapper.selectObjs(wrapper);
        objectList.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT name FROM tb_user WHERE age=? AND email=?
    ==> Parameters: 28(Integer), test3@itcast.cn(String)
    <==    Columns: name
    <==        Row: 王五
    <==      Total: 1
    王五    
    
    • 使用QueryWrapper(T entity, String... columns)构造方法
    @Test
    public void testQueryWrapper3() {
        User user = new User();
        user.setAge(28);
        user.setMail("test3@itcast.cn");
    
        // 将实体类传入 Wrapper,并设置查询的返回字段
        QueryWrapper<User> wrapper = new QueryWrapper<>(user, "id", "name");
    
        List<Map<String, Object>> mapList = userMapper.selectMaps(wrapper);
        mapList.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT id,name FROM tb_user WHERE age=? AND email=?
    ==> Parameters: 28(Integer), test3@itcast.cn(String)
    <==    Columns: id, name
    <==        Row: 3, 王五
    <==      Total: 1
    {name=王五, id=3}    
    

    UpdateWarpper 构造器

    UpdateWarpper 构造器可用于构造查询条件和设置更新字段,其有三个构造器:

    public UpdateWrapper() {
        // 如果无参构造函数,请注意实体 NULL 情况 SET 必须有否则 SQL 异常
        this(null);
    }
    
    public UpdateWrapper(T entity) {
        super.setEntity(entity);
        super.initNeed();
        this.sqlSet = new ArrayList<>();
    }
    
    private UpdateWrapper(T entity, List<String> sqlSet, AtomicInteger paramNameSeq,
                          Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
                          SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
        super.setEntity(entity);
        this.sqlSet = sqlSet;
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
    }
    

    使用方式和 QueryWarpper 构造器差不多。


    Lamdba 构造器

    前面也说过,QueryWrapper 和 UpdateWrapper 是通过自己写表的字段名进行条件构造的,容易发生拼写错误,所以推荐使用 Lambda 条件构造器。Lambda 条件构造器的条件是通过调用实体类中的属性方法来构造,如果方法名称写错会出现错误提示,相对而言更容易纠正。

    MyBatis-Plus 提供以下三种种方式构造 LambdaQueryWrapper 和 LambdaUpdateWrapper 条件构造器:

    LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
    LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
    LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();
    
    LambdaUpdateWrapper<User> lambdaUpdateWrapper = new UpdateWrapper<User>().lambda();
    LambdaUpdateWrapper<User> lambdaUpdateWrapper1 = new LambdaUpdateWrapper<>();
    LambdaUpdateWrapper<User> lambdaUpdateWrapper2 = Wrappers.lambdaUpdate();
    
  • 相关阅读:
    Ubuntu16.04 + OpenCV源码 + Qt5.10 安装、配置
    DML和DQL
    初识MySql
    表单校验
    使用jQuery操作DOM
    jQuery中的事件与动画
    jQuery选择器
    初识jQuery
    JavaScript对象及初识OOP
    JavaScript操作DOM对象
  • 原文地址:https://www.cnblogs.com/zongmin/p/13399539.html
Copyright © 2011-2022 走看看