扩展功能
P12 扩展功能-代码生成器
-
方法一:mybatisplus官方文档中的代码生成配置
-
方法二:插件mybatsx
-
方法三:插件mybatisplus
P13 DB静态工具
iservice中的方法是非静态的,db方法是静态的。
静态方法无法读取到类的泛型的,也就无法知道实体类类型、表信息,可以看到方法中都需要传额外参数,即实体类的字节码,以得到相关信息。
例如:可能会存在两个service相互注入的情况,即循环依赖,如何解决?
使用静态工具进行解决,(静态工具和iservice用法差不多,就是额外传入字节码),但是这样能够避免注入service,避免了循环依赖。(当然也可以不适用iservice中的方法,也可以使用mapper中的方法)
示例:根据用户id查询用户信息,以及用户地址信息并返回;
@Overridepublic 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());
@Overridepublic 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();// 转换地址VOList<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_idlogic-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 // 声明一个beanpublic 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结果
使用示例:
@Testvoid 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的VOdto.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的VOdto.setList(records.stream().map(convertor).collect(Collectors.toList()));// 5.返回return dto;}
}