五天SpringCloud计划——DAY1之mybatis-plus的使用

一、引言

   咱也不知道为啥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的使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/61302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统实现屏幕横竖屏切换

需求场景 机器默认横屏或者竖屏显示 -强制横竖屏显示 实现思路 旋转 uboot logo 和内核 logo旋转 Android 桌面旋转触摸 这个很好理解&#xff1a; uboot 内核 开机动画都是有界面的&#xff0c;旋转改变方向&#xff0c;同时提供新的横屏或者竖屏logo旋转桌面&#xff0c…

【机器学习】机器学习的基本分类-监督学习-逻辑回归-Sigmoid 函数

Sigmoid 函数是一种常用的激活函数&#xff0c;尤其在神经网络和逻辑回归中扮演重要角色。它将输入的实数映射到区间 (0, 1)&#xff0c;形状类似于字母 "S"。 1. 定义与公式 Sigmoid 函数的公式为&#xff1a; 特点 输出范围&#xff1a;(0, 1)&#xff0c;适合用…

eBay 基于 Celeborn RESTful API 进行自动化工具集成实践

作者&#xff1a;王斐&#xff0c;ebay Hadoop 团队软件工程师&#xff0c;Apache Kyuubi PMC member&#xff0c;Apache Celeborn Committer。 简介&#xff1a;Apache Celeborn 是一个统一的大数据中间服务&#xff0c;致力于提高不同MapReduce引擎的效率和弹性。为了Spark …

Python 和 Pyecharts 对Taptap相关数据可视化分析

结果展示&#xff1a; 数据来源&#xff1a; Python爬取TapTap 热门游戏信息并存储到数据库&#xff08;详细版&#xff09; 目录 结果展示&#xff1a; 数据来源&#xff1a; Python爬取TapTap 热门游戏信息并存储到数据库&#xff08;详细版 一、引言 二、准备工作 三、…

【Linux】常见指令 + 权限概念

文章目录 一、重要的指令mkdir指令rmdir指令 && rm 指令man指令cp指令mv指令less指令find指令tar指令 二、关于Linux中的权限文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性&#xff09;文件权限值的表示方法文件访问权限的相关设…

老旧前端项目如何升级工程化的项目

因为历史的原因存在着大量的老旧前端项目&#xff0c;而在今天的开发环境中已经不再适应了&#xff0c;于是产生了升级到新的环境的需求。比如笔者当前的一个登录页面项目&#xff0c;就是以下面为技术栈的老旧项目。 基于 jQuery包管理基于 require.js&#xff0c;甚至有的没…

在国外,使用中国移动app办理停机保号

1.人在国内的时候&#xff0c;先使用手机下载中国移动app 以前网上营业厅是可以直接办理停机保号的&#xff0c;现在不可以了 2.人在国内的时候&#xff0c;确保自己的手机能够登录中国移动app 这个步骤保证回国前可以使用中国移动app复机 3.人在国内的时候&#xff0c;拨打…

C# 解决【托管调试助手 “ContextSwitchDeadlock“:……】问题

文章目录 一、遇到问题二、解决办法 一、遇到问题 托管调试助手 “ContextSwitchDeadlock”:“CLR 无法从 COM 上下文 0x56e81e70 转换为 COM 上下文 0x56e81d48&#xff0c;这种状态已持续 60 秒。拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows …

Vscode阅读C/C++ Code实用教程

目录 1.必备插件2.创建工程3.重要的快捷键及使用 1.必备插件 C/C IntelliSense - 用于跳转GitLens — Git supercharged -用于查看git 提交记录Remote - SSH -用于连接linux服务器 2.创建工程 创建工程还是蛮重要的&#xff0c;虽然不创建同样可以看Code&#xff0c;创建工程…

OGRE 3D----4. OGRE和QML共享opengl上下文

