Начало работы с MyBatis-Plus

MyBatis

MyBatis-Plus (MP) только расширяет, а не изменяет на основе MyBatis, что упрощает разработку и повышает эффективность.

Эта статья основана наРуководство по началу работы с MyBatis-PlusВидео, подведение итогов после обучения. Если интересно, можете глянуть, для новичков очень полезно.

Курс интегрирован по типу SpringBoot + MyBatis-Plus.

адрес проекта

В статье может быть не понятно описано, или если вы хотите увидеть код, вы можете клонировать репозиторий

immoc-mybatis-plus

Предварительная конфигурация

Конфигурация базы данных и таблиц

В этом исследовании используется только одна библиотека и одна таблица с именем библиотеки mp и именем таблицы mp_user. Просто скопируйте его в инструмент sql и импортируйте.

/*
 Navicat Premium Data Transfer

 Source Server         : mac
 Source Server Type    : MySQL
 Source Server Version : 50716
 Source Host           : localhost:3306
 Source Schema         : mp

 Target Server Type    : MySQL
 Target Server Version : 50716
 File Encoding         : 65001

 Date: 30/07/2020 23:12:46
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for mp_user
-- ----------------------------
DROP TABLE IF EXISTS `mp_user`;
CREATE TABLE `mp_user` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `manager_id` bigint(20) DEFAULT NULL COMMENT '直属上级id',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `manager_fk` (`manager_id`),
  CONSTRAINT `manager_fk` FOREIGN KEY (`manager_id`) REFERENCES `mp_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of mp_user
-- ----------------------------
BEGIN;
INSERT INTO `mp_user` VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL, '2019-01-11 14:20:20');
INSERT INTO `mp_user` VALUES (1088248166370832385, '王天风', 26, 'wtf2@baomidou.com', 1087982257332887553, '2019-02-05 11:12:22');
INSERT INTO `mp_user` VALUES (1088250446457389058, '李艺伟', 32, 'lyw2019@baomidou.com', 1088248166370832385, '2019-02-14 08:31:16');
INSERT INTO `mp_user` VALUES (1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385, '2019-01-14 09:15:15');
INSERT INTO `mp_user` VALUES (1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385, '2019-01-14 09:48:16');
INSERT INTO `mp_user` VALUES (1288460195017072641, '刘明强', 31, NULL, 1088248166370832385, '2020-07-29 21:03:44');
INSERT INTO `mp_user` VALUES (1288842439342780417, '张强', 29, 'lh@baomidou.com', 1088248166370832385, '2020-07-30 22:23:33');
INSERT INTO `mp_user` VALUES (1288848935841488897, '刘花', 29, 'lh@baomidou.com', 1088248166370832385, '2020-07-30 22:48:27');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

Конфигурация зависимостей

Укажите родительский проект SpringBoot

  1. Зависит от SpringBootStarter
  2. ломбок, сгенерировать, установить, уменьшить код шаблона
  3. Стартер предоставлен нам MyBatis-Plus
  4. Последний пакет драйверов mysql.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.immoc.mybatisplus</groupId>
    <artifactId>immoc-mybatis-plus</artifactId>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>first</module>
        <module>crud</module>
    </modules>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mybatis.starter.version>3.1.0</mybatis.starter.version>
    </properties>

    <!-- Spring Boot Starter 父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <dependencies>
        <!-- SpringBoot启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- SpringBoot test启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- lombok简化Java代码 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- MyBatis-Plus启动器 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis.starter.version}</version>
        </dependency>
        <!-- MySQL jdbc 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
</project>

База данных и соответствующая конфигурация MyBaits-Plus

В каталоге ресурсов создайтеapplication.ymlдокумент. Конфигурация:

  1. Имя элемента
  2. 4 основные конфигурации базы данных, измените свое имя пользователя и пароль mysql!
  3. уровень печати журнала
  4. Конфигурация MyBatis-Plus и т.д., комментарии написаны очень четко, поэтому повторяться не буду.
spring:
  application:
    name: crud
  #配置数据库
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: hezihao123

#配置Log打印
logging:
  level:
    root: warn
    com.imooc.mybatisplus: trace
  pattern:
    console: '%p%m%n'

mybatis-plus:
  #配置mapper文件的位置,注意,如果你是maven多模块下使用,路径前需要加classpath*,即加载多个jar包下的xml文件
  mapper-locations:
    - classpath*:com/imooc/mybatisplus/mapper/*
  global-config:
    db-config:
      #全局id策略
      id-type: id_worker
      #字段生成sql的where策略,默认实体中的字段为null时,不添加到sql中,如果想为null,也添加到sql语句中,则使用ignored
      #一般不设置为ignored,因为在update语句中,如果不设置值,就会用null覆盖掉原有的值。一般我们是希望设置值的才更新,为null则不更新
      #not_empty,如果字段值为null或者空字符串,会忽略掉,不添加到sql语句中
      field-strategy: default
      #统一表名前缀
      #table-prefix: mp_
      #数据库表是否使用下划线间隔命名,默认为true
      table-underline: true
  #传统的mybatis配置文件位置
  #config-location: classpath:mybatis-config.xml
  #实体别名包配置
  type-aliases-package: com.imooc.mybatisplus.entity
  #注意configuration不能和config-location同时出现,不然会报错
  configuration:
    #驼峰转下划线(实体类用驼峰,数据库表字段用下划线),默认为true
    map-underscore-to-camel-case: true

Настройте файл конфигурации оригинального MyBatis

По сути, конфигурация MyBatis будет настроена наapplication.yml, но есть некоторые специфичные для MyBatis конфигурации, которые все еще можно поместить в исходные файлы конфигурации, такие как вторичный и третичный кэши и т. д. Создал в папке ресурсовmybatis-config.xmlфайл, пока не настраивайте его.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>

Настройте плагин пейджинга MyBatis-Plus

Создайте пакет конфигурации и добавьте класс MyBatisPlusConfig, который представляет собой класс конфигурации, предоставляющий экземпляр подключаемого модуля пейджинга PaginationInterceptor.

@Configuration
public class MyBatisPlusConfig {
    /**
     * MyBatisPlus的分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

Запись приложения

SpringBoot требует, чтобы мы предоставили запись с основным методом, например, мы предоставляем класс Application для запуска

Примечание. Используйте аннотацию @MapperScan, чтобы указать путь к пакету, сканируемому MyBatis.

@SpringBootApplication
//指定MyBatis扫描的包路径
@MapperScan("com.imooc.mybatisplus.dao")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Создайте класс сущности, соответствующий таблице

  1. Аннотация @Data для создания методов получения и установки классов сущностей
  2. аннотацию @EqualsAndHashCode, сгенерируйте методы equals и hashcode и укажите, что не следует вызывать методы equals и hashcode родительского класса.
  3. В аннотации @TableName указывается имя таблицы, соответствующее классу сущностей.Если имя таблицы и имя класса сущностей совпадают, его можно опустить, но если они несовместимы, аннотация требуется для указания имени таблицы.
  4. Аннотация @TableId указывает, что переменная-член класса сущности является полем первичного ключа в таблице базы данных, а атрибут value указывает имя первичного ключа в таблице. Имя первичного ключа по умолчанию — id. Если два имени согласуются с базой данных, их можно опустить. Если они несовместимы, их необходимо добавить.
  5. Аннотация @TableField указывает, что переменная-член является полем, соответствующим базе данных.Если имя переменной-члена и поля таблицы совпадают, его можно опустить.Если они несовместимы, его необходимо добавить.
    • атрибут value, используемый для указания соответствующего имени поля таблицы
    • Атрибут стратегии может указывать стратегию поля. По умолчанию, когда поле не равно нулю, операции вставки и обновления будут добавлены в поле данных инструкции sql. Можно настроить различные стратегии, например FieldStrategy.NOT_EMPTY, когда поле не является нулевым и ненулевым. Когда строка, добавляется только оператор sql
    • Атрибут exists указывает, что переменная-член не является полем в таблице базы данных. Во время CRUD это поле не будет добавлено в sql.
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("mp_user")
public class User {
    /**
     * 主键
     * 注解@TableId,标识字段为主键,如变量名和数据库字段名不一致,可指定
     */
    //@TableId(value = "id")
    /**
     * 主键策略
     * 1.IdType.AUTO:数据库自增
     * 2.IdType.NONE:不配置,默认雪花算法
     * 以下3种策略,主键的Id不能设置值,才会生效
     * 3.IdType.ID_WORKER:雪花算法,数值类型
     * 4.IdType.UUID:UUID,字符串类型
     * 5.IdType.ID_WORKER_STR,雪花算法,字符串类型
     */
    @TableId(value = "id")
    private Long userId;
    /**
     * 姓名
     * 注解@TableField,标识为数据库字段,如变量名和数据库字段名不一致,可指定
     * condition属性,用作在使用实体作为查询条件给MyBatis-Plus直接查询时,可以指定字段是作为等值还是模糊查询,或不等于等条件(默认是等值)
     */
    //@TableField(value = "name", condition = SqlCondition.LIKE)
    /**
     * strategy属性,字段策略,NOT_EMPTY会忽略为null和空字符串的值
     */
    //@TableField(value = "name", strategy = FieldStrategy.NOT_EMPTY)
    @TableField(value = "name")
    private String readName;
    /**
     * 年龄
     */
    private Integer age;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 直属上级
     */
    private Long managerId;
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    /**
     * 备注,非数据库字段,默认MyBatis-Plus会将实体的所有变量名作为数据库字段,如果没有字段就会报错
     * 标识为非数据库字段的3种方式:
     * 1.transient关键字,标识该字段不参与序列化
     * 2.将字段使用static静态标识,但会导致全类共用一个属性,一般不会用
     * 3.使用@TableField注解,将exist属性设置为false,来表示不是数据库字段
     */
    //private transient String remark;
    //private static String remark;
    @TableField(exist = false)
    private String remark;
}

