一、引言
咱也不知道为啥SpringCloud课程会先教mybatis-plus的使用,但是教都教了,就学了吧,学完之后觉得mybatis-plus中的一些方法还是很好用了,本文作为我学习mybatis-plus的总结提升,希望大家看完之后也可以熟悉mybatis-plus的使用
二、mybatis-plus
1.依赖引入
在xml文件中引入mybatis-plus依赖
<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和数据库中的字段相对应
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方法
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 {@Autowiredprivate UserMapper userMapper;@Testvoid 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);}@Testvoid testSelectById() {User user = userMapper.selectById(5L);System.out.println("user = " + user);}@Testvoid testSelectByIds() {List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L, 5L));users.forEach(System.out::println);}@Testvoid testUpdateById() {User user = new User();user.setId(5L);user.setBalance(20000);userMapper.updateById(user);}@Testvoid 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方法的,如下
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就是这样的
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元的人。
@Test
void testQueryWrapper() {// 1.构建查询条件 where name like "%o%" AND balance >= 1000QueryWrapper<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,代码如下:
@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
@Test
void testUpdateWrapper() {List<Long> ids = List.of(1L, 2L, 4L);// 1.生成SQLUpdateWrapper<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,他们可以通过反射获取属性名,并不需要写死
代码如下
@Test
void testLambdaQueryWrapper() {// 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000QueryWrapper<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语句维护在持久层,所以我们写在业务层的方法是不被允许的,所以我们来改造一下我们的代码
源代码---
@Test
void testUpdateWrapper() {List<Long> ids = List.of(1L, 2L, 4L);// 1.生成SQLUpdateWrapper<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);
}
改造之后的代码---
@Test
void testCustomWrapper() {// 1.准备自定义查询条件List<Long> ids = List.of(1L, 2L, 4L);QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);// 2.调用mapper的自定义方法,直接传递WrapperuserMapper.deductBalanceByIds(200, wrapper);
}
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之中的用户
我们可以这样实现
查询条件这样来构建:
@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中自定义方法:
@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
:
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
:
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集合 |
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为POUser 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.处理voreturn 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.处理voreturn 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
@PutMapping("{id}/deduction/{money}")
@ApiOperation("扣减用户余额")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){userService.deductBalance(id, money);
}
2.这里是接口哈
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.实现类
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 {@Overridepublic 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拼接的地方
@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实体:
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
@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.处理voreturn BeanUtil.copyToList(users, UserVO.class);
}
2.lambdaQuery
@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.处理voreturn BeanUtil.copyToList(users, UserVO.class);
}
我只能说,这个简单的有些过分了,大家可以自己看一下
7.代码生成
这部分学到了一个代码生成器的使用
我是不怎么喜欢用这个,大家想用的话可以用用
8.静态工具
有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db
,其中的一些静态方法与IService
中方法签名基本一致,也可以帮助我们实现CRUD功能:
那么问题来了,我们为什么要用它,这里我给出一个解释,因为懒,不想多次注入多个service,同时循环依赖
那我们来用一下吧,改造根据id用户查询的接口,查询用户的同时返回用户收货地址列表
1.controller
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@PathVariable("id") Long userId){// 基于自定义service方法查询return userService.queryUserAndAddressById(userId);
}
2.service
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);
}
@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.处理voUserVO 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
中配置逻辑删除字段
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.首先,我们要定义一个枚举类
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文件中添加配置
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,它存储的内容是这样的
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.首先配置一下分页插件
@Configuration
public class MybatisConfig {@Beanpublic 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>
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
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)直接使用
@Overridepublic 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的使用