3.1.代码生成
Mapper、Service、PO代码相对固定,重复编写比较麻烦,因此MybatisPlus官方提供了代码生成器根据数据库表结构生成PO、Mapper、Service等相关代码。
但是代码生成器也要编码使用,比较麻烦,这里使用MybatisPlus的插件,可以基于图形化界面完成
3.1.1.安装插件
在Idea的plugins市场中搜索并安装MyBatisPlus插件: 
3.1.2.使用
目前address表未生成对应的实体和mapper基础代码,可以利用插件生成。 首先配置数据库地址,在Idea顶部菜单,工具->ConfigDatabase

点击ok保存之后,然后点击工具->Code Generator
3.2.静态工具
Service之间相互调用就会出现循环依赖问题,MybatisPlus提供给了一个静态工具类:Db,其中一些静态方法与IService方法签名基本一致,帮我们实现CRUD功能: 
示例: 

需求:改造根据id用户查询的接口,查询用户的同时返回用户收货地址列表
首先,要添加一个收货地址的VO对象:
package com.itheima.mp.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "收货地址VO")
public class AddressVO {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("省")
private String province;
@ApiModelProperty("市")
private String city;
@ApiModelProperty("县/区")
private String town;
@ApiModelProperty("手机")
private String mobile;
@ApiModelProperty("详细地址")
private String street;
@ApiModelProperty("联系人")
private String contact;
@ApiModelProperty("是否是默认 1默认 0否")
private Boolean isDefault;
@ApiModelProperty("备注")
private String notes;
}改造UserVO,添加地址属性: 
接下来修改UserController中根据id查询用户的业务接口:
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@PathVariable("id") Long userId) {
//基于自定义service方法查询
return userService.queryUserAndAddressById(userId);
}首先在IUserService中定义方法:
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 UserService extends IService<User> {
void deductBalance(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> addressList= Db.lambdaQuery(Address.class)
.eq(Address::getUserId,userId)
.list();
//3.处理VO
UserVO userVO= BeanUtil.copyProperties(user,UserVO.class);
userVO.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));
return userVO;
}查询地址时,采用了Db的静态方法,避免了注入AddressService,减少了循环依赖的风险。

需求:根据id批量查询用户,并查询出用户对应的所有地址
@Override
public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {
//1.查询id查询
List<User> users=listByIds(ids);
if(CollUtil.isEmpty(users)){
return Collections.emptyList();
}
//2.从用户列表提取用户id
List<Long> userIds=users.stream().map(User::getId).collect(Collectors.toList());
//3.根据用户id集合批量查询地址
List<Address> addresses=Db.lambdaQuery(Address.class).in(Address::getUserId,userIds).list();
//4.把地址按照userId分组
Map<Long,List<Address>> addressMap=addresses.stream().collect(Collectors.groupingBy(Address::getUserId));
//5.转为VO对象
List<UserVO> userVOList=users.stream().map(user -> {
UserVO userVO=BeanUtil.copyProperties(user,UserVO.class);
List<Address> addressList=addressMap.get(user.getId());
if(CollUtil.isEmpty(addressList)){
userVO.setAddresses(Collections.emptyList());
}else {
userVO.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));
}
return userVO;
}).collect(Collectors.toList());
return userVOList;
}3.3.逻辑删除
对于一些重要数据,我们往往采用逻辑删除方案,即:
- 在表中添加一个字段标记数据是否被删除
- 当删除数据时把标记置为true
- 查询时过滤掉标记为true的数据 一旦采用逻辑删除,所有查询和删除逻辑都要跟着变化,为了解决这个问题,MybatisPlus就添加了对逻辑删除的支持。
注意:自定义SQL需要自己手动处理逻辑删除。
例如:给address表添加一个逻辑删除字段:
alter table address add deleted bit default b'0' null comment '逻辑删除';然后给Address实体添加deleted字段

接下来在application.yaml配置逻辑删除字段
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
global-config:
db-config:
id-type: auto #全局id类型为自增长
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)测试: 首先执行删除操作:
@Test
void testDeleteByLogic() {
// 删除方法与以前没有区别
addressService.removeById(59L);
}看出看到底层SQL逻辑变了: 
注意:逻辑删除本身也有自己的问题
- 会导致数据库表垃圾数据越来越多,从而影响查询效率
- SQL中全都需要对逻辑删除字段做判断,影响查询效率
3.4.通用枚举
User类有一个用户状态字段: 
做业务判断时可以直接基于枚举作比较,但数据库是int类型,对应PO也是Integer。因此业务操作需要手动把枚举与Integer转换,比较麻烦。
因此,MybatisPlus提供了一个处理枚举的类型转换器,帮我们把枚举类型自动转换。
3.4.1.定义枚举
定义一个用户状态的枚举: 
代码如下:
package com.itheima.mp.enums;
import lombok.Getter;
@Getter
public enum UserStatus {
FORMAL(1,"正常"),
FREEZE(2,"冻结")
;
private final int value;
private final String desc;
UserStatus(int value,String desc){
this.value = value;
this.desc = desc;
}
}要让MybatisPlus处理枚举与数据库类型自动转换,必须告诉MybatisPlus,枚举中那个字段值作为数据库值。
MybatisPlus提供了@EnumValue注解来标记枚举属性:

3.4.2.配置枚举处理器
在application.yaml添加配置:
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
global-config:
db-config:
id-type: auto #全局id类型为自增长
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler3.4.3.修改实体类字段
修改User实体类中的status类型为UserStatus
修改业务方法中的判断逻辑: 
修改UserVO中的属性类型: 
3.4.5.测试
状态变成了FORMAL
3.5.JSON类型处理器
数据库user表中有一个info字段,是JSON类型: 
而User实体类是String类型: 
如果要方便获取,info的类型最好是一个Map或者实体类。
而如果我们要把info改为对象类型,就需要在写入数据库时手动转为String,再读取数据库时,手动转换为对象,这会非常麻烦。
因此MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理器JSON就可以使用JacksonTypeHandler处理器。
3.5.1.定义实体
首先定义一个单独实体类来与info字段属性匹配:
package com.itheima.mp.domain.po;
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}3.5.2.使用类型处理器
接下来将User类的info字段修改为UserInfo类型,并声明类型处理器: 
同时在User类上添加注解,声明自动映射: 
为了让页面返回的结果也以对象格式返回,要求改UserVO中的info对象 
如果在xml手写了#{info},也需要修改:
在页面查询如下: 