はじめにMyBatis -
Plus (略して MP) は、開発を簡素化し、効率を向上させるために、中国の Baomidou チームによって開発された MyBatis 拡張ツールです。)
- このチュートリアルでは、実際の開発環境の使用をシミュレートします。この環境は比較的完全で、mybatis-plus の最も一般的に使用されるプロセスをカバーしています。
- このプロジェクトで使用されている Maven の依存関係 jar は新しいバージョンです。私自身も使用しています。mp やその他の関連するバージョンの依存関係については心配する必要はありません。安心して使用できます。
- mybatis-plus の導入後は、mybatis-springboot の依存関係を導入する必要がなくなり、マッパー インターフェイスの記述と XML でのカスタム SQL の記述もサポートされます (オリジナルを変更しないでください)。
1: まず、学生テーブル 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='学生表';
- ここでシミュレートされた学生テーブル ID は bigint 型であり、自動インクリメントされません (一般に、大量のデータを含むテーブルに自動インクリメントの主キーを使用することはお勧めできません)。
- ほとんどすべてのテーブルに存在する create_time、update_time、および擬似削除のデフォルト値 0 を持つ論理削除フラグ del_flag があり、これら 3 つのフィールドは固定されているため、一般に全体として見なされます。
2: MP-Demo プロジェクトを作成し、依存関係と環境を構成する
1.Mavenの依存関係
<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>
- 上記では、このケースで使用されるいくつかの jar パッケージを紹介しただけです。他に必要な場合は、自分で再インポートできます。
- springboot バージョンは 2.5.2、mp バージョンは 3.4.2、接続プールは druid を使用せず、デフォルトの hikari が使用されます。
- ページング機能については、実は mp にもページングプラグインが用意されていて、それを利用できるのですが、私は組み込みのものを使いたくなくて、使うのが面倒でコードに煩わしさを感じるので、今でも使い続けています。ページングには pagehelper を使用することを選択します。これはより便利です
2. プロジェクト構造パッケージを作成する
handlerパッケージはプロセッサパッケージ、poパッケージはエンティティクラスパッケージ、マッパーはmybatisのマッパーインターフェース、サービスはサービスインターフェース+impl実装クラスで、結果パッケージは私のカスタム統一インターフェースです。返品パッケージ。ご自身のものでもお気軽に
3.application.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
- ここでは主に mybatis-plus の構成について説明します。実際、ハンプ ルール、エンティティ クラス パス、および XML パスを有効にするなど、以前の mybatis 構成と非常に似ており、これらはすべて以前と同じです。
- log-impl の設定ですが、実際には mp の実行 SQL を出力します。個人的には、初めて使用するときは有効にすることをお勧めします。使い慣れてきたら、この行をコメントしたり削除したりすると、ログが少なくなるため、今後はログを通じて問題を確認できます。
- global-config 設定に注目してみましょう。ここで設定されているのは、グローバル トゥームストーン delFlag、つまり擬似削除です。自動クエリ中に、mp は削除されたデータを除外するために where ステートメントの後に del_flag = 0 を自動的に追加します。また、更新および削除時にこの条件も自動的に追加します。
ただし、SQL ステートメントを手動で作成する場合は、手動で追加する必要があります。この関数の設定については、状況によって異なります。結局、一度設定すると、より必須になる可能性があるため、このグローバル tombstone フィールドを設定する必要はありません。条件 del_flag = 0 を手動で追加できます。つまり、実際の状況に応じて柔軟に使用できます。
4.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. ハンドラーパッケージの下にオートフィラーを作成します
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);
}
}
}
- テーブルの create_time フィールドと update_time フィールドが新規または更新時に自動的に入力され、手動で設定する必要がないことを期待しているため、上記の自動入力機能が構成されています。
- ここで説明するフィラーは、実際には、createBy、updateBy、つまり作成者と更新者を追加するなど、より充実したものにすることができます。これらは実際の状況に応じて追加できますが、一般的には上記の 2 つ (作成時間、更新時間) で十分です。
- ここでの時間型は Date 型ではなく、jdk8 の LocalDateTime 型を使用します。プロジェクト内の時間型はすべて LocalDateTime を使用します (LocalDateTime を推奨)
- 実は、公式の更新オートフィラー コードには小さな問題があります。最初にクエリを実行してから更新すると、updateTime 値が更新されないことがわかります。これは、デフォルトのポリシーの問題が原因です。値がある場合は更新されません。上書きされるため、直接metaObject.setValue()を実行し、次の行をコメントアウトします。これにより、このバグが回避されます
6. エンティティクラスの作成
(1) po パッケージ配下に新しいベースパッケージを作成し、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;
}
- オートフィラーは上で構成されているため、ここで使用する場合は、対応するアノテーションをフィールドに追加する必要があります。これにより、挿入および更新時に値を手動で設定する必要がなくなります。
- 注: 上記で構成された更新オートフィラーは、特殊な場合、つまり、一部のフィールドをカスタマイズして更新する場合にも失敗します (カスタム更新フィールドなど: 名前 = Zhang San の携帯電話番号を xxxx に変更する)。この小さなバグはまだ公式によって直接修正されていませんが、後で紹介する別の方法で解決できます。
(2) po パッケージの下に新しい Student クラスを作成し、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. マッパーインターフェイスの作成: StudentMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.taoge.po.Student;
public interface StudentMapper extends BaseMapper<Student> {
}
- マッパーインターフェイスはmpのBaseMapperインターフェイスを直接継承し、対応するエンティティクラスがStudentであることを指定します。
- マッパー パッケージ全体をスキャンするために @MapperScan アノテーションがスタートアップ クラスに追加されているため、マッパー インターフェイスに @Mapper や @Repository などのアノテーションを追加する必要はありません。
8. サービスインターフェースと実装クラスの作成
(1) StudentServiceインターフェース
import com.baomidou.mybatisplus.extension.service.IService;
import com.taoge.po.Student;
import java.util.List;
public interface StudentService extends IService<Student> {
}
サービス インターフェイスは mp の IService インターフェイスを直接継承し、エンティティ クラスを Student として指定するため、IService に付属するメソッドを使用できます。
(2) 実装クラス
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 {
}
ここでは ServiceImpl を継承し、対応するマッパーとクラスを指定して、自動カード操作が完了するようにします。
3: テスト コントローラーを作成する
1. 新しいインターフェース
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);
}
}
Student テーブルの主キー ID はインクリメントされていないため、ここでは setId を持っていませんが、mybatis-plus は自動的に長い ID を生成します。もちろん、プロジェクトで ID 生成戦略を使用して、それを生成し、雪の結晶などの ID を手動で設定します。アルゴリズムによって ID が生成されます。これはより良いものになります。
postman を使用して http://localhost:8080/student/add をリクエストします。デバッグ プロセスは次のとおりです。
2. クエリ インターフェイス
/**
* 查询
*/
@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: 次のメソッドを追加します
// 这里可以注入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;
}
上記のサービスで実行できることは、マッパーでも実行できます。サービスのマッパーは引き続きマッパーを使用してクエリを実行するため、自分で試すことができます。さらに、ファジーなど、より一般的に使用される特別なクエリもあります。最後に紹介するクエリ
postman リクエストを使用します: http://localhost:8080/student/query/1472863272568754178
3. インターフェイスを更新します。
/**
* 更新
*/
@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);
}
注: 指定された条件が更新されると、この更新は弊社によってカスタマイズされているため、updateTime の自動入力は無効になり、mp は入力されないため、setUpdateTime は手動で追加されます
postman リクエスト: http://localhost:8080/student/update/1472863272568754178
4. インターフェイスを削除します。
/**
* 删除
*/
@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, "删除成功");
}
Postman アクセス: http://localhost:8080/student/delete/1472863272568754178
5. 特別なクエリの概要
まず、簡単にテストできるように、削除された Zhang Sansan の delFlag を 0 に変更します。
/**
* 特殊查询介绍(这里我就都用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);
}
postman アクセス: 上記の http://localhost:8080/student/query2、これは mybatis-plus の最も一般的に使用されるチュートリアルです。すべてのコードは実際のテストに合格しています。問題はありません。注意すべき点が 1 つあります。各テーブルには del_flag フィールドがあるため、yml ファイルでグローバル トゥームストーンを設定する必要はありませんが、手動で実装します。たとえば、クエリを実行するときに条件を手動で追加します: delFlag = 0,削除する場合と同様ですので、慣れてから柔軟に使ってください(参考になったら「いいね!」をお願いします)。
MyBatis-Plus の公式 Web サイトを添付します: https://baomidou.com/ (ドキュメントが表示されます)