Integración detallada de SpringBoot y Mybatis-Plus y tutoriales prácticos comunes (paginación de pagehelper + manejo de fallas de actualización automática)

Prólogo MyBatis -
Plus (MP para abreviar) es una herramienta de mejora de MyBatis desarrollada por el equipo chino de Baomidou, con el fin de simplificar el desarrollo y mejorar la eficiencia. , potente, muy fácil de usar (muy recomendable)

  1. Este tutorial intentará simular el uso del entorno de desarrollo real, que es relativamente completo y cubre el proceso más utilizado de mybatis-plus
  2. El contenedor de dependencias de maven utilizado en este proyecto es una versión más nueva. También lo uso yo mismo. No se preocupe por mp y otras dependencias de versiones relacionadas. Puede usarlo con confianza.
  3. Después de la introducción de mybatis-plus, no es necesario introducir la dependencia de mybatis-springboot, y también es compatible con la escritura de la interfaz del mapeador y la escritura de sql personalizado en xml (mantenga el original sin cambios)

Uno: Primero cree una tabla de estudiantes: t_student

CREATE TABLE `t_student` (
  `id` bigint(20) NOT NULL,
  `name` varchar(16) NOT NULL COMMENT '姓名',
  `gender` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别(1男,2女,0未知)',
  `major_id` int(11) NOT NULL COMMENT '所属专业id',
  `phone` varchar(16) NOT NULL COMMENT '手机号',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `del_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标记(0未删除,1已删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
  1. La identificación de la tabla de estudiantes simulada aquí es de tipo bigint y no se incrementa automáticamente (generalmente, no se recomienda usar claves primarias de incremento automático para tablas con una gran cantidad de datos)
  2. Hay create_time, update_time que existen en casi todas las tablas, y un indicador de eliminación lógica del_flag, que tiene un valor predeterminado de 0 para pseudo-eliminación Estos tres campos generalmente se consideran como un todo, porque son fijos, cada tabla puede tener

Dos: Crear proyecto MP-Demo, configurar dependencias y entorno

1. dependencias expertas

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
    </parent>

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

    <dependencies>
        <!--  spring相关jar包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 数据库连接jar包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!-- pageHelper分页(注意:为避免和mybatis-plus的冲突,需排除部分依赖) -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.3.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 必备工具包 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${
    
    project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  1. Arriba, solo introduje algunos paquetes jar utilizados en este caso, si tiene otras necesidades, puede volver a importarlos usted mismo
  2. La versión springboot es 2.5.2, la versión mp es 3.4.2, el grupo de conexiones no usa druid y se usa el hikari predeterminado
  3. En cuanto a la función de paginación, de hecho, mp también proporciona un complemento de paginación para usar, pero no me gusta usar el integrado. Me resulta problemático de usar y más intrusivo para el código, así que sigo elija usar pagehelper para la paginación, que es más conveniente

2. Crear un paquete de estructura de proyectoestructura del proyecto

El paquete del controlador es el paquete del procesador, el paquete po es el paquete de la clase de entidad, el mapeador es la interfaz del mapeador de mybatis y el servicio es la interfaz de servicio + la clase de implementación impl. En cuanto al paquete de resultados, es mi interfaz unificada personalizada. Paquete de devolución Siéntete libre de tener el tuyo también.

3. aplicación.yml

server:
  port: 8080

spring:
  application:
    name: MP-Demo

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/my_test?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 5
      maximum-pool-size: 15
      auto-commit: true
      idle-timeout: 30000
      pool-name: ${
    
    spring.application.name}-HikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰法则(可省略此行,默认已开启)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL打印(就是每次操作数据库都会在控制台打印出来SQL语句,建议使用熟练以后注掉此行,不然日志很多)
  type-aliases-package: com.taoge.po # 实体类包路径(也可省略此行,不用配置)
  mapper-locations: classpath*:mapper/**/*Mapper.xml # 存放sql语句的xml文件目录
  global-config: # 此项是否配置根据实际项目来
    db-config:
      logic-delete-field: delFlag # 全局逻辑删除的实体字段名(这里填表字段名好像也可以)
      logic-delete-value: 1 # 逻辑已删除值(为1,表示已删除)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为0, 表示未删除)

# 分页插件PageHelper(当reasonable=false时,若当前页超过最大页数则不返回数据,为true则依然会返回最后一页数据)
pagehelper:
  helper-dialect: mysql
  pageSizeZero: true
  params: count=countSql
  reasonable: false
  support-methods-arguments: true
  1. Aquí hablamos principalmente sobre la configuración de mybatis-plus: de hecho, es muy similar a la configuración anterior de mybatis, como habilitar la regla de joroba, la ruta de clase de entidad y la ruta xml, todas las cuales son las mismas que antes.
  2. En cuanto a la configuración de log-impl, en realidad imprime el sql de ejecución de mp. Personalmente, recomiendo que lo habilite cuando lo use por primera vez. Una vez que sea experto en usarlo, puede comentar o eliminar esta línea, para que el log será menor, para que pueda verificar el problema a través del registro en el futuro.
  3. Concéntrese en la configuración de configuración global: aquí está la lápida global delFlag, es decir, pseudo-eliminación. Durante la consulta automática, mp agregará automáticamente del_flag = 0 después de la instrucción where para filtrar los datos eliminados, y también agregará automáticamente esta condición al actualizar y eliminar.
    Pero si escribe declaraciones SQL manualmente, debe agregarlas manualmente. En cuanto a la configuración de esta función, depende de la situación, después de todo, una vez configurada, puede ser más obligatoria, por lo que no es necesario configurar este campo de desecho global, puede agregar manualmente la condición del_flag = 0 cuando consultando En resumen, es flexible de acuerdo con la situación real.

4. Aplicación MP

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.taoge.mapper") // 扫描mapper接口包
@SpringBootApplication
public class MPApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(MPApplication.class, args);
    }
}

5. Cree un relleno automático debajo del paquete del controlador

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * MyBatis-Plus自定义填充处理器
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    

    // 默认策略:如果属性有值则不覆盖,如果填充值为null则不填充

    private static final String CREATE_TIME = "createTime";
    private static final String UPDATE_TIME = "updateTime";

    /**
     * 做insert插入时自动填充的值(这里一般是create_time和update_time)
     */
    @Override
    public void insertFill(MetaObject metaObject) {
    
    
        if (metaObject.hasGetter(CREATE_TIME) && metaObject.hasGetter(UPDATE_TIME)) {
    
     // 实体类有get方法,就是有这个字段
            LocalDateTime localDateTime = LocalDateTime.now();
            this.strictInsertFill(metaObject, CREATE_TIME, () -> localDateTime, LocalDateTime.class); // 起始版本 3.3.3(推荐)
            this.strictInsertFill(metaObject, UPDATE_TIME, () -> localDateTime, LocalDateTime.class); // 起始版本 3.3.3(推荐)
        }
    }

    /**
     * 做update更新时自动填充的值(更新就只针对update_time)
     */
    @Override
    public void updateFill(MetaObject metaObject) {
    
    
        if (metaObject.hasGetter(UPDATE_TIME)) {
    
    
            metaObject.setValue(UPDATE_TIME, LocalDateTime.now());
            // this.strictUpdateFill(metaObject, UPDATE_TIME, () -> LocalDateTime.now(), LocalDateTime.class);
        }
    }
}
  1. Esperamos que los campos create_time y update_time en la tabla se puedan completar automáticamente cuando sean nuevos o actualizados, y no es necesario configurarlos manualmente, por lo que se configura el autocompletado anterior.
  2. El relleno que se muestra aquí en realidad puede ser más rico, como agregar: createBy, updateBy, es decir, el creador y el actualizador, que se pueden agregar de acuerdo con la situación real, pero generalmente los dos anteriores (tiempo de creación, tiempo de actualización) son suficientes
  3. El tipo de hora aquí no usa el tipo Fecha, sino el tipo LocalDateTime en jdk 8. Todos los tipos de hora en el proyecto usan LocalDateTime (se recomienda LocalDateTime)
  4. En realidad, hay un pequeño problema con el código oficial de actualización automática: cuando consulta primero y luego actualiza, encontrará que el valor de updateTime no está actualizado. Esto se debe al problema de la política predeterminada: si hay un valor, no lo hará. se sobrescribirá, así que directamente metaObject.setValue(), comente la siguiente línea, esto evitará este error

6. Cree una clase de entidad
(1) Cree un nuevo paquete base bajo el paquete po y luego cree una BaseEntity

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

/**
 * @author rlt
 * @date 2021/12/15 14:57
 */
@Getter
@Setter
public abstract class BaseEntity implements Serializable {
    
    

    private static final long serialVersionUID = -922201555125882232L;

    /** 创建时间 */
    @TableField(fill = FieldFill.INSERT) // 插入自动填充
    private LocalDateTime createTime;

    /** 更新时间 */
    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入或更新时自动填充
    private LocalDateTime updateTime;

    /** 删除标记(0未删除,1已删除) */
//    @TableLogic // 此注解表示该字段是逻辑删除字段(这里注掉是因为现用的mp版本是3.4.2,从3.3.0版本后就可以省略该注解)
    private Integer delFlag;
}
  1. Dado que el relleno automático se configuró anteriormente, la anotación correspondiente debe agregarse al campo cuando se usa aquí, de modo que no sea necesario establecer manualmente el valor al insertar y actualizar
  2. Tenga en cuenta: el autorrelleno de actualización configurado anteriormente también fallará en un caso especial, es decir, cuando personalizamos y actualizamos algunos campos (como campos de actualización personalizados: cambiar el número de teléfono móvil de la persona cuyo nombre = Zhang San a xxxx), este pequeño error aún no ha sido solucionado directamente por el oficial, pero se puede resolver de otra manera, que se presentará más adelante

(2) Cree una nueva clase de estudiante en el paquete po y herede BaseEntity

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.taoge.po.base.BaseEntity;
import lombok.Data;

import java.io.Serializable;

/**
 * 学生po
 */
@Data
@TableName(value = "t_student") // 指定数据库里对应的表名
public class Student extends BaseEntity {
    
    

    private static final long serialVersionUID = 3819669465179936254L;

    // @TableId(type = IdType.AUTO):表主键id注解,AUTO表示id是自增的
    // @TableField(value = "major_id"):表字段注解,value表示对应表里的字段,但只要表字段命名规范,实体类里驼峰命名,就可不加此注解

    /** 学生id */
//    @TableId(type = IdType.AUTO) // 如果你的表主键id是自增的,就加上这行注解,我这里的id是非自增,所以不用加@TableId注解
    private Long id;

    /** 姓名 */
    private String name;

    /** 性别:1男,2女,0未知 */
    private Integer gender;

    /** 专业id */
//    @TableField(value = "major_id") // 指定对应表里字段,为了方便,此注解可以不加
    private Integer majorId;

    /** 手机号 */
    private String phone;

    //======下面是我额外加的字段=========

    /** 所属专业名称 */
    @TableField(exist = false) // 非表字段(这种就得加上@TableField注解,exist默认为true,为false时表示非数据表字段)
    private String majorName; // 这个字段在mybatis-plus自动查询的时候就不会带上,不过当你在xml里自定义写多表查询sql时可映射此字段
}

7. Crear interfaz de mapeador: StudentMapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.taoge.po.Student;

public interface StudentMapper extends BaseMapper<Student> {
    
    
}
  1. La interfaz del mapeador hereda directamente la interfaz BaseMapper de mp y especifica que la clase de entidad correspondiente es Student
  2. No es necesario agregar anotaciones como @Mapper y @Repository en la interfaz del mapeador, porque la anotación @MapperScan se agregó a la clase de inicio para escanear todo el paquete del mapeador.

8. Crear interfaz de servicio y clase de implementación
(1) Interfaz StudentService

import com.baomidou.mybatisplus.extension.service.IService;
import com.taoge.po.Student;

import java.util.List;

public interface StudentService extends IService<Student> {
    
    
}

La interfaz de servicio hereda directamente la interfaz IService de mp, y también especifica la clase de entidad como Student, de modo que se pueden usar los métodos que vienen con IService.

(2) Clase de implementación

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.taoge.mapper.StudentMapper;
import com.taoge.po.Student;
import com.taoge.service.StudentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {
    
    
}

Aquí hay que heredar ServiceImpl, especificar el mapeador y la clase correspondientes, para que se complete la operación de cuajada automática

Tres: crear un controlador de prueba

1. Nueva interfaz

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.taoge.mapper.StudentMapper;
import com.taoge.po.Student;
import com.taoge.result.CodeMsg;
import com.taoge.result.Result;
import com.taoge.service.StudentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author rlt
 * @date 2021/12/15 15:59
 */
@Slf4j
@RestController
@RequestMapping("/student")
@RequiredArgsConstructor // lombok注解:可代替@Autowired,但是必须加上final(其实就是通过构造器来注入)
public class StudentController {
    
    

    // 为了演示,我这里把service和mapper都注入了进来,实际开发只需注入一个service,主要是介绍service的mapper对的操作区别
    // 其实service和mapper在功能上基本上是一样的,service有的功能mapper也有,只是他两用的时候在方法名字上有区别
    // service里的方法内部再去调用了mapper,可以点进去看下源码就明白了
    // 实际中,可以在service中自定义一些方法,在serviceImpl中再调用mapper,当明白了mybatisplus的使用后可自由发挥

    private final StudentService studentService;
    private final StudentMapper studentMapper;

    /**
     * 新增
     */
    @PostMapping("/add")
    public Result add() {
    
    
        // service演示新增
        Student student = new Student();
        student.setName("张三");
        student.setGender(1);
        student.setMajorId(1);
        student.setPhone("18300001111");
        boolean save = studentService.save(student);
        // mapper演示新增
        Student student1 = new Student();
        student1.setName("小芳");
        student1.setGender(2);
        student1.setMajorId(1);
        student1.setPhone("18300002222");
        int insert = studentMapper.insert(student1); // 上面的save内部也是调用insert,只是使用时名称不一样
        log.info("【add】save={}, insert={}", save, insert);
        // 批量插入
        Student student2 = new Student();
        student2.setName("小三");
        student2.setGender(2);
        student2.setMajorId(2);
        student2.setPhone("18300003333");
        Student student3 = new Student();
        student3.setName("小明");
        student3.setGender(1);
        student3.setMajorId(1);
        student3.setPhone("18300004444");
        List<Student> studentList = new ArrayList<>();
        studentList.add(student2);
        studentList.add(student3);
        studentService.saveBatch(studentList); // saveBatch,只能用service去调用
        return Result.success(save && insert == 1);
    }
}

Dado que la identificación de la clave principal de la tabla del estudiante no es incremental, aunque no tengo un identificador establecido aquí, mybatis-plus generará automáticamente una identificación larga para nosotros. Por supuesto, también puede usar la estrategia de generación de identificación en su proyecto para generarlo, y luego establecer manualmente Id, como copos de nieve El algoritmo genera id, que será mejor

Use cartero para solicitar: http://localhost:8080/student/add, el proceso de depuración es el siguiente:
agregar depuración
datos de la tabla
2. Interfaz de consulta

   /**
     * 查询
     */
    @GetMapping("/query/{id}")
    public Result query(@PathVariable("id") Long id) {
    
    
        // 1. 根据id查询
        Student student = studentService.getById(id);
//        student = studentMapper.selectById(id); // 等同于上面一行
        log.info("【query】student={}", JSON.toJSONString(student));

        // 2. 查询所有(查询列表时可以加上pagehelper分页,这里就不演示了,自己尝试,很简单)
        List<Student> studentAllList = studentService.list(); // 或者写成.list(null),两个是一样的
//        List<Student> studentAllList = studentMapper.selectList(null); // 等价于上面的写法
        log.info("【query】studentAllList={}", JSON.toJSONString(studentAllList));

        // 3. 查询器查询

        // 条件构造器(不建议用这个):查询name=张三
        QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "张三"); // 条件查询(eq是等于,即name='张三')
        queryWrapper.select("id", "name", "major_id"); // 查询指定字段(不加这行会默认查询所有字段,注意:这里的字段填的是表里的字段)
        Student student1 = studentService.getOne(queryWrapper); // getOne:表示只去查询一条结果
//        Student student1 = studentMapper.selectOne(queryWrapper); 等同于上面一行
        log.info("【query】student1={}", JSON.toJSONString(student1)); // 注意:由于我上面加了指定字段查询,所以这里只有查的这几个字段才有值

        // lambda查询构造器(推荐):查询major_id=1的学生列表
        LambdaQueryWrapper<Student> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Student::getMajorId, 1); // 条件:where major_id = 1(如果多条件查询可以继续.eq这样添加)
        lambdaQueryWrapper.select(Student::getId, Student::getName, Student::getGender); // 我只想查这三个字段
        lambdaQueryWrapper.orderByDesc(Student::getCreateTime); // 排序:order by create_time desc
        List<Student> studentList = studentService.list(lambdaQueryWrapper);
        log.info("【query】studentList={}", JSON.toJSONString(studentList));

        // 这里再写一个service里我们自定义的方法:查询专业为1且性别为女
        List<Student> studentList2 = studentService.getByMajorIdAndGender(1, 2);
        log.info("【query】studentList2={}", JSON.toJSONString(studentList2));
        return Result.success(true);
    }

StudentServiceImpl: agregue el siguiente método

    // 这里可以注入mapper进来进行操作,也可以用mp的baseMapper进行操作(因为mapper继承了baseMapper)
    // 如果你有复杂的sql或者自定义的sql,那么就按照原生的mybatis那样操作,去mapper里写接口,xml里写sql
    private final StudentMapper studentMapper;

    @Override
    public List<Student> getByMajorIdAndGender(Integer majorId, Integer gender) {
    
    
        // 同样,使用LambdaQueryWrapper
        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Student::getMajorId, majorId).eq(Student::getGender, gender);

        // 由于StudentService继承了IService,所以这里可以直接用service里的方法,也可以用mapper
        // 用service操作
        List<Student> serviceList = this.list(queryWrapper); // 这里实现类里可直接使用IService里的方法
        // 用mapper操作
        List<Student> mapperList = baseMapper.selectList(queryWrapper); // 这里也可以用:studentMapper.selectList(queryWrapper)
        log.info("【getByMajorIdAndGender】serviceList={}", serviceList);
        log.info("【getByMajorIdAndGender】mapperList={}", mapperList); // 两条查询肯定是一样的
        return mapperList;
    }

Lo que se puede hacer con el servicio anterior también se puede hacer con el mapeador, porque el mapeador en el servicio todavía usa el mapeador para realizar consultas, y puede probarlo usted mismo. Además, hay consultas especiales más comúnmente utilizadas, como fuzzy consultas, que se introducen al final

Use la solicitud del cartero: http://localhost:8080/student/query/1472863272568754178
consulta de identificación
3. Interfaz de actualización

   /**
     * 更新
     */
    @PostMapping("/update/{id}")
    public Result update(@PathVariable("id") Long id) {
    
    
        // 1. 根据id更新
        Student student = new Student();
        student.setId(id);
        student.setName("张三三"); // 根据id更新姓名为张三三
        boolean updateById = studentService.updateById(student); // 会自动填充updateTime
        log.info("【update】updateById={}", updateById);

        // 2. 指定条件更新:把姓名=张三三的专业改为3
        LambdaUpdateWrapper<Student> updateWrapper = new LambdaUpdateWrapper<>(); // 创建lambda更新器
        updateWrapper.set(Student::getMajorId, 3); // 更新器中set是要更新的字段,是最后结果
        updateWrapper.set(Student::getUpdateTime, DateUtil.toLocalDateTime(new Date())); // 这里需手动setUpdateTime,因为这里更新的字段值是我们自定义的,我们自定义了updateWrapper.set()
        updateWrapper.eq(Student::getName, "张三三"); // 而eq是更新条件,即sql是:set major_id = 3 where name = '张三三'
        boolean update = studentService.update(updateWrapper);
        log.info("【update】update={}", update);

        // 这里提一点,如果我要吧表中的某一个字段由原先的非null改为null,那么就不能用updateById,不会生效
        // 建议用指定字段更新来实现,当然也可以用原生的手动写sql语句实现
        // 比如上面的,我现在要把姓名=张三三的专业改为null,则改为updateWrapper.set(Student::getMajorId, null)
        return Result.success(updateById && update);
    }

Nota: cuando se actualicen las condiciones especificadas, el llenado automático de updateTime no será válido, porque nosotros personalizamos esta actualización y mp no se llenará, por lo que setUpdateTime se agrega manualmente

Solicitud de cartero: http://localhost:8080/student/update/1472863272568754178
actualización de identificación
actualización de condición
4. Eliminar interfaz

   /**
     * 删除
     */
    @PostMapping("/delete/{id}")
    public Result delete(@PathVariable("id") Long id) {
    
    
        // 一般删除前可以先查一下,不为空再删除
        Student student = studentService.getById(id);
        if (null == student) {
    
    
            return Result.error(CodeMsg.DELETE_FAIL);
        }

        // 说明:removeById表示根据id删除(常用),还有根据id批量删除,方法是removeByIds
        // 但如果yml里配的是伪删除,那么removeById由原本的执行delete变成走update,即把del_flag字段值改为1

        // 注意:由于现yml里配置了伪删除,如果走mp的删除接口,则会有个小bug:数据虽删了但update_time没有自动填充改变,还是原来的时间
        // 为避免这个bug,既然是伪删,我建议还是全走update,手动更新del_flag和update_time这两个字段,或者手动在xml里写sql实现

        // 1. 根据id删除(走根据id更新指定字段)
        LambdaUpdateWrapper<Student> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Student::getDelFlag, 1);
        updateWrapper.set(Student::getUpdateTime, DateUtil.toLocalDateTime(new Date()));
        updateWrapper.eq(Student::getId, id);
        boolean update = studentService.update(updateWrapper);
        log.info("【delete】update={}", update);

        // 2. 条件删除:删除专业=5的女学生(同样,伪删除走指定条件更新)
        updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Student::getDelFlag, 1);
        updateWrapper.set(Student::getUpdateTime, DateUtil.toLocalDateTime(new Date()));
        updateWrapper.eq(Student::getMajorId, 5);
        updateWrapper.eq(Student::getGender, 2);
        boolean update1 = studentService.update(updateWrapper); // 由于没有专业id=5的数据,所以返回false
        log.info("【delete】update1={}", update1);

//        // 如果你的表没有类似的del_flag字段,直接是真删,则删除为下面2种:
//        // 根据id删除
//        studentService.removeById(id); // 或者studentMapper.deleteById(id)
//        // 根据条件删除
//        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(Student::getMajorId, 5);
//        queryWrapper.eq(Student::getGender, 2);
//        studentService.remove(queryWrapper);
        return Result.success(update, "删除成功");
    }

Acceso de cartero: http://localhost:8080/student/delete/1472863272568754178
id borrado falso
5. Introducción a la consulta especial
Primero cambie la delFlag del Zhang Sansan eliminado a 0 para facilitar la prueba

   /**
     * 特殊查询介绍(这里我就都用service、lambda查询器来演示)
     */
    @GetMapping("/query2")
    public Result query2() {
    
    
        // 1. 模糊查询:姓名中有三的学生
        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(Student::getName, "三"); // name like '%三%'(对应还有个不常用的notLike)
//        queryWrapper.likeLeft(Student::getName, "三"); // name like '%三'
//        queryWrapper.likeRight(Student::getName, "三"); // name like '三%'
        queryWrapper.orderByDesc(Student::getCreateTime); // 倒序输出
        List<Student> likeList = studentService.list(queryWrapper);
        log.info("【query2】likeList={}", JSON.toJSONString(likeList));

        // 2. 范围查询:大于小于
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.gt(Student::getCreateTime, "2021.12.20 17:38:00"); // gt是大于的意思:create_time > 2021.12.17 06:00:00
        queryWrapper.lt(Student::getCreateTime, "2021.12.25 22:00:00"); // lt是小于的意思:create_time < 2021.12.18 22:00:00
        List<Student> gltList = studentService.list(queryWrapper);
        // 还有:ge(大于等于>=)、le(小于等于<=)、ne(不等于<>)、between(两值之间),照葫芦画瓢就行,不演示了
        log.info("【query2】gltList={}", JSON.toJSONString(gltList));

        // 3. 分组查询:groupBy
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.groupBy(Student::getMajorId);
        List<Student> groupList = studentService.list(queryWrapper);
        log.info("【query2】groupList={}", JSON.toJSONString(groupList));

        // 4. in查询
        queryWrapper = new LambdaQueryWrapper<>();
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        queryWrapper.in(Student::getName, list); // 对应的还有notIn
        List<Student> inList = studentService.list(queryWrapper);
        log.info("【query2】inList={}", JSON.toJSONString(inList));

        // 5. 空查询(不常用)
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.isNull(Student::getName); // name is null,对应的肯定还有isNotNull
        List<Student> nullList = studentService.list(queryWrapper);
        log.info("【query2】nullList={}", JSON.toJSONString(nullList));

        // 6. 动态查询
        // 上面的不管是eq,还是gt、in等,其实在执行时最前面还有个布尔参数,不传的话默认都是true
        // 像上面的所有查询,我们都没传这个参数,其实内部自动为我们设置成了true,点进去看源码就知道了
        // 这个布尔参数的意思:为true表示带上这个条件,为false则忽略此条件,其实就是做动态SQL用
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(false, Student::getName, "张三"); // 这里设成false,则此设置不生效,等于没有这行,依然会查询所有的学生
        queryWrapper.eq(nullList.size() > 0, Student::getMajorId, 1); // 如果nullList有数据此设置才生效,才会查询专业=1的学生
        List<Student> dynamicList = studentService.list(queryWrapper);
        log.info("【query2】dynamicList={}", JSON.toJSONString(dynamicList));
        return Result.success(true);
    }

acceso de cartero: http://localhost:8080/student/query2
consulta difusa
consulta de rango
consulta de grupo
consulta dinámica
anterior, que es el tutorial más utilizado de mybatis-plus, todos los códigos han pasado mi prueba real, no hay problema, una cosa a la que hay que prestar atención es pseudo- eliminación, si no lo hace Cada tabla tiene un campo del_flag, por lo que no necesita configurar el desecho global en el archivo yml, sino implementarlo manualmente. Por ejemplo, agregue manualmente la condición al consultar: delFlag = 0, que es lo mismo que cuando se elimina. En resumen, utilícelo de manera flexible después de familiarizarse con él (por favor, dale me gusta si lo encuentras útil).

Adjunta la web oficial de MyBatis-Plus: https://baomidou.com/ (puedes ver el documento)

Supongo que te gusta

Origin blog.csdn.net/qq_36737803/article/details/122036386
Recomendado
Clasificación