Настройте Dao или вызовите Mapper

UserMapper — это интерфейс Mapper, соответствующий классу User. Каждый пользовательский интерфейс Mapper наследует интерфейс BaseMapper, который в общем определяет класс объекта операции.

BaseMapper предоставляет множество операций с одной таблицей, пакетных операций, условных запросов, запросов на подкачку и т. д., общих методов. Наш пользовательский интерфейс Mapper записывает только специальные методы операций, такие как пользовательские запросы на объединение нескольких таблиц и т. д., которые пока не добавляются и будут добавлены, когда пользовательские методы будут объяснены позже.

public interface UserMapper extends BaseMapper<User> {
}

Тест CRUD

  • Вставьте тест, под тестовым пакетом создайте новый тестовый класс InsertTest

Insert вызывает метод insert() UserMapper, который предоставляется BaseMapper.

//指定可以在Spring环境下使用Junit测试
@RunWith(SpringRunner.class)
//标识该类是SpringBoot测试类,并指定启动类
@SpringBootTest(classes = Application.class)
public class InsertTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void insert() {
        //插入
        User user = new User();
        user.setReadName("刘明强");
        user.setAge(31);
        user.setManagerId(1088248166370832385L);
        user.setCreateTime(LocalDateTime.now());
        int rows = userMapper.insert(user);
        System.out.println("影响记录数:" + rows);
    }

    @Test
    public void insert2() {
        //插入
        User user = new User();
        user.setReadName("向中");
        user.setAge(25);
        user.setManagerId(1088248166370832385L);
        user.setEmail("xd@baomidou.com");
        user.setCreateTime(LocalDateTime.now());
        user.setRemark("我是一个备注信息");
        int rows = userMapper.insert(user);
        System.out.println("影响记录数:" + rows);
    }
}
  • Удалите тест, под тестовым пакетом создайте новый тестовый класс DeleteTest

