MyBatis Plus 【详解】| 学习日志 | 第 17 天
目录
MyBatis Plus
常用注解
常见配置
条件构造器
1.QueryWrapper
2.UpdateWrapper
3.LambdaQueryWrapper
自定义SQL
IService接口
代码生成
1.安装插件
2.连接数据库
3.生成代码
Db静态工具
逻辑删除
枚举处理器
JSON处理器
插件功能
1.分页插件
2.通用分页实体
2.1通用分页实体初步实现
2.2通用分页实体进一步实现
2.3通用分页实体和MP转换
MyBatis Plus
MyBatis Plus基本操作:
1.引入依赖。
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency>
2.mapper继承extends BaseMapper<E>。
MyBatis Plus值做增强不做改变,引入它不会对现有工程造成影响,只需要简单配置可以进行单表CRUD操作。
MyBatis Plus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。约定大于配置。
默认:
MybatisPlus会把PO实体的类名驼峰转下划线作为表名
MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
MybatisPlus会把名为id的字段作为主键
常用注解
@TableName
描述:表名注解,标识实体类对应的表
使用位置:实体类
@TableId
描述:主键注解,标识实体类中的主键字段
使用位置:实体类的主键字段
支持两个属性,value,type默认IdType.NONE常用IdType.AUTO
@TableField
描述:普通字段注解
成员变量名于数据库阻断不一致
成员变量名is开头的Boolean类型
成员变量名与数据库关键字冲突
成员变量不是数据库的字段@TableField(exist = false)进行标记
常见配置
application.yml中大多数配置默认就行需要来就自己配。
mybatis-plus:type-aliases-package: com.ithema.mp.domain.poglobal-config:db-config:id-type: auto
条件构造器
MybatisPlus提供了支持各种复杂的where条件,可以满足日常开发的所有需求。
Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
1.QueryWrapper
AbstractWrapper提供了where中包含的所有条件构造方法。
QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段。
@Testvoid testQueryWrapper() {QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id", "username", "info", "balance") // 使用数据库实际列名.like("username", "o").ge("balance", 800);List<User> users = userMapper.selectList(wrapper);System.out.println(users);}
2.UpdateWrapper
UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分。
@Testvoid testUpdateWrapper() {List<Long> list = new ArrayList<>();Collections.addAll(list, 1L, 2L);UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id", list);userMapper.update(null, userUpdateWrapper);}
3.LambdaQueryWrapper
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。那怎么样才能不写字段名,又能知道字段名呢?其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
LambdaQueryWrapper,LambdaUpdateWrapper分别对应QueryWrapper和UpdateWrapper。
方法一:使用lambda方法。
@Testvoid testQueryWrapper() {QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.lambda().select(User::getId, User::getUsername, User::getInfo, User::getBalance).like(User::getUsername, "o").ge(User::getBalance, 1000);List<User> users = userMapper.selectList(wrapper);System.out.println(users);}
这其中运用了方法引用中的类名引用实例方法。
如select中的参数是SFunction<T, ?>... columns,也就是需要多个SFunction<T, ?>,而它的代码中标注了@FunctionalInterface是一个函数式接口。
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}
它唯一的抽象方法继承自Function,其中R apply(T t); 没有方法体{},只有分号结尾,而无修饰符将默认隐式修饰public abstract,所以
是该函数式接口的唯一抽象方法。
@FunctionalInterface
public interface Function<T, R> {R apply(T t);
}
这一方法的第一个参数需要泛型对象,User类对象满足该抽象方法的第一个参数,返回泛型R类型,getId等成员方法依旧满足返回类型。
方法二: 使用LambdaQueryWrapper,LambdaUpdateWrapper。
@Testvoid testLambdaQueryWrapper() {LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>().select(User::getId, User::getBalance, User::getUsername, User::getInfo).like(User::getUsername, "o").ge(User::getBalance, 1000);List<User> users = userMapper.selectList(lambdaQueryWrapper);System.out.println(users);}
这其中所用到的反射机制,如图。
自定义SQL
MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL。
我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自定义SQL语句中剩下的部分。
1.基于Wrapper构建where条件
@Testvoid testQueryWrapper() {List<Long> list = new ArrayList<>();Collections.addAll(list, 1L, 2L);int amount = 200;LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().in(User::getId, list);userMapper.updateBalanceByIds(wrapper, amount);}
2.在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
void updateBalanceByIds(@Param(Constants.WRAPPER) LambdaUpdateWrapper<User> wrapper, @Param("amount") int amount);
3.自定义SQL,并使用wrapper条件
<update id="updateBalanceByIds">UPDATE user SET balance = balance - #{amount} ${ew.customSqlSegment}</update>
IService接口
继承的关系如图。接口IUserService继承IService。
public interface IUserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
实现增加用户。
@SpringBootTest
class IUserServiceTest {@Autowiredprivate IUserService iUserService;@Testvoid testSaveUser() {User user = new User();user.setId(6L);user.setUsername("Lii");user.setPassword("123");user.setPhone("18688990011");user.setBalance(200);user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());iUserService.save(user);}}
基于Restful风格,开发基础业务接口。
@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {private final IUserService userService;@ApiOperation("新增用户接口")@PostMappingpublic void saveUser(@RequestBody UserFormDTO userFormDTO){//1.把DTO拷贝到POUser user = BeanUtil.copyProperties(userFormDTO, User.class);//2.新增userService.save(user);}@ApiOperation("删除用户接口")@DeleteMapping("{id}")public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id){userService.removeById(id);}@ApiOperation("根据id查询用户接口")@GetMapping("{id}")public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){//1.查询用户POUser user = userService.getById(id);//2.把PO保存到VOreturn BeanUtil.copyProperties(user, UserVO.class);}@ApiOperation("根据id批量查询用户接口")@GetMappingpublic List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){//1.查询用户POList<User> users = userService.listByIds(ids);//2.把PO保存到VOreturn BeanUtil.copyToList(users, UserVO.class);}}
开发复杂业务接口。
@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {private final IUserService userService;@ApiOperation("根据name,status和blance批量查询用户接口")@GetMapping("/list")public List<UserVO> queryUsers(UserQuery query){//1.查询用户POList<User> users = userService.queryUsers(query.getName(), query.getStatus(), query.getMinBalance(), query.getMaxBalance());//2.把PO保存到VOreturn BeanUtil.copyToList(users, UserVO.class);}}
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {@ApiModelProperty("用户名关键字")private String name;@ApiModelProperty("用户状态:1-正常,2-冻结")private Integer status;@ApiModelProperty("余额最小值")private Integer minBalance;@ApiModelProperty("余额最大值")private Integer maxBalance;
}
public interface UserMapper extends BaseMapper<User> {}
public interface IUserService extends IService<User> {List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status) //等.gt(minBalance != null, User::getBalance, minBalance) //greater than.lt(maxBalance != null, User::getBalance, maxBalance) //less than.list();}
}
IService批量新增
MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句。这个参数的默认值是false,我们需要修改连接参数,将其配置为true。修改项目中的application.yml文件,在jdbc的url后面添加参&rewriteBatchedStatements= true。
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=truedriver-class-name: com.mysql.cj.jdbc.Driver
@SpringBootTest
class IUserServiceTest {@Autowiredprivate IUserService iUserService;private User buildUser(int i) {User user = new User();user.setUsername("user_" + i);user.setPassword("123");user.setPhone("18688990011");user.setBalance(2000);user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());return user;}@Testvoid saveBatchUsers() {List<User> list = new ArrayList<>(10000);for (int i = 0; i < 10000; i++) {list.add(buildUser(i));}iUserService.saveBatch(list, 1000);}
}
代码生成
1.安装插件
2.连接数据库
点击Other,点击Config Database
3.生成代码
点击Other,点击Code Generator,选中要生成代码的表,选择相应的参数即可生成代码。
Db静态工具
为了解决循环依赖(当你尝试创建 A 的实例时,它的构造函数会去创建 B。当创建 B 的实例时,它的构造函数又会去创建 A。创建 A 又会去创建 B,创建 B 又会去创建 A...这将导致无限递归,最终程序会抛出 StackOverflowError(栈溢出错误)),使用Db静态工具可以解决。
// 文件: A.java
public class A {// Class A 依赖(需要)Class B 的一个实例private B b;public A() {this.b = new B();}
}// 文件: B.java
public class B {// Class B 又依赖(需要)Class A 的一个实例private A a;public B() {this.a = new A();}
}
使用Db静态工具完成按Id查找用户和地址。
@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {@ApiOperation("根据id查询用户和地址接口")@GetMapping("{id}")public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){return userService.queryUserAndAddressById(id);}}
public interface IUserService extends IService<User> {UserVO queryUserAndAddressById(Long id);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic UserVO queryUserAndAddressById(Long id) {//1.查询用户User user = getById(id);if (user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常");}//2.查询地址List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();//3.封装VO//3.1转User为UserVOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);//3.2转addresses为List<AddressVO>if (CollUtil.isNotEmpty(addresses)) {userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));}return userVO;}}
使用Db静态工具完成按Ids批量查找用户和地址。
@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {return userService.queryUserAndAddressById(id);@ApiOperation("根据ids批量查询用户和地址接口")@GetMappingpublic List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){return userService.queryUserAndAddressByIds(ids);}}
public interface IUserService extends IService<User> {List<UserVO> queryUserAndAddressByIds(List<Long> ids);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic List<UserVO> queryUserAndAddressByIds(List<Long> ids) {//1.查询用户List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)) {return Collections.emptyList();}//2.查询地址//2.1获取用户id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());//2.2根据用户id查询地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();//2.3转换地址VOList<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);//***2.4按用户id分组,返回给一个Map 键:id,值:addressVOsMap<Long, List<AddressVO>> addressMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOS)) {addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}//3.转换VO//提前定义好List<UserVO>的大小和查到的users一样List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {//3.1转换用户VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);list.add(userVO);//3.2转换地址VOuserVO.setAddresses(addressMap.get(user.getId()));}return list;}}
逻辑删除
可以给表配置一个字段deleted然后再配置文件中添加下面的代码实现逻辑删除。
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)
枚举处理器
1.在application.yaml文件中添加配置,使用枚举处理器。
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
2.定义枚举,给枚举中的与数据库对应value值添加@EnumValue注解
public enum UserStatus {NORMAL(1, "正常"),FROZEN(2, "冻结"),;@EnumValue //告诉mp是把value往数据库中写private final int value;@JsonValue //告诉SpringMVC展示数据是descprivate final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;}
}
3.修改PO,VO中的成员变量的类型为UserStatus,在赋值比较时使用UserStatus.NORMAL和UserStatus.FROZEN。
JSON处理器
1.定义一个类用来定义Json数据。
@Data
//@AllArgsConstructor
@NoArgsConstructor //Jackson 默认使用无参构造器创建对象实例如果只注解@AllArgsConstructor将没有无参构造
//两个注解都不使用,类中没有显式定义任何构造器,所以Java编译器自动生成了一个默认的无参构造器。
@AllArgsConstructor(staticName = "of") //定义一个构造的静态方法
public class UserInfo {private Integer age;private String intro;private String gender;
}
2.修改PO,VO中的userInfo,PO需要添加@TableName(value = "user", autoResultMap = true),@TableField(typeHandler = JacksonTypeHandler.class)两个注解,修改类型为UserInfo,VO只用修改类为UserInfo。
@Data
@TableName(value = "user", autoResultMap = true)
//autoResultMap自动结果映射,启用后可以处理复杂类型映射:
//JSON 类型字段
//枚举类型
//自定义类型处理器
//嵌套对象映射
public class User {/*** 用户id*/@TableId(type = IdType.AUTO)private Long id;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 注册手机号*/private String phone;/*** 详细信息*/@TableField(typeHandler = JacksonTypeHandler.class)//数据库存储时:Java 对象 → JSON 字符串//数据库读取时:JSON 字符串 → Java 对象private UserInfo info;/*** 使用状态(1正常 2冻结)*/private UserStatus status;/*** 账户余额*/private Integer balance;/*** 创建时间*/private LocalDateTime createTime;@TableField(exist = false)/*** 更新时间*/private LocalDateTime updateTime;
}
@Data
@ApiModel(description = "用户VO实体")
public class UserVO {@ApiModelProperty("用户id")private Long id;@ApiModelProperty("用户名")private String username;@ApiModelProperty("详细信息")private UserInfo info;@ApiModelProperty("使用状态(1正常 2冻结)")private UserStatus status;@ApiModelProperty("账户余额")private Integer balance;@ApiModelProperty("用户的收货地址")private List<AddressVO> addresses;
}
插件功能
1.分页插件
首先,要在配置类中注册MyBatisPlus的核心插件,同时添加分页插件。
//声明这是一个配置类,相当于XML配置文件
//Spring容器启动时,会扫描到@Configuration注解的类
@Configuration
public class MybatisConfig {@Bean //相当于XML中的<bean>标签//两者都是告诉 Spring 容器://"请创建这个对象"//"请管理这个对象的生命周期"//"请将这个对象提供给其他需要它的组件"这里创建的是interceptorpublic MybatisPlusInterceptor mybatisPlusInterceptor() {//1.初始化核心插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//2.添加分页插件PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);pageInterceptor.setMaxLimit(1000L); // 设置分页上限interceptor.addInnerInterceptor(pageInterceptor);return interceptor;}
}
接着使用分页的API,IService中的IPage。
常使用IPage的子类Page。
开始查询。
@Testvoid testPageQuery() {int PageNum = 1;int PageSize = 2;//1.准备分页条件//1.1分页条件Page<User> page = Page.of(PageNum, PageSize);//1.2排序条件page.addOrder(new OrderItem("balance", true));page.addOrder(new OrderItem("id", true));//2.执行分页查询Page<User> resPage = iUserService.page(page);//3.解析System.out.println(resPage.getTotal());System.out.println(resPage.getPages());List<User> users = resPage.getRecords();users.forEach(System.out::println);}
2.通用分页实体
2.1通用分页实体初步实现
首先创建一个通用的分页查询query实体。
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Integer pageNum;@ApiModelProperty("页大小")private Integer pageSize;@ApiModelProperty("排序")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc;}
修改UserQuery继承查询分页查询实体。
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery extends PageQuery{@ApiModelProperty("用户名关键字")private String name;@ApiModelProperty("用户状态:1-正常,2-冻结")private Integer status;@ApiModelProperty("余额最小值")private Integer minBalance;@ApiModelProperty("余额最大值")private Integer maxBalance;
}
创建分页查询结果存储的PageDTO。
@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {@ApiModelProperty("总条数")private Long total;@ApiModelProperty("总页数")private Long pages;@ApiModelProperty("集合")private List<?> list; //不确定查询后records是什么类型的用泛型?,这里的?不需要和T一致
}
创建Controller中分页查询接口。
@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {@ApiOperation("根据条件分页查询用户接口")@GetMapping("/page")public PageDTO<UserVO> queryUsersPage(UserQuery query){return userService.queryUsersPage(query);}}
创建ServiceImpl中执行分页查询的方法。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();//1.构建查询条件//1.1分页条件Page<User> page = Page.of(query.getPageNum(), query.getPageSize());//1.2排序条件if(StrUtil.isNotBlank(query.getSortBy())){page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));} else {//为空则按时间排序page.addOrder(new OrderItem("update_time", false));}//2.分页查询Page<User> resPage = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);//3.封装VO结果//3.1存储查询返回参数到DTOPageDTO<UserVO> pageDTO= new PageDTO<>();pageDTO.setTotal(resPage.getTotal());pageDTO.setPages(resPage.getPages());//3.2存储查询数据到POList<User> users = resPage.getRecords();//没查到数据则返回空DTOif(CollUtil.isEmpty(users)) {pageDTO.setList(Collections.emptyList());return pageDTO;}//3.3存储查询数据到VO中List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);//3.4存储查询数据到DTOpageDTO.setList(userVOS);//4.返回return pageDTO;}}
2.2通用分页实体进一步实现
改造PageQuery,创建一个静态泛型方法让其来做创建查询条件。
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Integer pageNum = 1;@ApiModelProperty("页大小")private Integer pageSize = 5;@ApiModelProperty("排序")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc = true;public <T> Page<T> toMpPage(OrderItem...items) {//第一个 <T>:类型参数声明,声明这是一个泛型方法//第二个 T:返回类型中的具体化,指定返回类型为Page<T>//1.构建查询条件//1.1分页条件Page<T> page = Page.of(pageNum, pageSize);//1.2排序条件if(StrUtil.isNotBlank(sortBy)){//不为空page.addOrder(new OrderItem(sortBy, isAsc));} else if(items != null){//为空则按默认排序page.addOrder(items);}return page;}public <T> Page<T> toMpPageDefaultSortByCreateTime() {return toMpPage(new OrderItem("create_time", false));}public <T> Page<T> toMpPageDefaultSortByUpdateTime() {return toMpPage(new OrderItem("update_time", true));}public <T> Page<T> toMpPage(String defaultSortBy, Boolean defaultAsc) {return toMpPage(new OrderItem(defaultSortBy, defaultAsc));}}
改造PageDTO<T>创建静态泛型方法让其来做PO到VO再存储到DTO的转换。
@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {@ApiModelProperty("总条数")private Long total;@ApiModelProperty("总页数")private Long pages;@ApiModelProperty("集合")private List<?> list; //不确定查询后records是什么类型的用泛型?,这里的?不需要和T一致public static <PO, VO> PageDTO<VO> of(Page<PO> resPage, Class<VO> clazz) {//3.封装PO到VO结果到DTO//3.1存储查询返回参数到DTOPageDTO<VO> pageDTO= new PageDTO<>();//总条数pageDTO.setTotal(resPage.getTotal());//总页数pageDTO.setPages(resPage.getPages());//3.2存储查询页数据到POList<PO> po = resPage.getRecords();//没查到数据则返回空DTOif(CollUtil.isEmpty(po)) {pageDTO.setList(Collections.emptyList());return pageDTO;}//3.3拷贝查询页数据到VO中//List<VO> userVOS = BeanUtil.copyToList(users, VO.class);//调用者才知道VO的字节码所以public static <PO, VO> PageDTO<VO> of(Page<PO> page, Class<VO> clazz)//传递参数 Class<VO> clazzList<VO> vos = BeanUtil.copyToList(po, clazz);//3.4存储查询数据到DTOpageDTO.setList(vos);//4.返回return pageDTO;}
}
修改Service简化分页查询。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();
// //1.构建查询条件
// //1.1分页条件
// Page<User> page = Page.of(query.getPageNum(), query.getPageSize());
// //1.2排序条件
// if(StrUtil.isNotBlank(query.getSortBy())){
// page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
// } else {
// //为空则按时间排序
// page.addOrder(new OrderItem("update_time", false));
// }//1.构建查询条件Page<User> page = query.toMpPageDefaultSortByUpdateTime();//2.分页查询Page<User> resPage = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);
// //3.封装VO结果
// //3.1存储查询返回参数到DTO
// PageDTO<UserVO> pageDTO= new PageDTO<>();
// pageDTO.setTotal(resPage.getTotal());
// pageDTO.setPages(resPage.getPages());
//
// //3.2存储查询数据到PO
// List<User> users = resPage.getRecords();
// //没查到数据则返回空DTO
// if(CollUtil.isEmpty(users)) {
// pageDTO.setList(Collections.emptyList());
// return pageDTO;
// }
// //3.3存储查询数据到VO中
// List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
//
// //3.4存储查询数据到DTO
// pageDTO.setList(userVOS);
// //4.返回
// return pageDTO;return PageDTO.of(resPage, UserVO.class);}}
2.3通用分页实体和MP转换
修改PageDTO静态泛型方法中的参数为函数式皆苦,让Service自定义查询数据的转换。
@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {@ApiModelProperty("总条数")private Long total;@ApiModelProperty("总页数")private Long pages;@ApiModelProperty("集合")private List<?> list; //不确定查询后records是什么类型的用泛型?,这里的?不需要和T一致public static <PO, VO> PageDTO<VO> of1(Page<PO> resPage, Function<PO, VO> convertor) {//3.封装PO到VO结果到DTO//3.1存储查询返回参数到DTOPageDTO<VO> pageDTO= new PageDTO<>();//总条数pageDTO.setTotal(resPage.getTotal());//总页数pageDTO.setPages(resPage.getPages());//3.2存储查询页数据到POList<PO> po = resPage.getRecords();//没查到数据则返回空DTOif(CollUtil.isEmpty(po)) {pageDTO.setList(Collections.emptyList());return pageDTO;}//3.3拷贝查询页数据到VO中List<VO> vos = po.stream().map(convertor).collect(Collectors.toList());//3.4存储查询数据到DTOpageDTO.setList(vos);//4.返回return pageDTO;}
修改Service并使用lambda表达式定义自己的转换逻辑。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic void deductBalance(Long id, Integer money) {//查询用户User user = getById(id);//校验用户状态if(user == null|| user.getStatus() == UserStatus.FROZEN) {throw new RuntimeException("用户状态异常");}//校验余额是否充足if(user.getBalance() < money) {throw new RuntimeException("用户余额不足");}//扣减余额baseMapper.deductBalance(id, money);}@Overridepublic List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status) //等.gt(minBalance != null, User::getBalance, minBalance) //greater than.lt(maxBalance != null, User::getBalance, maxBalance) //less than.list();}@Overridepublic UserVO queryUserAndAddressById(Long id) {//1.查询用户User user = getById(id);if (user == null || user.getStatus() == UserStatus.FROZEN) {throw new RuntimeException("用户状态异常");}//2.查询地址List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();//3.封装VO//3.1转User为UserVOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);//3.2转addresses为List<AddressVO>if (CollUtil.isNotEmpty(addresses)) {userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));}return userVO;}@Overridepublic List<UserVO> queryUserAndAddressByIds(List<Long> ids) {//1.查询用户List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)) {return Collections.emptyList();}//2.查询地址//2.1获取用户id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());//2.2根据用户id查询地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();//2.3转换地址VOList<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);//***2.4按用户id分组,返回给一个Map 键:id,值:addressVOsMap<Long, List<AddressVO>> addressMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOS)) {addressMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}//3.转换VO//提前定义好List<UserVO>的大小和查到的users一样List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {//3.1转换用户VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);list.add(userVO);//3.2转换地址VOuserVO.setAddresses(addressMap.get(user.getId()));}return list;}@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();
// //1.构建查询条件
// //1.1分页条件
// Page<User> page = Page.of(query.getPageNum(), query.getPageSize());
// //1.2排序条件
// if(StrUtil.isNotBlank(query.getSortBy())){
// page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
// } else {
// //为空则按时间排序
// page.addOrder(new OrderItem("update_time", false));
// }//1.构建查询条件Page<User> page = query.toMpPageDefaultSortByUpdateTime();//2.分页查询Page<User> resPage = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);
// //3.封装VO结果
// //3.1存储查询返回参数到DTO
// PageDTO<UserVO> pageDTO= new PageDTO<>();
// pageDTO.setTotal(resPage.getTotal());
// pageDTO.setPages(resPage.getPages());
//
// //3.2存储查询数据到PO
// List<User> users = resPage.getRecords();
// //没查到数据则返回空DTO
// if(CollUtil.isEmpty(users)) {
// pageDTO.setList(Collections.emptyList());
// return pageDTO;
// }
// //3.3存储查询数据到VO中
// List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
//
// //3.4存储查询数据到DTO
// pageDTO.setList(userVOS);
// //4.返回
// return pageDTO;return PageDTO.of1(resPage, user -> {//1.拷贝基础属性(PO转VO)UserVO vo = BeanUtil.copyProperties(user, UserVO.class);//2.处理特殊逻辑(其他的操作)vo.setUsername(vo.getUsername().substring(0, vo.getUsername().length() - 2) + "**");return vo;});}}