扩展功能
P12 扩展功能-代码生成器
方法一:mybatisplus官方文档中的代码生成配置
方法二:插件mybatsx
方法三:插件mybatisplus
P13 DB静态工具
iservice中的方法是非静态的,db方法是静态的。
静态方法无法读取到类的泛型的,也就无法知道实体类类型、表信息,可以看到方法中都需要传额外参数,即实体类的字节码,以得到相关信息。
例如:可能会存在两个service相互注入的情况,即循环依赖,如何解决?
使用静态工具进行解决,(静态工具和iservice用法差不多,就是额外传入字节码),但是这样能够避免注入service,避免了循环依赖。(当然也可以不适用iservice中的方法,也可以使用mapper中的方法)
示例:根据用户id查询用户信息,以及用户地址信息并返回;
@Override
public UserVO queryUserAndAddressById(Long id){
// 查询用户信息
User user = getById(id);
if (user == null || user.getStatus() == UserStatus.FROZEN){
throw new RuntimeException("用户状态异常");
}
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
// 查询用户地址,地址表一个用户多个地址
// 使用lambdaquery,避免相互依赖,使用DB静态方法
List<Address> addresses = Db.lambdaQuery(Address.class)
.eq(Address::getUserId, id)
.list();
if (CollUtil.isNotEmpty(addresses)){
userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
}
return userVO; // 没有自己写sql就实现了业务操作
}
P14 扩展功能-DB静态工具2
例如:实现批量查询用户,同时查询出用户地址信息。
注意查询地址信息,需要先获取用户的id集合,参数ids就是一个集合,如果以后再遇到这种,可以使用stream流从用户对象从获取到用户id的结合,例如:
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
@Override
public List<UserVO> queryUserAndAddressByIds(List<Long> ids){
// 查询用户
List<User> users = listByIds(ids);
if (CollUtil.isEmpty(users)){
return Collections.emptyList();
}
// 方法参数传入的ids可能为空
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
// 查询地址
List<Address> addresses = Db.lambdaQuery(Address.class)
.in(Address::getUserId, userIds)
.list();
// 转换地址VO
List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
// 将用户地址分组,
Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);
if (CollUtil.isNotEmpty(addressVOList)){
addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId)); // 依据用户id进行分组
}
// 转为VO返回
List<UserVO> list = new ArrayList<>(users.size());
for (User user : users){
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
list.add(userVO);
userVO.setAddresses(addressMap.get(user.getId()));
}
return list;
}
P15 扩展功能-逻辑删除
逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据
在表中添加一个字段标记数据是否被删除
当删除数据时置该字段为1
查询时只查询标记为0的数据
这种功能mybatisplus已经提供了,直接帮我们在底层自动修改CRUD语句,在application.yaml中配置逻辑删除的字段名和值即可。
例如:
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
global-config:
db-config:
id-type: auto # 配置id生成的策略,如果是雪花算法就是assign_id
logic-delete-field: deleted # 配置逻辑删除字段
update-strategy: not_null # 更新策略,只更新非空字段
逻辑删除也会存在问题:
垃圾数据越来越多
sql中全都需要对逻辑删除字段进行判断,影响查询效率
替代方案,采用数据迁移的方式
P16 扩展功能-枚举处理器
使用枚举类型。
问题:java中的枚举类型与数据库中的int类型相互转换问题。这个问题是mybatis实现的。
mybatisplus加入了几个类型处理器,就有MybatisEnumTypeHandler。
先配置:
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
加注解:
@EnumValue
private final int value; // 对应数据库中的值
我们定义的枚举类是UserStatus,修改用户实体类中的status的类型为这个枚举类型而不是int类型。
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
枚举返回前端默认是枚举项的名字,返回给前端的显示值,使用注解jsonvalue,可以自定义返回
@JsonValue
private final String desc;
P17 扩展功能-JSON处理器
mybatis plus还提供了AbstractJsonTypeHandler转换器,实现java和json的转换
例如数据库中info字段存储的json类型,如下图:
在java中使用这类数据,如果读取到再将字符串转换为可用的数据比较麻烦,可以考虑定义一个这种json格式的实体,然后使其变成这个对象,mybatis没有提供这个类型处理器,mybatis plus提供了。
例如:
/**
* 详细信息
*/
@TableField(typeHandler = JacksonTypeHandler.class) // 定义类型处理器
private UserInfo info;
还要开启自动的结果映射
@Data
@TableName(value = "user", autoResultMap = true) // 自动结果映射
public class User {
插件功能
mybatis plus提供的内置拦截器有:
P18 分页插件
之间使用了PageHelper,
使用mybatis plus中的分页插件,首先配置,
@Configuration
public class MyBatisConfig {
@Bean // 声明一个bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 创建核心插件 ,可以往里面加插件
// 1.创建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);// 分页内置拦截器
paginationInnerInterceptor.setMaxLimit(1000L);// 最多1000条
// 2.添加分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor); // 可以添加插件
return interceptor;
}
}
使用分页的API,返回page结果
使用示例:
@Test
void testPageQuery() {
int pageNo = 1, pageSize = 2;
// 1.准备分页条件
// 1.1.分页条件
Page<User> page = Page.of(pageNo, pageSize); // 创建page对象
// 1.2.排序条件
page.addOrder(new OrderItem("balance", true));
page.addOrder(new OrderItem("id", true));
// 2.分页查询
Page<User> p = userService.page(page);
// 3.解析
long total = p.getTotal();
System.out.println("total = " + total);
long pages = p.getPages();
System.out.println("pages = " + pages);
List<User> users = p.getRecords();
users.forEach(System.out::println);
}
P19 通用分页实体
例如:
从接收处理到业务逻辑实现到返回。
如果单独在UserDTO中实现,加入AddressDTO中也有分页需求,又要写。所以可以设计统一的分页查询请求,即公共分页类。
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
@ApiModelProperty("页码")
private Integer pageNo = 1;
@ApiModelProperty("页码")
private Integer pageSize = 5;
@ApiModelProperty("排序字段")
private String sortBy;
@ApiModelProperty("是否升序")
private Boolean isAsc = true;
public <T> Page<T> toMpPage(OrderItem ... items){
// 1.分页条件
Page<T> page = Page.of(pageNo, pageSize);
// 2.排序条件
if(StrUtil.isNotBlank(sortBy)){
// 不为空
page.addOrder(new OrderItem(sortBy, 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(){
return toMpPage(new OrderItem("create_time", false));
}
public <T> Page<T> toMpPageDefaultSortByUpdateTime(){
return toMpPage(new OrderItem("update_time", false));
}
}
userquery继承这个分页查询实体即可。
可以定义pageDTO,
@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
@ApiModelProperty("总条数")
private Long total;
@ApiModelProperty("总页数")
private Long pages;
@ApiModelProperty("集合")
private List<T> list; // 泛型T,不知道具体哪种类型
public static <PO, VO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz){
PageDTO<VO> dto = new PageDTO<>();
// 1.总条数
dto.setTotal(p.getTotal());
// 2.总页数
dto.setPages(p.getPages());
// 3.当前页数据
List<PO> records = p.getRecords();
if (CollUtil.isEmpty(records)) {
dto.setList(Collections.emptyList());
return dto;
}
// 4.拷贝user的VO
dto.setList(BeanUtil.copyToList(records, clazz));
// 5.返回
return dto;
}
public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor){
PageDTO<VO> dto = new PageDTO<>();
// 1.总条数
dto.setTotal(p.getTotal());
// 2.总页数
dto.setPages(p.getPages());
// 3.当前页数据
List<PO> records = p.getRecords();
if (CollUtil.isEmpty(records)) {
dto.setList(Collections.emptyList());
return dto;
}
// 4.拷贝user的VO
dto.setList(records.stream().map(convertor).collect(Collectors.toList()));
// 5.返回
return dto;
}
}