Удалить, BaseMapper предоставляет несколько

  1. deleteById, удалить по первичному ключу
  2. deleteByMap, поместите условие удаления в карту и удалите ее.
  3. deleteBatchIds, удаление идентификатора пакета
  4. удалить, передать в Wrapper, класс Wrapper является условным конструктором, и в конструкторе есть методы условного добавления, такие как или, получить больше, чем и т. д. Например, вот для удаления записей, возраст которых 27 или старше 41 года.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class DeleteTest {
    @Autowired
    private UserMapper userMapper;

    /**
     * 根据主键Id删除
     */
    @Test
    public void deleteById() {
        int rows = userMapper.deleteById(1288677289033801729L);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 提供一个Map,Map中记录删除条件,进行删除
     */
    @Test
    public void deleteByMap() {
        HashMap<String, Object> columnMap = new HashMap<>();
        columnMap.put("id", 1288676754557825026L);
        columnMap.put("age", 25);
        int rows = userMapper.deleteByMap(columnMap);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 传入多个id,进行批量删除
     */
    @Test
    public void deleteIds() {
        int rows = userMapper.deleteBatchIds(Arrays.asList(1288461659789660161L,
                1288671248736870402L, 1288676285735272450L));
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 使用条件构造器,进行删除
     */
    @Test
    public void deleteByWrapper() {
        LambdaQueryWrapper<User> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(User::getAge, 27)
                .or()
                .gt(User::getAge, 41);
        int rows = userMapper.delete(lambdaQuery);
        System.out.println("影响记录数:" + rows);
    }
}
  • Обновите тест, под тестовым пакетом создайте тестовый класс UpdateTest

BaseMapper предоставляет следующие методы обновления:

  1. updateById, обновление в соответствии с идентификатором первичного ключа объекта
  2. update, передать объект и класс Wrapper, объект устанавливает обновленное значение и условие конфигурации Wrapper.
  3. update, который похож на второй, за исключением того, что при создании класса Wrapper передается класс сущности.
  4. update, используйте только Wrapper, класс сущности передает значение null, а значение обновления устанавливается методом set Wrapper
  5. обновление, как и четвертое, заменить класс Wrapper на LambdaXXXWrapper, поддерживающий лямбда-выражения
  6. Метод update не находится в классе Mapper, но передается в Mapper при создании Wrapper.При вызове метода update Wrapper косвенно вызывается класс Mapper Лиги чемпионов.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UpdateTest {
    @Autowired
    private UserMapper userMapper;

    /**
     * 更新条件和更新信息放在实体,按主键Id更新
     */
    @Test
    public void updateById() {
        User user = new User();
        user.setUserId(1088248166370832385L);
        user.setAge(26);
        user.setEmail("wtf2@baomidou.com");
        int rows = userMapper.updateById(user);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 更新信息放在实体,更新条件使用条件构造器,进行更新
     */
    @Test
    public void updateByWrapper() {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name", "李艺伟")
                .eq("age", 28);
        User user = new User();
        user.setEmail("lyw2019@baomidou.com");
        user.setAge(29);
        int rows = userMapper.update(user, updateWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * UpdateWrapper创建时传入更新实体
     */
    @Test
    public void updateByWrapper2() {
        User whereUser = new User();
        whereUser.setReadName("李艺伟");
        whereUser.setEmail("lyw2019@baomidou.com");
        whereUser.setAge(29);
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(whereUser);
        int rows = userMapper.update(whereUser, updateWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 通过UpdateWrapper进行条件设置,并且通过set方法设置新值
     */
    @Test
    public void updateByWrapper3() {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name", "李艺伟")
                .eq("age", 29)
                .set("age", 30);
        int rows = userMapper.update(null, updateWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 通过UpdateWrapper进行条件设置,并且通过set方法设置新值
     */
    @Test
    public void updateByWrapperLambda() {
        LambdaUpdateWrapper<User> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(User::getReadName, "李艺伟")
                .eq(User::getAge, 30)
                .set(User::getAge, 31);
        int rows = userMapper.update(null, updateWrapper);
        System.out.println("影响记录数:" + rows);
    }

    /**
     * 另外一种Lambda表达式方式
     */
    @Test
    public void updateByWrapperLambdaChain() {
        LambdaUpdateChainWrapper<User> updateWrapper = new LambdaUpdateChainWrapper<>(userMapper);
        boolean isUpdateSuccess = updateWrapper.eq(User::getReadName, "李艺伟")
                .eq(User::getAge, 31)
                .set(User::getAge, 32)
                .update();
        System.out.println("是否更新成功:" + isUpdateSuccess);
    }
}
  • Тест запроса, в тестовом пакете добавьте тестовый класс RetrieveTest
  1. selectById, запрос по идентификатору первичного ключа
  2. selectBatchIds, запрос идентификатора пакета
  3. selectByMap, поместите условия запроса в карту и запросите
  4. selectList, предоставляющий экземпляр класса QueryWrapper, используемый в качестве конструктора условия запроса.
  5. selectMaps предоставляет экземпляр класса QueryWrapper, который используется в качестве конструктора условия запроса, но возвращаемый результат запроса является не объектом класса сущности, а картой, в которой хранятся все атрибуты и значения.
  6. selectObjs, предоставляет экземпляр класса QueryWrapper, который используется в качестве конструктора условия запроса.Этот метод будет возвращать только атрибуты и значения первого столбца результата запроса (остальные поля запрашиваются в sql, но selectObjs( ) метод выбирает только первый столбец Данные столбца, его можно использовать, когда возвращается только первый столбец)
  7. selectCount, статистический запрос, результаты статистического запроса, количество записей
  8. selectOne, запрашивается только 1 часть данных (должен быть только 1 результат запроса, более одного сообщит об ошибке)
  9. selectList, используйте LambdaQueryWrapper для использования лямбда-выражений (преимущества: имитация написания, если это распространенный способ, передать имя поля базы данных, если оно неверно, оно сообщит об ошибке, лямбда-выражения используют ссылки на методы для получения информации о поле)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class RetrieveTest {
    @Autowired
    private UserMapper userMapper;

    /**
     * 根据主键Id查询
     */
    @Test
    public void selectById() {
        User user = userMapper.selectById(1094590409767661570L);
        System.out.println(user);
    }

    /**
     * 一次使用多个Id进行查询
     */
    @Test
    public void selectIds() {
        List<Long> idList = Arrays.asList(1088248166370832385L, 1288460195017072641L, 1094590409767661570L);
        List<User> userList = userMapper.selectBatchIds(idList);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用Mao存放查询字段和条件来进行查询
     */
    @Test
    public void selectByMap() {
        //Map存放查询条件,注意key存放的是数据库的字段名,而不是实体中的变量名
        HashMap<String, Object> columnMap = new HashMap<>();
        columnMap.put("name", "王天风");
        columnMap.put("age", 25);
        List<User> userList = userMapper.selectByMap(columnMap);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 需求:
     * 名字中包含"雨",并且年龄小于40
     * sql: name like %雨% and age < 40
     */
    @Test
    public void selectByWrapper() {
        //直接创建一个条件构造器,获取使用Wrappers工具类
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //小于
                .lt("age", 40);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 需求:
     * 名字中包含"雨",并且年龄大于等于20,且小于等于40,并且email不为空
     * sql: name like '%雨%' and age between 20 and 40 and email is not null
     */
    @Test
    public void selectByWrapper2() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //年龄大于20,并且小于40
                .between("age", 20, 40)
                //不为null
                .isNotNull("email");
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 需求:
     * 名字为王姓,或者年龄大于等于25,按照年龄降序排列,年龄相同按照id升序排列
     * name like '王%' or age>=25 order by age desc,id asc
     */
    @Test
    public void selectByWrapper3() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                //模糊查询,只包含右边一个%,注意这里的字段都是数据库字段,而不是实体的变量名
                .likeRight("name", "王")
                .or()
                //年龄大于等于25
                .ge("age", 25)
                //先按年龄降序排(从大到小)
                .orderByDesc("age")
                //年龄相同的,再按id的升序排(从小到大)
                .orderByAsc("id");
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 创建日期为2019年2月14日,并且直属上级为名字为王姓
     * sql: date_format(create_time,'%Y-%m-%d')='2019-02-14' and manager_id in (select id from user where name like '王%')
     */
    @Test
    public void selectByWrapper4() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                //直接用,不使用占位符,可能会有sql注入的风险
                //.apply("date_format(create_time,'%Y-%m-%d')=2019-02-14")
                //apply占位符查询,目的是为了防止sql注入
                .apply("date_format(create_time,'%Y-%m-%d')={0}", "2019-02-14")
                //inSql子查询
                .inSql("manager_id", "select id from mp_user where name like '王%'");
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 名字为王姓并且(年龄小于40或邮箱不为空)
     * sql: name like '王%' and (age<40 or email is not null)
     */
    @Test
    public void selectByWrapper5() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("name", "王")
                //函数式编程
                //.and(wrapper-> wrapper.lt("age", 40).or().isNotNull("email"))
                .and(new Function<QueryWrapper<User>, QueryWrapper<User>>() {
                    @Override
                    public QueryWrapper<User> apply(QueryWrapper<User> wrapper) {
                        //年龄小于40
                        return wrapper.lt("age", 40)
                                //或者邮箱不为空
                                .or()
                                .isNotNull("email");
                    }
                });

        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 名字为王姓,或者(年龄小于40并且年龄大于20并且邮箱不为空)
     * sql: name like '王%' or (age<40 and age>20 and email is not null)
     */
    @Test
    public void selectByWrapper6() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                //王姓开头
                .likeRight("name", "王")
                //or()使用Function参数的,将获取年龄小于40,并且年龄大于20
                .or(wrapper -> wrapper.lt("age", 40)
                        .gt("age", 20)
                        //并且邮箱不为空的条件用括号包裹起来
                        .isNotNull("email"));
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * (年龄小于40或邮箱不为空)并且名字为王姓
     * sql: (age<40 or email is not null) and name like '王%'
     */
    @Test
    public void selectByWrapper7() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                //nested(),嵌套,就是加括号
                .nested(wrapper -> wrapper.lt("age", 40).or().isNotNull("email"))
                //邮箱不为null
                .likeRight("name", "王");
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 年龄为30、31、34、35
     * sql: age in (30、31、34、35)
     */
    @Test
    public void selectByWrapper8() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .in("age", Arrays.asList(30, 31, 34, 35));
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 9、只返回满足条件的其中一条语句即可
     * sql: limit 1
     */
    @Test
    public void selectByWrapper9() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //字符串拼接到sql,会有sql注入的风险
        queryWrapper.last("limit 1");
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 需求:
     * select中字段不全部出现的查询,例如只查询出id和姓名(默认会查询出实体中的所有字段)
     * sql: select id,name from user where name like '%雨%' and age<40
     */
    @Test
    public void selectByWrapperSupper() {
        //直接创建一个条件构造器,获取使用Wrappers工具类
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper
                //<重点>,相比上面的selectByWrapper,多了调用select()方法,传入需要查询的列名
                .select("id", "name")
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //小于
                .lt("age", 40);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用条件构造器进行查询
     * <p>
     * 需求:
     * select中字段不全部出现的查询,例如只查询出id和姓名、年龄、邮箱(默认会查询出实体中的所有字段)
     * sql: select id,name,age,email from user where name like '%雨%' and age<40
     */
    @Test
    public void selectByWrapperSupper2() {
        //直接创建一个条件构造器,获取使用Wrappers工具类
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //小于
                .lt("age", 40)
                //如果字段比较多,我们每个都写上会比较麻烦,我们可以使用排除法,毕竟只是去掉少量的字段,其他字段都保留
                //参数一:实体类的Class
                //参数二:Predicate函数式接口,test()方法返回boolean,表示是否保留当前遍历到的字段,返回true代表需要,false代表不需要
                .select(User.class, info -> !info.getColumn().equals("create_time")
                        && !info.getColumn().equals("manager_id"));
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 测试动态条件
     */
    @Test
    public void testCondition() {
        String name = "王";
        String email = "";
        condition(name, email);
    }

    /**
     * 查询条件,条件可传可不传
     *
     * @param name  姓名
     * @param email 邮箱
     */
    private void condition(String name, String email) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //手动判空后,加入条件
//        if (StringUtils.isNotEmpty(name)) {
//            queryWrapper.like("name", name);
//        }
//        if (StringUtils.isNotEmpty(email)) {
//            queryWrapper.like("email", email);
//        }
        //上面不够优雅,代码量大
        queryWrapper.like(StringUtils.isNotEmpty(name), "name", name)
                .like(StringUtils.isNotEmpty(email), "email", email);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 使用实体Entity中的字段作为查询条件
     */
    @Test
    public void selectByWrapperEntity() {
        //使用实体作为查询条件加入到where中
        User whereUser = new User();
        whereUser.setReadName("刘雨红");
        whereUser.setAge(32);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(whereUser);
        //再给查询条件加like等操作也是可以的
        queryWrapper
                .like("name", "雨")
                .lt("age", 40);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User result : userList) {
            System.out.println(result);
        }
    }

    /**
     * 使用map作为sql的查询条件,map中的所有非空属性会作为sql的等于条件来拼接
     */
    @Test
    public void selectByWrapperAllEq() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        Map<String, Object> params = new HashMap<>();
        params.put("name", "王天风");
        //age参数,如果为null,会给生成的sql加上age is null,如果想过滤掉为null的属性字段,则将allEq的null2IsNull属性设置为false,默认为true
        params.put("age", null);
        //queryWrapper.allEq(params, false);

        //函数式方式,传入BiPredicate过滤器,test()方法返回当前遍历到的键值对是否加入到条件中,返回true表示加入条件中,返回false代表不加入到条件中
        queryWrapper.allEq(new BiPredicate<String, Object>() {
            @Override
            public boolean test(String key, Object value) {
                //例如过滤掉name的字段
                return !key.equals("name");
            }
        }, params);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User result : userList) {
            System.out.println(result);
        }
    }

    /**
     * 查询,返回列表,但列表里面的元素不是实体,而是一个Map,每个Map就是一条记录的所有属性以键值对的形式存在
     * 当我们查询的字段相比实体字段少很多的时候,使用实体去存放,会有很多属性是null,不是很优雅,我们使用Map存放会更加明确有什么属性和值
     */
    @Test
    public void selectByWrapperMaps() {
        //直接创建一个条件构造器,获取使用Wrappers工具类
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper
                .select("id", "name")
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //小于
                .lt("age", 40);
        List<Map<String, Object>> userList = userMapper.selectMaps(queryWrapper);
        for (Map<String, Object> map : userList) {
            System.out.println(map);
        }
    }

    /**
     * 按照直属上级分组,查询每组的平均年龄、最大年龄、最小年龄。并且只取年龄总和小于500的组。
     * sql:
     * select avg(age) avg_age,min(age) min_age,max(age) max_age
     * from user
     * group by manager_id
     * having sum(age) <500
     */
    @Test
    public void selectByWrapperMaps2() {
        //直接创建一个条件构造器,获取使用Wrappers工具类
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper
                //字段起别名:数据库字段名 别名
                .select("avg(age) avg_age", "min(age) min_age", "max(age) max_age")
                .groupBy("manager_id").having("sum(age) < {0}", 500);

        List<Map<String, Object>> userList = userMapper.selectMaps(queryWrapper);
        for (Map<String, Object> map : userList) {
            System.out.println(map);
        }
    }

    /**
     * selectObjs(),只拿出数据的第一列的数据,其他列都被舍弃(sql中是会查询其他字段的,但是selectObjs()方法只选择第一列的数据)
     * 场景:只返回第一列的时候可以用它
     */
    @Test
    public void selectByWrapperObjs() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .select("id", "name")
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //小于
                .lt("age", 40);
        List<Object> userList = userMapper.selectObjs(queryWrapper);
        for (Object o : userList) {
            System.out.println(o);
        }
    }

    /**
     * 统计查询
     */
    @Test
    public void selectByWrapperCount() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                //会帮我们在sql中添加 COUNT( 1 ),所以我们就不能使用select()方法来指定要查询的列了,否则会作为count的参数来使用
                //.select("id", "name")
                //除非你想count其他字段,就可以使用
                .select("id")
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .like("name", "雨")
                //小于
                .lt("age", 40);
        Integer count = userMapper.selectCount(queryWrapper);
        System.out.println("count:" + count);
    }

    /**
     * 只查询出1条数据(必须查询结果只有1条,多条会报错)
     */
    @Test
    public void selectByWrapperOne() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .select("id", "name")
                //模糊查询,注意这里的字段都是数据库字段,而不是实体的变量名
                .eq("name", "刘雨红");
        User user = userMapper.selectOne(queryWrapper);
        System.out.println(user);
    }

    /**
     * Lambda条件构造器
     * 好处:仿误写,如果是普通方式,传入数据库字段名,如果写错了就会报错,Lambda表达式使用方法引用来获取字段信息
     */
    @Test
    public void selectByWrapperLambda() {
        //Lambda条件构造器的3种创建方式
//        LambdaQueryWrapper<User> queryWrapper = new QueryWrapper<User>().lambda();
//        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper
                //where name like '%雨%'
                .like(User::getReadName, "雨")
                //and age < 40
                .lt(User::getAge, 40);
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 查询,姓名为王姓,并且(年龄小于40岁或者邮箱不为空)
     */
    @Test
    public void selectByWrapperLambda2() {
        LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper
                //where name like '%王%'
                .likeRight(User::getReadName, "王")
                //and (age < 40 or email is not null)
                .and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
        List<User> userList = userMapper.selectList(queryWrapper);
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 查询,姓名为王姓,并且年龄大于等于20
     */
    @Test
    public void selectByWrapperLambda3() {
        LambdaQueryChainWrapper<User> chainWrapper = new LambdaQueryChainWrapper<>(userMapper);
        List<User> userList = chainWrapper
                //姓名为王姓
                .like(User::getReadName, "雨")
                //年龄大于等于20
                .ge(User::getAge, 20)
                .list();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
  • Пейджинговый запрос

BaseMapper также предоставляет связанные методы для запросов пейджинга.

  1. selectPage, передать объект страницы параметра подкачки и условный конструктор QueryWrapper, вернуть объект IPage, и есть параметры подкачки, такие как общее количество страниц и общее количество записей в IPage.
  2. selectMapsPage, второй метод пейджинга, общий тип объекта пейджинга — Map, а не объект класса сущностей, то есть поля и значения класса сущностей инкапсулированы в Map
  3. selectPage, входящий объект пейджинга, конструктор настраивает для параметра isSearchCount значение false, что означает пейджинг, но не запрашивает общее количество записей. В сценариях, где общее количество записей не требуется, один запрос можно уменьшить, например со стороны приложения загружать больше не нужно.Общее количество записей необходимо оценивать
/**
 * 分页查询,分页对象的泛型是实体类
 */
@Test
public void selectPage() {
    //直接创建一个条件构造器,获取使用Wrappers工具类
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.ge("age", 26);
    Page<User> page = new Page<>(1, 2);

    //分页
    IPage<User> iPage = userMapper.selectPage(page, queryWrapper);

    System.out.println("总页数:" + iPage.getPages());
    System.out.println("总记录数:" + iPage.getTotal());
    List<User> userList = iPage.getRecords();
    for (User user : userList) {
        System.out.println(user);
    }
}

/**
 * 第二种分页方式,分页对象的泛型是Map
 */
@Test
public void selectPage2() {
    //直接创建一个条件构造器,获取使用Wrappers工具类
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.ge("age", 26);
    Page<User> page = new Page<>(1, 2);

    //第二种分页,但返回的IPage泛型类型是Map类型,就是将实体类的字段和值封装到了Map中
    IPage<Map<String, Object>> iPage = userMapper.selectMapsPage(page, queryWrapper);

    System.out.println("总页数:" + iPage.getPages());
    System.out.println("总记录数:" + iPage.getTotal());
    List<Map<String, Object>> userList = iPage.getRecords();
    for (Map<String, Object> map : userList) {
        System.out.println(map);
    }
}

/**
 * 分页,不查询总记录数(默认会查,会查询总数和分页查询,会查询2次,例如不断上拉加载的场景是不需要查总记录数的,就可以不进行查询)
 */
@Test
public void selectPage3() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.ge("age", 26);
    //isSearchCount传false,代表不进行查询总记录数,少一次查询
    Page<User> page = new Page<>(1, 2, false);

    //分页
    IPage<User> iPage = userMapper.selectPage(page, queryWrapper);

    System.out.println("总页数:" + iPage.getPages());
    List<User> userList = iPage.getRecords();
    for (User user : userList) {
        System.out.println(user);
    }
}

Пользовательский метод сопоставления

Предоставьте собственный метод Mapper

Добавьте 2 пользовательских метода в UserMapper.

Есть два способа настройки метода, аннотация и XML.Здесь в основном используется XML, но также предоставляется аннотация.Поскольку они не могут существовать одновременно, они закомментированы.При необходимости вы можете обратиться к нему снова.

  1. selectAll, пользовательские параметры запроса, условия запроса предоставляются входящей оболочкой, параметры должны быть аннотированы с помощью @Param, а имена параметров фиксированы, как Constants.WRAPPER
  2. selectUserPage, настраиваемые параметры запроса с функцией подкачки, необходимо передать 2 параметра, объект Page и объект Wrapper, один и тот же параметр должен быть аннотирован с помощью @Param, а имя параметра фиксировано, оба параметра Constants.WRAPPER
public interface UserMapper extends BaseMapper<User> {
    /**
     * 自定义Sql查询,让Wrapper中配置的条件和自定义sql进行组合
     */
    //@Select("select * from mp_user ${ew.customSqlSegment}")
    List<User> selectAll(@Param(Constants.WRAPPER) Wrapper<User> wrapper);

    /**
     * 自定义Sql,进行分页查询
     */
    IPage<User> selectUserPage(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
}

XML-файл для настройки Mapper

Существуют настраиваемые интерфейсы Mapper, использующие либо аннотации, либо XML, обычно использующие XML. В каталоге ресурсов создайте многослойную папку com/imooc/mybatisplus.mapper и создайте в ней новый файл XML: UserMapper.xml

Примечание. Имя интерфейса Mapper и соответствующего XML-файла должны совпадать! а пространство имен — это полное имя класса 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">
<!-- namespace,命名空间必须和Dao接口一样 -->
<mapper namespace="com.imooc.mybatisplus.dao.UserMapper">
    <!-- 查询所有 -->
    <select id="selectAll" resultType="User">
        select * from mp_user ${ew.customSqlSegment}
    </select>

    <!-- 分页查询 -->
    <select id="selectUserPage" resultType="User">
        select * from mp_user ${ew.customSqlSegment}
    </select>
</mapper>

Способы тестирования пользовательского SQL

Условия настраиваются в экземпляре Wrapper, а запрашиваемые поля предоставляются пользовательскими операторами SQL.

Это запросы selectAll() all и selectMyPage() соответственно.

/**
 * 自定义sql
 */
@Test
public void selectMy() {
    LambdaQueryWrapper<User> lambdaQuery = Wrappers.lambdaQuery();
    lambdaQuery
            //where name like '%王%'
            .likeRight(User::getReadName, "王")
            //and (age < 40 or email is not null)
            .and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
    List<User> userList = userMapper.selectAll(lambdaQuery);
    for (User user : userList) {
        System.out.println(user);
    }
}

/**
 * 自定义查询分页
 */
@Test
public void selectMyPage() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.ge("age", 26);
    Page<User> page = new Page<>(1, 2);
    //自定义分页查询
    IPage<User> iPage = userMapper.selectUserPage(page, queryWrapper);
    System.out.println("总页数:" + iPage.getPages());
    System.out.println("总记录数:" + iPage.getTotal());
    List<User> userList = iPage.getRecords();
    for (User user : userList) {
        System.out.println(user);
    }
}

Общего обслуживания

Мы использовали общий Mapper выше, и, как правило, у нас будет служба, которая объединяет несколько Mapper для завершения бизнес-логики.

MyBatis-Plus также предоставляет нам общее сервисное решение.

Установить интерфейс службы и реализацию

Создайте пакет службы, интерфейс UserService, наследуйте интерфейс IService и в общем запишите тип класса сущности.

public interface UserService extends IService<User> {
}

Создайте класс реализации UserServiceImpl, наследуйте ServiceImpl, в общем случае передайте интерфейс Mapper и тип класса сущности

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

контрольная работа

  1. getOne, получить 1 результат, если будет несколько результатов, будет сообщено об ошибке. Если вы не хотите сообщать об ошибке, возьмите первую по умолчанию, а затем установите для последующего свойства throwEx значение false.
  2. saveBatch, сохраняет несколько объектов в таблицу базы данных, если поле id объекта объекта имеет значение, оно обновляется, если оно равно нулю, значит новое
  3. lambdaQuery, список вызовов, запрос цепного программирования
  4. lambdaUpdate, обновление вызова, обновление цепного программирования
  5. lambdaUpdate, удаление вызова, удаление цепного программирования
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ServiceTest {
    @Autowired
    private UserService userService;

    /**
     * 获取一个结果
     */
    @Test
    public void getOne() {
        //查询1个,如果有多个结果,会报错。
        //后面的throwEx属性代表如果查询出多个,是否抛出异常,默认为true,如果有多个就抛出异常
        //如果设置为false,则不抛出异常,打印警告,并且拿取第一个返回
        User result = userService.getOne(Wrappers.<User>lambdaQuery()
                .gt(User::getAge, 25), false);
        System.out.println(result);
    }

    /**
     * 批量插入
     */
    @Test
    public void batch() {
        User user1 = new User();
        user1.setReadName("徐丽1");
        user1.setAge(28);

        User user2 = new User();
        user2.setReadName("徐丽2");
        user2.setAge(29);
        List<User> userList = Arrays.asList(user1, user2);
        boolean isSuccess = userService.saveBatch(userList);
        System.out.println("是否批量插入成功:" + isSuccess);
    }

    /**
     * 批量操作,如果设置了id,则先查询,有则更新,无则做插入
     */
    @Test
    public void batch2() {
        User user1 = new User();
        user1.setReadName("徐丽3");
        user1.setAge(28);

        User user2 = new User();
        user2.setUserId(1289004540795330562L);
        user2.setReadName("徐力");
        user2.setAge(30);
        List<User> userList = Arrays.asList(user1, user2);
        boolean isSuccess = userService.saveOrUpdateBatch(userList);
        System.out.println("是否批量插入成功:" + isSuccess);
    }

    /**
     * 链式编程,查询
     */
    @Test
    public void chain() {
        List<User> userList = userService.lambdaQuery()
                .gt(User::getAge, 25)
                .like(User::getReadName, "雨")
                .list();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    /**
     * 链式编程,更新
     */
    @Test
    public void chain2() {
        boolean isSuccess = userService.lambdaUpdate()
                .eq(User::getAge, 25)
                .set(User::getAge, 26)
                .update();
        System.out.println("是否更新成功:" + isSuccess);
    }

    /**
     * 链式编程,删除
     */
    @Test
    public void chain3() {
        boolean isSuccess = userService.lambdaUpdate()
                .eq(User::getAge, 24)
                .remove();
        System.out.println("是否删除成功:" + isSuccess);
    }
}

режим дополненной реальности

Режим AR, то есть режим ActiveRecord, использует операции класса сущностей для добавления, удаления, проверки и изменения, а классы сущностей необходимо модифицировать.

  1. Класс сущностей наследует класс Model, а универсальный тип переходит в тип класса сущностей.
  2. Создать сериализованный UID
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("mp_user")
public class User extends Model<User> {
    private static final long serialVersionUID = 1L;

    //...省略字段,和之前的没区别
}

контрольная работа

  1. вставить, вставить данные
  2. selectById, основанный на запросе первичного ключа сущности, также может передаваться в параметрах метода.
  3. updateById, обновить набор данных объекта в соответствии с первичным ключом объекта.
  4. deleteById, удаление в соответствии с первичным ключом объекта, также может передаваться в параметре метода
  5. insertOrUpdate, новое или обновленное, в зависимости от того, имеет ли первичный ключ объекта значение, если есть значение, оно обновляется, а если оно равно null, оно новое
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ARTest {
    /**
     * 插入数据
     */
    @Test
    public void insert() {
        User user = new User();
        user.setReadName("刘花");
        user.setAge(29);
        user.setEmail("lh@baomidou.com");
        user.setManagerId(1088248166370832385L);
        user.setCreateTime(LocalDateTime.now());
        boolean isSuccess = user.insert();
        System.out.println("是否插入成功:" + isSuccess);
    }

    /**
     * 按主键进行查询
     */
    @Test
    public void selectById() {
        User user = new User();
        User result = user.selectById(1288460195017072641L);
        System.out.println(result);
    }

    /**
     * 按实体类上的主键属性进行查询
     */
    @Test
    public void selectById2() {
        User user = new User();
        user.setUserId(1288839790966910978L);
        User result = user.selectById();
        System.out.println(result);
    }

    /**
     * 更新,按实体类上的主键属性
     */
    @Test
    public void updateById() {
        User user = new User();
        user.setUserId(1288839790966910978L);
        user.setReadName("刘草");
        boolean isSuccess = user.updateById();
        System.out.println("是否更新成功:" + isSuccess);
    }

    /**
     * 按主键删除
     */
    @Test
    public void deleteById() {
        User user = new User();
        boolean isSuccess = user.deleteById(1288839790966910978L);
        System.out.println("是否删除成功:" + isSuccess);
    }

    /**
     * 按实体类上的主键属性进行删除
     */
    @Test
    public void deleteById2() {
        User user = new User();
        user.setUserId(1288839790966910978L);
        boolean isSuccess = user.deleteById();
        System.out.println("是否删除成功:" + isSuccess);
    }

    /**
     * 先查询主键Id的记录,如果有则更新,无则新增
     */
    @Test
    public void insertOrUpdate() {
        User user = new User();
        //设置了主键Id,则会先查询,有记录则更新,无则删除
        //user.setUserId(1288842439342780417L);
        user.setReadName("张强");
        user.setAge(29);
        user.setEmail("lh@baomidou.com");
        user.setManagerId(1088248166370832385L);
        user.setCreateTime(LocalDateTime.now());
        boolean isSuccess = user.insertOrUpdate();
        System.out.println("是否插入成功:" + isSuccess);
    }
}