在现代图形应用开发中,OGRE(Object-Oriented Graphics Rendering Engine)和QML(Qt Modeling Language)都是非常流行的工具。OGRE提供了强大的3D渲染能力,而QML则用于构建灵活的用户界面。在某些应用场景中,我们需要在同一个应用程序中同时使用OGRE和QML,并且共享OpenGL…

C语言进阶7:程序环境与预处理

本章重点 程序的翻译环境程序的执行环境详解&#xff1a;C语言程序的编译 链接预定义符号介绍预处理指令 #define宏和函数的对比预处理操作符#和##的介绍命令定义预处理指令 #include预处理指令 #undef条件编译 1.程序的翻译环境和执行环境 在ANSIC的任何一种实现中&#x…

.net的winfrom程序 窗体透明打开窗体时出现在屏幕右上角

窗体透明&#xff0c; 将Form的属性Opacity&#xff0c;由默认的100% 调整到 80%&#xff0c;这个数字越小越透明(尽量别低于50%&#xff0c;不信你试试看)&#xff01; 打开窗体时出现在屏幕右上角 //构造函数 public frmCalendarList() {InitializeComponent();//打开窗体&…

【Java基础入门篇】三、面向对象和JVM底层分析(3)

Java基础入门篇 三、面向对象和JVM底层分析 3.5 抽象类/接口和外/内部类 &#xff08;1&#xff09;抽象类 **“抽象方法”**是使用abstract修饰的方法&#xff0c;没有方法体&#xff0c;只有声明&#xff0c;定义的是一种“规范”&#xff0c;就是告诉子类必须要给抽象方法…

快速理解倒排索引在ElasticSearch中的作用

一.基础概念 定义&#xff1a; 倒排索引是一种数据结构&#xff0c;用来加速文本数据的搜索和检索&#xff0c;和传统的索引方式不同&#xff0c;倒排索引会被每个词汇项与包含该词汇项的文档关联起来&#xff0c;从而去实现快速的全文检索。 举例&#xff1a; 在传统的全文…

AI开发 - GPT之魂 用Python 演示chatGPT的自注意力机制 - 机器学习

自注意力机制&#xff08;Self-Attention&#xff09;就是让模型在处理每个词时&#xff0c;学会“关注重点”&#xff0c;而不是平均地对每个词一视同仁。这种机制让 GPT 能更聪明地理解句子的上下文和语义之间的关系。 自注意力机制是 GPT 的核心&#xff0c;它帮助模型在理解…

将自定义 AWS S3 快照存储库连接到 Elastic Cloud

作者&#xff1a;来自 Elastic Annie Hansen, Stef Nestor 在本博客中&#xff0c;我们将介绍如何通过 Elasticsearch 的快照将我们已提交的集群数据备份到 AWS S3 存储桶中。在 Elastic Cloud&#xff08;企业版&#xff09;中&#xff0c;Elastic 在其 found-snapshots 存储…

linux下Qt程序部署教程

文章目录 [toc]1、概述2、静态编译安装Qt1.1 安装依赖1.2 静态编译1.3 报错1.4 添加环境变量1.5 下载安装QtCreator 3、配置linuxdeployqt环境1.1 在线安装依赖1.2 使用linuxdeployqt提供的程序1.3 编译安装linuxdeployqt 4、使用linuxdeployqt打包依赖1.1 linuxdeployqt使用选…

【QT】控件8

1.QDial 通过调节旋钮位置来控制窗口的不透明度&#xff1a; void Widget::on_dial_valueChanged(int value) {qDebug()<<value;this->setWindowOpacity((double)value/100); }效果演示&#xff1a; 2.Date/Time Edit 计算两个日期的差值 ui界面设计 计算按钮按下…

CGAL CGAL::Polygon_mesh_processing::self_intersections解析

CGAL::Polygon_mesh_processing::self_intersections 是用于检测多边形网格&#xff08;Polygon Mesh&#xff09;中的自相交的函数。自相交是指网格中的某些面&#xff08;例如三角形&#xff09;与同一网格中的其他面交叉的情况。这种情况通常是不期望的&#xff0c;因为它会…