一、引言
咱也不知道为啥SpringCloud课程会先教mybatis-plus的使用,但是教都教了,就学了吧,学完之后觉得mybatis-plus中的一些方法还是很好用了,本文作为我学习mybatis-plus的总结提升,希望大家看完之后也可以熟悉mybatis-plus的使用
二、mybatis-plus
1.依赖引入
在xml文件中引入mybatis-plus依赖
java
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
注:引入这个依赖就代表吧mybatis的依赖随便引入引入进来了,可以删除你引入mybatis依赖
2.继承BaseMapper
之所以我们可以使用mybatis-plus中简单的单表CRUD方法,是因为mybatis-plus提供了相应的方法类BaseMapper,而我们的mapper只需要继承BaseMapper即可使用其中的方法
注:这里要指定泛型,如<User>这样才能知道你增删改查的对象是什么类型,User和数据库中的字段相对应
java
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
public interface UserMapper extends BaseMapper<User> {
}
3.使用基本的CRUD方法
java
package com.itheima.mp.mapper;
import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.List;
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testInsert() {
User user = new User();
user.setId(5L);
user.setUsername("Lucy");
user.setPassword("123");
user.setPhone("18688990011");
user.setBalance(200);
user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
userMapper.insert(user);
}
@Test
void testSelectById() {
User user = userMapper.selectById(5L);
System.out.println("user = " + user);
}
@Test
void testSelectByIds() {
List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L, 5L));
users.forEach(System.out::println);
}
@Test
void testUpdateById() {
User user = new User();
user.setId(5L);
user.setBalance(20000);
userMapper.updateById(user);
}
@Test
void testDelete() {
userMapper.deleteById(5L);
}
}
代码中
1.userMapper.insert(user) 新增方法
2.userMapper.selectById(5L); 根据id单个查询全部信息
3.userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L, 5L)); 根据id批量查询信息
4.userMapper.updateById(user); 根据id修改信息,这里默认指修改有值的部分,即balance
5.userMapper.deleteById(5L); 根据id删除信息
这些方法见名知意,也是很好记,如果仔细看测试输出,会发现其在底层是自动生成了相应的CRUD方法的,如下
sql
11:05:01 INFO 15524 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
11:05:02 INFO 15524 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
11:05:02 DEBUG 15524 --- [ main] c.i.mp.mapper.UserMapper.selectById : ==> Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM user WHERE id=?
11:05:02 DEBUG 15524 --- [ main] c.i.mp.mapper.UserMapper.selectById : ==> Parameters: 5(Long)
11:05:02 DEBUG 15524 --- [ main] c.i.mp.mapper.UserMapper.selectById : <== Total: 1
user = User(id=5, username=Lucy, password=123, phone=18688990011, info={"age": 21}, status=1, balance=20000, createTime=Fri Jun 30 11:02:30 CST 2023, updateTime=Fri Jun 30 11:02:30 CST 2023)
4.BaseMapper<User>中的<User>的讲究
看完上面的讲解,大家肯定会疑问,mybatis-plus是如何通过我指定的<User>泛型,找到我相应的数据库的表的,现在就给大家解开这个谜题
其实,在你的User实体类的定义时是有讲究的
比如我的User就是这样的
java
package com.itheima.mp.domain.po;
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.itheima.mp.domain.vo.AddressVO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
@TableName("user")
public class User {
//@TableField使用场景
// 1.成员变量名与数据库名称不一致
// 2.成员变量以is开头而且是布尔值 isMarried
// 3.成员变量名与数据库关键字冲突 order
/**
* 用户id
*/
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
@TableField("username")
private String username;
/**
* 密码
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 详细信息
*/
private String info;
/**
* 使用状态(1正常 2冻结)
*/
private Integer status;
/**
* 账户余额
*/
private Integer balance;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
@ApiModelProperty("用户的收货地址")
private List<AddressVO> address;
}
默认情况下
MybatisPlus会把PO实体的类名驼峰转下划线作为表名
MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
MybatisPlus会把名为id的字段作为主键
注:名词解释PO,指实体类
可以发现里面有很多的注解,也就是这些注解在起作用,下面给大家讲解一下这些注解
1.@TableName("user"),指定实体类名对应的表名,也就是User实体类就对应user表
2.@TableId(value = "id",type = IdType.AUTO),指定主键对应的字段,type中的type = IdType.AUTO,代表主键自增
还有其他的type,这里说明一下
|-------------|---------------------------------------------------------------------------------------------------------------------------------|
| AUTO | 数据库 ID 自增 |
| NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
| INPUT | insert 前自行 set 主键值 |
| ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法) |
| ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法) |
3.@TableField("username")指定对象属性对应的表中字段,用到它有三种情况
成员变量名与数据库字段名不一致
成员变量是以
isXXX
命名,按照JavaBean
的规范,MybatisPlus
识别字段时会把is
去除,这就导致与数据库不符。成员变量名与数据库一致,但是与数据库的关键字冲突。使用
@TableField
注解给字段名添加转义字符:````````
5.更加复杂的数据库操作
这里指更加复杂的where
条件,因为前文都是简单的根据id怎么样,根据id列表怎么样,但是如果指定其他字段查询又该怎么办呢,这里就是为了解决这个问题
在解决这个问题之前,给大家介绍几个条件构造的抽象类,就是这些抽象类里面可以装我们的条件,然后就可以用这些条件去作为where
条件
接下来给大家介绍一下其中常用的几个抽象类
1)QueryWrapper
下面是一个使用QueryWrapper的例子,用来查询出名字中带o
的,存款大于等于1000元的人。
java
@Test
void testQueryWrapper() {
// 1.构建查询条件 where name like "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
// 2.查询数据
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
其中的wrapper便是一个条件构造器,其中构造了三个条件:
1..select("id", "username", "info", "balance")
表示要查找id、username、info、balance四个字段
2..like("username", "o")
表示模糊查询,即username中带o
3..ge("balance", 1000)
表示balance的值大于1000
之后调用userMapper.selectList(wrapper);查询结果列表,将我们的条件构造器wrapper传入进去
让我们再举一个例子
更新用户名为jack的用户的余额为2000,代码如下:
java
@Test
void testUpdateByQueryWrapper() {
// 1.构建查询条件 where name = "Jack"
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
// 2.更新数据,user中非null字段都会作为set语句
User user = new User();
user.setBalance(2000);
userMapper.update(user, wrapper);
}
这里包含了两部分,怎么改,改哪个:
怎么改---
就像前文讲到的updateById(user),创建一个对象,mybatis-plus默认修改其中有值的字段
改哪里---
这时候就得请出我们的QueryWrapper了,构造条件username为Jack
2)UpdateWrapper
这里也给大家举例
更新id为1,2,4
的用户的余额,扣200
java
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200") // SET balance = balance - 200
.in("id", ids); // WHERE id in (1, 2, 4)
// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
// 而是基于UpdateWrapper中的setSQL来更新
userMapper.update(null, wrapper);
}
大家可以看到这个需求,它需要自定义SQL语句,即"balance = balance - 200",这句需要加在sql语句中set的后面,UpdateWrapper便提供了这种功能
3)LambdaQueryWrapper
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值
。这在编程规范中显然是不推荐的。
于是出现了LambdaQueryWrapper和LambdaUpdateWrapper,他们可以通过反射获取属性名,并不需要写死
代码如下
java
@Test
void testLambdaQueryWrapper() {
// 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
这里的User::getId便代替了id
4)自定义 SQL
在企业中,通常SQL语句维护在持久层,所以我们写在业务层的方法是不被允许的,所以我们来改造一下我们的代码
源代码---
java
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200") // SET balance = balance - 200
.in("id", ids); // WHERE id in (1, 2, 4)
// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
// 而是基于UpdateWrapper中的setSQL来更新
userMapper.update(null, wrapper);
}
改造之后的代码---
java
@Test
void testCustomWrapper() {
// 1.准备自定义查询条件
List<Long> ids = List.of(1L, 2L, 4L);
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
// 2.调用mapper的自定义方法,直接传递Wrapper
userMapper.deductBalanceByIds(200, wrapper);
}
java
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;
public interface UserMapper extends BaseMapper<User> {
@Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}
这里需要注意三点
1.在int money前加@Param("money")是为了确保money接收的是传来的money
2.在QueryWrapper<User> wrapper)前要加@Param("ew"),这样才可以识别到是一个条件构造器
3.${ew.customSqlSegment},这是字符串拼接,写法是固定的
这里我其实有一个疑问,如果SQL语句维护在持久层,那么UpdateWrapper的setSql是否还有用的必要,或者说UpdateWrapper是否还有用的必要
5)多表关联查询
理论上来讲MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。
例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户
我们可以这样实现
查询条件这样来构建:
java
@Test
void testCustomJoinWrapper() {
// 1.准备自定义查询条件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("u.id", List.of(1L, 2L, 4L))
.eq("a.city", "北京");
// 2.调用mapper的自定义方法
List<User> users = userMapper.queryUserByWrapper(wrapper);
users.forEach(System.out::println);
}
然后在UserMapper中自定义方法:
java
@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);
6)小总结
这部分内容比较多,我总结一下要点
mybatis-plus处理"更复杂的数据库操作"主要是通过条件构造器来实现的,个人觉得最好用的是LambdaQueryWrapper,因为其比较全面,包括反射,条件构造,在更新的方法也可以用到它
而条件构造器主要是构造where之后的内容,比如我们所讲到的自定义sql,字符串拼接的也是where部分的内容
6.Service接口
MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。 通用接口为IService
,默认实现为ServiceImpl
,其中封装的方法可以分为以下几类:
save
:新增
remove
:删除
update
:更新
get
:查询单个结果
list
:查询集合结果
count
:计数
page
:分页查询
其实学到这里的时候我就在想,BaseMapper已经够全面了,为啥要来一个IService,我这里猜测一下,是为了迎合三层架构(瞎猜的)
这里的学习方法就和学习BaseMapper很像了
1)继承
首先,定义IUserService
,继承IService
:
java
package com.itheima.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
public interface IUserService extends IService<User> {
// 拓展自定义方法
}
然后,编写UserServiceImpl
类,继承ServiceImpl
,实现UserService
:
java
package com.itheima.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.service.IUserService;
import com.itheima.mp.mapper.UserMapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements IUserService {
}
这样就可以使用mybatis-plus准备好的方法了
2) CRUD
又来到了我们的增删改查环节,实现下面四个接口噢
编号 | 接口 | 请求方式 | 请求路径 | 请求参数 | 返回值 |
---|---|---|---|---|---|
1 | 新增用户 | POST | /users | 用户表单实体 | 无 |
2 | 删除用户 | DELETE | /users/{id} | 用户id | 无 |
3 | 根据id查询用户 | GET | /users/{id} | 用户id | 用户VO |
4 | 根据id批量查询 | GET | /users | 用户id集合 | 用户VO集合 |
java
package com.itheima.mp.controller;
import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "用户管理接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("users")
public class UserController {
private final IUserService userService;
@PostMapping
@ApiOperation("新增用户")
public void saveUser(@RequestBody UserFormDTO userFormDTO){
// 1.转换DTO为PO
User user = BeanUtil.copyProperties(userFormDTO, User.class);
// 2.新增
userService.save(user);
}
@DeleteMapping("/{id}")
@ApiOperation("删除用户")
public void removeUserById(@PathVariable("id") Long userId){
userService.removeById(userId);
}
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@PathVariable("id") Long userId){
// 1.查询用户
User user = userService.getById(userId);
// 2.处理vo
return BeanUtil.copyProperties(user, UserVO.class);
}
@GetMapping
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
// 1.查询用户
List<User> users = userService.listByIds(ids);
// 2.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
}
这里的CRUD方法和BaseMapper的用法相似,只是名称不一致
1.userService.save(user) 新增用户
2.userService.removeById(userId) 根据id删除用户
3.userService.getById(userId) 根据id查询用户
4.userService.listByIds(ids) 根据id列表查询用户
这里有需要注意的方法
1.BeanUtil.copyProperties(userFormDTO, User.class) 字段映射
2.BeanUtil.copyToList(users, UserVO.class) 批量字段映射
3.@RequiredArgsConstructor+private final IUserService userService;
这里注入userService,是一种更推荐的写法
3)controller+service+mapper
下面尝试实现根据id扣减用户余额
我们发现在UserController中好像实现不了这个功能,因为牵扯到业务逻辑的代码我们要放在service层去实现,不然代码就太乱了,而且牵扯到了自定义sql,还要用到mapper层
业务逻辑如下
判断用户状态是否正常
判断用户余额是否充足
1.UserController,这里不需要处理业务逻辑,所以直接调用service
java
@PutMapping("{id}/deduction/{money}")
@ApiOperation("扣减用户余额")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){
userService.deductBalance(id, money);
}
2.这里是接口哈
java
package com.itheima.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
public interface IUserService extends IService<User> {
void deductBalance(Long id, Integer money);
}
3.实现类
java
package com.itheima.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public void deductBalance(Long id, Integer money) {
// 1.查询用户
User user = getById(id);
// 2.判断用户状态
if (user == null || user.getStatus() == 2) {
throw new RuntimeException("用户状态异常");
}
// 3.判断用户余额
if (user.getBalance() < money) {
throw new RuntimeException("用户余额不足");
}
// 4.扣减余额
baseMapper.deductMoneyById(id, money);
}
}
4.mapper,这里是自定义sql拼接的地方
java
@Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
void deductMoneyById(@Param("id") Long id, @Param("money") Integer money);
4)Lambda
实现一个根据复杂条件查询用户的接口,查询条件如下:
name:用户名关键字,可以为空
status:用户状态,可以为空
minBalance:最小余额,可以为空
maxBalance:最大余额,可以为空
对于像这种的复杂条件查询,我们最好先定义一个查询条件实体,UserQuery实体:
java
package com.itheima.mp.domain.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {
@ApiModelProperty("用户名关键字")
private String name;
@ApiModelProperty("用户状态:1-正常,2-冻结")
private Integer status;
@ApiModelProperty("余额最小值")
private Integer minBalance;
@ApiModelProperty("余额最大值")
private Integer maxBalance;
}
我们有两种方法去实现这个功能
1.LambdaQueryWrapper
java
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance);
// 2.查询用户
List<User> users = userService.list(wrapper);
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
2.lambdaQuery
java
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
// 2.查询用户
List<User> users = userService.lambdaQuery()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
我只能说,这个简单的有些过分了,大家可以自己看一下
7.代码生成
这部分学到了一个代码生成器的使用
我是不怎么喜欢用这个,大家想用的话可以用用
8.静态工具
有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db
,其中的一些静态方法与IService
中方法签名基本一致,也可以帮助我们实现CRUD功能:
那么问题来了,我们为什么要用它,这里我给出一个解释,因为懒,不想多次注入多个service,同时循环依赖
那我们来用一下吧,改造根据id用户查询的接口,查询用户的同时返回用户收货地址列表
1.controller
java
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@PathVariable("id") Long userId){
// 基于自定义service方法查询
return userService.queryUserAndAddressById(userId);
}
2.service
java
package com.itheima.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
public interface IUserService extends IService<User> {
void deduct(Long id, Integer money);
UserVO queryUserAndAddressById(Long userId);
}
java
@Override
public UserVO queryUserAndAddressById(Long userId) {
// 1.查询用户
User user = getById(userId);
if (user == null) {
return null;
}
// 2.查询收货地址
List<Address> addresses = Db.lambdaQuery(Address.class)
.eq(Address::getUserId, userId)
.list();
// 3.处理vo
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
return userVO;
}
其实很多时候什么代码放在controller什么代码放在service都是主观感觉,我就得就是三部分代码能完成的就不用service,只需要定义,数据库,返回三部分,每一部分一句代码,当然是个人感觉
这里有要注意的一点
使用Db的方法时,需要多加一个Address.class,得让他知道你对应的实体类是谁
9.逻辑删除
先给大家说一下什么是逻辑删除以及如何实现逻辑删除
为什么要逻辑删除
比如你在淘宝的消费记录你删除了,那么你就看不到了,但是这个数据还得给商家看等等,所以不能真正的删除,他只是你看不到了
如何逻辑删除
对应其实现,只是在你的数据库中加了一个is_deleted字段,不删除为0,删除就为1
对于mybatis-plus很简单,只需要在application.yml
中配置逻辑删除字段
java
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
之后执行删除操作时,会修改底层代码为逻辑删除
注:只有MybatisPlus生成的SQL语句才支持自动的逻辑删除,自定义SQL需要自己手动处理逻辑删除。
10.通用枚举
这里给大家将一下为何要使用枚举
比如用户的账号状态,有正常 和冻结两种,但是我们为了代码的可维护性,要在数据库存0,1,但是你又希望在数据的传输和查看过程中,一直是正常和冻结,这时就要使用枚举了
1.首先,我们要定义一个枚举类
java
package com.itheima.mp.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
@Getter
public enum UserStatus {
NORMAL(1, "正常"),
FREEZE(2, "冻结")
;
private final int value;
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}
2.然后把User
类中的status
字段改为UserStatus
类型
3.在application.yaml文件中添加配置
java
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
4.两个注解
MybatisPlus
提供了@EnumValue
注解来标记枚举属性不传输json格式时会传输枚举的名字
在UserStatus枚举中通过
@JsonValue
注解标记JSON序列化时展示的字段传输json类型时会传输枚举的值
11.JSON 类型处理器
如果我们有个数据库的字段里面存储的时json类型的字符串时就会出现一种问题
需要在写入数据库时手动转为String
,再读取数据库时,手动转换为对象
,这会非常麻烦。
所以mybatis-plus提供了一种非常方便的工具JacksonTypeHandler
处理器
下面教大家如何去使用它
比如现在有一个字段info,它存储的内容是这样的
java
package com.itheima.mp.domain.po;
import lombok.Data;
@Data
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}
那么,我们需要在它对应的实体类里面加入如下注解
这样便可以自动执行数据的转换了
12.配置加密
用不到,嘿嘿,不写了
13.分页插件
这里直接展示成品
1.首先配置一下分页插件
java
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 1.创建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
// 2.分页查询上限
paginationInnerInterceptor.setMaxLimit(1000L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
2.整几个分页需要的类
1)PageDTO<T>
java
package com.itheima.mp.domain.dto;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Collections;
import java.util.List;
@Data
@ApiModel("分页结果")
public class PageDTO<T> {
@ApiModelProperty("总条数")
private Integer total;
@ApiModelProperty("总页数")
private Integer pages;
@ApiModelProperty("结果")
private List<T> list;
public static <PO,VO> PageDTO<VO> of(Page<PO> p,Class<VO> clazz){
PageDTO<VO> dto = new PageDTO<>();
dto.setTotal((int) p.getTotal());
dto.setPages((int) p.getPages());
List<PO> records=p.getRecords();
if(CollUtil.isEmpty(records)){
dto.setList(Collections.emptyList());
return dto;
}
dto.setList(BeanUtil.copyToList(records,clazz));
return dto;
}
}
2)PageQuery
java
package com.itheima.mp.domain.query;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "分页查询条件实体")
public class PageQuery {
@ApiModelProperty("页码")
private Integer pageNo=1;
@ApiModelProperty("每页数量")
private Integer pageSize=5;
@ApiModelProperty("排序字段")
private String orderBy;
@ApiModelProperty("是否升序")
private Boolean isAsc=true;
public <T> Page<T> toMpPage(OrderItem ... items) {
Page<T> page=Page.of(pageNo,pageSize);
if(StrUtil.isNotBlank(orderBy)) {
page.addOrder(new OrderItem(orderBy, isAsc));
}else if(items!=null){
page.addOrder(items);
}
return page;
}
public <T> Page<T> toMpPage(String defaultSortBy ,Boolean defaultAsc) {
return toMpPage(new OrderItem(defaultSortBy,defaultAsc));
}
public <T> Page<T> toMpPageDefaultSortByCreateTime(OrderItem ... items) {
return toMpPage(new OrderItem("create_time",false));
}
public <T> Page<T> toMpPageDefaultSortByUpdateTime(OrderItem ... items) {
return toMpPage(new OrderItem("update_time",false));
}
}
3)直接使用
java
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
String name=query.getName();
Integer status=query.getStatus();
// 1.构建查询条件
Page<User> page =query.toMpPageDefaultSortByUpdateTime();
// 2.分页查询
Page<User> p=lambdaQuery()
.like(name!=null,User::getUsername,name)
.eq(status!=null,User::getStatus,status)
.page(page);
// 3.封装vo结果
return PageDTO.of(p,UserVO.class);
}
三、结语
丸辣,怎么天就黑了了,本来还打算吧Docker学了的
能学完就把博客发出来吧,我尽量
不知道下一篇文章是
五天SpringCloud计划------DAY1之Docker的使用
还是
五天SpringCloud计划------DAY2之Docker的使用