四川交投建设工程股份有限公司网站/如何在国外推广自己的网站
MybatisPlus
- 1. 快速入门
- 1.1 入门案例
- 1.2 常见注解
- 1.3 常见配置
- 2. 核心功能
- 2.1 条件构造器
- 2.2 自定义SQL
- 2.3 Service接口
- 3. 扩展功能
- 3.1 代码生成
- 3.2 静态工具
- 3.3 逻辑删除
- 4. 插件功能
- 4.1 分页插件
- 4.2 通用分页实体
1. 快速入门
1.1 入门案例
步骤一:引入MybatisPlus的起步依赖
MyBatisPlus官方提供了starter,其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency>
步骤二:定义Mapper继承MybaitsPlus提供的BaseMapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;@Mapper
public interface UserMapper extends BaseMapper<User> {
}
注:BaseMapper中已经定义了常用的CURD方法
步骤三:调用接口中的方法进行测试
1.2 常见注解
MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
MybatisPlus中比较常用的几个注解如下:
- @TableName: 用来指定表明
- @TableId: 用来指定表中的主键字段信息
- @TableField: 用来指定表中的普通字段信息
IdType枚举:
- AUTO: 数据库自增长
- INPUT: 通过set方法自行输入
- ASSIGN_ID: 分配ID,接口IdentifierGenerator的方法nextId来生成id,默认实现类为DefaultIdentifierGenerator雪花算法
使用@TableFidle的常见场景:
- 成员变量名与数据库字段名不一致
- 成员变量名以is开头,且是布尔值
- 成员变量名与数据库关键字冲突
- 成员变量不是数据库字段
1.3 常见配置
MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。例如:
mybatis-plus:mapper-locations: classpath*:mapper/**/*.xml # Mapper.xml文件地址,默认值type-aliases-package: com.itheima.mp.domain.po # 别名扫描包configuration:map-underscore-to-camel-case: true # 是否开启下户线和驼峰的映射cache-enabled: false # 是否开启二级缓存global-config:db-config:id-type: assign_id # id为雪花算法生成update-strategy: not_null # 更新策略
2. 核心功能
2.1 条件构造器
刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了。
参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:
Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:
而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:
而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:
QueryWrapper:无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。查询:查询出名字中带o的,存款大于等于1000元的人。代码如下:
@SpringBootTest
public class UserWrapperTest {@ResourceUserMapper userMapper;@Testvoid 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);}
UpdateWrapper:
更新用户名为jack的用户的余额为2000,代码如下:
@SpringBootTest
public class UserWrapperTest {@ResourceUserMapper userMapper;@Testvoid testUpdateWrapper(){// 1.构建查询条件 set balance = 20000 where name = "Jack"UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = 2000").eq("username", "jack");// 2. 查询数据int update = userMapper.update(null, wrapper);System.out.println(update);}
}
更新id为1,2,4的用户的余额,扣200,代码如下:
@SpringBootTest
public class UserWrapperTest {@ResourceUserMapper userMapper;@Testvoid testUpdateBatchWrapper(){// 1.构建查询条件UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200") // SET balance = balance - 200.in("id",List.of(1L, 2L, 4L)); // // WHERE id in (1, 2, 4)// 2. 查询数据,注意第一个参数可以给null,也就是不填更新字段和数据,而是基于UpdateWrapper中的setSQL来更新int update = userMapper.update(null, wrapper);System.out.println(update);}}
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。那怎么样才能不写字段名,又能知道字段名呢?其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。
因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
- LambdaQueryWrapper
- LambdaUpdateWrapper
分别对应QueryWrapper和UpdateWrapper
@SpringBootTest
public class UserWrapperTest {@ResourceUserMapper userMapper;@Testvoid testLambdaQueryWrapper(){// 1.构建查询条件 where name like "%o%" AND balance >= 1000LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().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);}@Testvoid testLambdaUpdateWrapper(){// 1.构建查询条件LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().setSql("balance = 2000").eq(User::getUsername, "jack");// 2. 查询数据int update = userMapper.update(null, wrapper);System.out.println(update);}
}
2.2 自定义SQL
在演示UpdateWrapper的案例中,我们在代码中编写了更新的SQL语句:
@Testvoid testUpdateBatchWrapper(){// 1.构建查询条件UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200") // SET balance = balance - 200.in("id",List.of(1L, 2L, 4L)); // // WHERE id in (1, 2, 4)// 2. 查询数据,注意第一个参数可以给null,也就是不填更新字段和数据,而是基于UpdateWrapper中的setSQL来更新int update = userMapper.update(null, wrapper);System.out.println(update);}
这种写法在某些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。MybatisPlus提供了自定义SQL功能,先利用Wrapper来构建复杂的where条件,再结合Mapper.xml或注解自己编写SQL语句。
核心思想:将where条件使用Wrapper来构建,然后在Mapper.xml进行调用即可
2.3 Service接口
MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:
- save:新增
- remove:删除
- update:更新
- get:查询单个结果
- list:查询集合结果
- count:计数
- page:分页查询
- 自定义Service接口继承IService接口
- 自定义Service实现类,实现自定义接口并集成ServiceImpl类
由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。
首先,定义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 {
}
接下来实现下面5个接口:
首先引入依赖
<!--swagger-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.1.0</version>
</dependency>
<!--web-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后需要配置swagger信息:
knife4j:enable: trueopenapi:title: 用户管理接口文档description: "用户管理接口文档"email: xxxxxconcat: xxxxurl: xxxxversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- com.itheima.mp.controller
导入相关的UserVo、UserDTO、UserFormDTO(前端传入参数较多的时候封装成类)
UserController
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.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;/*** @author Mr.Lu* @version 1.0* @date 2024-08-13 20:57*/@RequestMapping("/users")
@RestController
@Slf4j
@Api(tags = "用户管理接口")
public class UserController {@Resourceprivate IUserService userService;@PostMapping@ApiOperation("新增用户")public void saveUser(@RequestBody UserFormDTO userFormDTO){User user = new User();BeanUtil.copyProperties(userFormDTO, user);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){User user = userService.getById(userId);UserVO userVO = new UserVO();BeanUtil.copyProperties(user, userVO);return userVO;}@GetMapping@ApiOperation("根据ids查询批量用户")public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){List<User> users = userService.listByIds(ids);List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);return userVOS;}@PutMapping("/{id}/deduction/{money}")@ApiOperation("根据id更新金额")public void deduction(@PathVariable("id") Long id, @PathVariable Integer money){userService.deduction(id, money);}}
UserServiceImpl
特别注意:ServiceImpl已经注入了badeMapper(泛型指定为userMapper),所以可以直接调用mapper无需注入
package com.itheima.mp.service.impl;import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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;/*** @author Mr.Lu* @version 1.0* @date 2024-08-13 20:53*/@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic void deduction(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.扣减余额LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().eq(User::getId, id);baseMapper.deduction(money, warpper);}
}
IUserService
package com.itheima.mp.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;/*** @author Mr.Lu* @version 1.0* @date 2024-08-13 20:52*/public interface IUserService extends IService<User> {void deduction(Long id, Integer money);
}
UserMapper
package com.itheima.mp.mapper;import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;@Mapper
public interface UserMapper extends BaseMapper<User> {@Update("update tb_user set balance = balance - #{money} ${ew.customSqlSegment}")void deduction(@Param("money")Integer money, @Param("ew")LambdaUpdateWrapper<User> wrapper);
}
IService中还提供了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;
}
在UserController中定义一个controller方法:
@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);
}
在组织查询条件的时候,我们加入了 username != null 这样的参数,意思就是当条件成立时才会添加这个查询条件,类似Mybatis的mapper.xml文件中的标签。
案例二:批量新增,修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true
@Test
void testSaveBatch() {// 准备10万条数据List<User> list = new ArrayList<>(1000);long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {list.add(buildUser(i));// 每1000条批量插入一次if (i % 1000 == 0) {userService.saveBatch(list);list.clear();}}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));
}
3. 扩展功能
3.1 代码生成
3.2 静态工具
有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db,其中的一些静态方法与IService中方法签名基本一致,也可以帮助我们实现CRUD功能:
1. 改造根据id查询用户的接口,查询用户的同时,查询出用户对应的所有地址改造;
@Overridepublic UserVO queryUserAndAddress(Long userId) {// 1. 查询用户信息User user = (User) Db.getById(userId, User.class);// 2. 判断用户是否存在if(user == null) return null;// 3. 查询用户地址List<Address> address = Db.lambdaQuery(Address.class).eq(Address::getUserId, userId).list();// 类型转换UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);List<AddressVO> addressVOList = BeanUtil.copyToList(address, AddressVO.class);userVO.setAddresses(addressVOList);return userVO;}
2. 根据id批量查询用户的接口,查询用户的同时,查询出用户对应的所有地址;
public List<UserVO> queryUsersAndAddresses(List<Long> ids) {// 1. 批量查询用户List<User> users = Db.listByIds(ids, User.class);if(CollUtil.isEmpty(users)){return Collections.emptyList();}users.forEach(System.out::println);// 2. 查询地址// 需要重新获取userIds,不能再直接使用ids, userIds保证都是存在的用户List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();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));}// 3. 转换VO返回List<UserVO> list = new ArrayList<>(users.size());for(User user : users){UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);userVO.setAddresses(addressMap.get(user.getId()));list.add(userVO);}return list;}
3.3 逻辑删除
对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:
- 在表中添加一个字段标记数据是否被删除
- 当删除数据时把标记置为true
- 查询时过滤掉标记为true的数据
一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。开启了逻辑删除功能以后,我们就可以像普通删除一样做CRUD,基本不用考虑代码逻辑问题。还是非常方便的。
然后给Address实体添加deleted字段:
在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)
测试:
@Testvoid testDeleteByLogic() {// 删除方法与以前没有区别addressService.removeById(59L);}
@Testvoid testQuery() {List<Address> list = addressService.list();list.forEach(System.out::println);}
逻辑删除本身也有自己的问题,比如:
- 会导致数据库表垃圾数据越来越多,从而影响查询效率
- SQL中全都需要对逻辑删除字段做判断,影响查询效率
因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法
4. 插件功能
4.1 分页插件
在未引入分页插件的情况下,MybatisPlus是不支持分页功能的,IService和BaseMapper中的分页方法都无法正常起效。
所以,我们必须配置分页插件。
配置分页插件:
编写一个分页查询的测试:
@Testvoid testPage(){int pageNo = 1, pageSize = 2;// 1. 分页参数Page<User> page = Page.of(pageNo, pageSize);// 1.1 设置排序条件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);}
4.2 通用分页实体
现在要实现一个用户分页查询的接口,接口规范如下:
返回值
需要定义3个实体:
-
UserQuery:分页查询条件的实体,包含分页、排序参数、过滤条件
-
PageDTO:分页结果实体,包含总条数、总页数、当前页数据
-
UserVO:用户页面视图实体
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;
}
其中缺少的仅仅是分页条件,而分页条件不仅仅用户分页查询需要,以后其它业务也都有分页查询的需求。因此建议将分页查询条件单独定义为一个PageQuery实体:
PageQuery是前端提交的查询参数,一般包含四个属性:
-
pageNo:页码
-
pageSize:每页数据条数
-
sortBy:排序字段
-
isAsc:是否升序
@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {@ApiModelProperty("页码")private Long pageNo;@ApiModelProperty("页码")private Long pageSize;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc;
}
然后让UserQuery继承这个实体:
package com.itheima.mp.domain.query;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;@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具体代码如下:
package com.itheima.mp.domain.dto;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.List;@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {@ApiModelProperty("总条数")private Long total;@ApiModelProperty("总页数")private Long pages;@ApiModelProperty("集合")private List<T> list;
}
UserVO具体代码如下:
package com.itheima.mp.domain.vo;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.List;@Data
@ApiModel(description = "用户VO实体")
public class UserVO {@ApiModelProperty("用户id")private Long id;@ApiModelProperty("用户名")private String username;@ApiModelProperty("详细信息")private String info;@ApiModelProperty("使用状态(1正常 2冻结)")private Integer status;@ApiModelProperty("账户余额")private Integer balance;@ApiModelProperty("用户使用地址")private List<AddressVO> addresses;
}
开发接口
在UserController中定义分页查询用户的接口:
package com.itheima.mp.controller;import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.query.UserQuery;
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.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;@RequestMapping("/users")
@RestController
@Slf4j
@Api(tags = "用户管理接口")
public class UserController {@Resourceprivate IUserService userService;@GetMapping("/page")@ApiOperation("分页查询")public PageDTO<UserVO> queryUsersPage(UserQuery query){return userService.queryUsersPage(query);}
}
在IUserService中创建queryUsersPage方法:
public interface IUserService extends IService<User> {PageDTO<UserVO> queryUsersPage(UserQuery query);
}
在UserServiceImpl中实现该方法:
package com.itheima.mp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.po.Address;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.query.UserQuery;
import com.itheima.mp.domain.vo.AddressVO;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;import java.util.*;
import java.util.stream.Collectors;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {// 1. 构造条件// 1.1 分页条件int pageNo = query.getPageNo();int pageSize = query.getPageSize();Page<User> page = Page.of(pageNo, pageSize);// 1.2 排序条件if(query.getSortBy() != null){page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));} else {// 默认按照更新时间排序page.addOrder(new OrderItem("update_time", false));}// 2. 查询// userService.page(page),由于是userService类自身的方法,所以不用再写userService// 2.1 不带筛选条件的// Page<User> p = page(page);// 2.2 带筛选条件的Page<User> p = lambdaQuery().like(query.getName() != null, User::getUsername, query.getName()).eq(query.getStatus() != null, User::getStatus, query.getStatus()).ge(query.getMaxBalance() != null, User::getBalance, query.getMinBalance()).lt(query.getMinBalance() != null, User::getBalance, query.getMaxBalance()).page(page);// 3. 数据校验是否合法List<User> users = p.getRecords();if(users == null || users.size() <= 0){// 无数据,返回空结果return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());}// 4. 有数据转换List<UserVO> list = BeanUtil.copyToList(users, UserVO.class);// 5 返回封装结果return new PageDTO<>(p.getTotal(), p.getPages(), list);}
}
相关文章:

MybatisPlus使用指南
MybatisPlus 1. 快速入门1.1 入门案例1.2 常见注解1.3 常见配置 2. 核心功能2.1 条件构造器2.2 自定义SQL2.3 Service接口 3. 扩展功能3.1 代码生成3.2 静态工具3.3 逻辑删除 4. 插件功能4.1 分页插件4.2 通用分页实体 1. 快速入门 1.1 入门案例 步骤一:引入Mybat…...

5. MongoDB 集合创建、更新、删除
1. 创建集合 1.1 语法 db.createCollection(name, options) 参数说明: name: 要创建的集合名称。options: 可选参数, 指定有关内存大小及索引的选项。 options 可以是如下参数: 参数名类型描述示例值capped布尔值是否创建一个固定大小的集合。truesize…...

PHP中如何将变量从函数传递给acf_add_filter
在PHP开发中,我们有时需要将变量从函数传递给acf的add_filter钩子。这样做可以让我们在acf字段加载时,对字段值进行动态修改。下面,我将详细介绍如何实现这一功能。 在acf中,我们使用add_filter来添加钩子,对字段的加…...

KNN算法的使用
目录 一、KNN 算法简介 二、KNN算法的使用 1.读取数据 2.处理数据 三、训练模型 1.导入KNN模块 2.训练模型 3.出厂前测试 四、进行测试 1.处理数据 2.进行测试 总结 一、KNN 算法简介 KNN 是一种基于实例的学习算法。它通过比较样本之间的距离来进行预测。算法的核心…...

java文件上传
导入jar包,或者maven <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>…...

MySQL 数据库经验总结
一、数据库操作 1. 创建数据库 CREATE DATABASE database_name;例如,创建一个名为 my_database 的数据库: CREATE DATABASE my_database;2. 选择数据库 USE database_name;要使用刚才创建的 my_database 数据库: USE my_database;3. 删除…...

Python环境安装及PIP安装(Mac OS版)
官网 https://www.python.org/downloads/ 安装python python-3.12.1-macos11.pkg下载后,安装一直下一步即可 验证是否安装成功,执行python3命令和pip3命令 配置环境变量 获取python3安装位置并配置在.bash_profile #查看python路径 which python3#…...

2024自动驾驶(多模态)大模型综述:从DriveGPT4、DriveMLM到DriveLM、DriveVLM
前言 由于今年以来,一直在不断深挖具身智能机器人相关,而自动驾驶其实和机器人有着无比密切的联系,甚至可以认为,汽车就是一个带着4个轮子的机器人 加之个人认为,目前大模型落地潜力最大的两个方向,一个是…...

晨控CK-GW08-EC与汇川AC801系列PLC的EtherCAT通讯连接说明手册
晨控CK-GW08-EC与汇川AC801系列PLC的EtherCAT通讯连接说明手册 晨控CK-GW08-EC是一款支持标准工业通讯协议EtherCAT的网关控制器,方便用户集成到PLC等控制系统中。系统还集成了8路读写接口,用户可通过通信接口使用EtherCAT协议对8路读写接口所连接的读卡器进行相对…...

向上or向下调整建堆 的时间复杂度的本质区别的讲解
知识点:(N代表节点数,h代表高度) 1:高度为h的满二叉树节点个数N为 2^(h)-1 即N 2^(h)-1 2:所以h log(N1) 一:向上…...

阿一网络安全实战演练之利用 REST URL 中的服务器端参数污染
所需知识 要解决这个实验室问题,您需要了解以下内容: 如何确定用户输入是否包含在服务器端的 URL 路径或查询字符串中。如何使用路径遍历序列尝试更改服务器端请求。如何查找 API 文档。 这些内容在我们的 API 测试学院主题中有涵盖。 进入实验室 研…...

[游戏开发] LuaTable转string存读二进制文件
UE5和Unity通用此方案,只不过读写文件的接口略有不同,lua代码的处理是相同的。 下面两个方法是 LuaTable和字符串互相转换的代码 function XUtils.luaTableToString(tab, sp)sp sp or ""local s ""for k,v in pairs(tab) doif t…...

光伏业务管理系统的一些妙用功能
现在信息化流程化基本上每个行业都必须要有的了,光伏业务管理系统软件是一种专门用于光伏产业运营和管理的综合性系统,它结合了信息技术、数据分析、项目管理、客户管理等多个领域的知识,为光伏企业提供了一个全面、高效、智能的管理平台&…...

Java面试八股之请简述消息队列的发布订阅模式
请简述消息队列的发布订阅模式 发布订阅(Publish-Subscribe,简称 Pub/Sub)模型是一种消息传递模式,它在组件之间提供了高度的解耦和灵活性。这种模式广泛应用于分布式系统、事件驱动架构以及消息队列系统中。下面是发布订阅模型的…...

七、2 ADC数模转换器有关函数介绍(Keil5)
函数介绍 (1)ADCCLK的配置函数(在rcc.h中) (2)ADC的库函数(在adc.h中)...

了解载波侦听多路访问CSMA(上)
1.CSMA的思想 CSMA的全称是Carrier Sense Multiple Access,在笔者的理解中,其更趋向于一种理论研究的随机接入协议,或者说,基于其思想诞生了比如CSMA/CD与CSMA/CA这样的具体协议。CSMA可以分成以下三种: 1-persistent…...

开启教育新征程:“集师” 知识付费平台搭建
在教育培训行业竞争日益激烈的今天,如何脱颖而出,实现知识的最大价值?答案就在 “集师” 知识付费平台搭建! “集师” 为您打造专属的知识付费平台,提供一站式解决方案。无论您是专注于学科教育、艺术培训还是职业技能…...

Vue3 + Electron 创建新的子窗口 且子窗口唯一
main.js const { app, BrowserWindow, ipcMain } require(electron) ...ipcMain.on(window-create, () > {createChildWindow() })let childWindow nullconst createChildWindow () > {// 如果窗口存在 先销毁if (childWindow) {childWindow.destroy()childWindow n…...

海康VisionMaster使用学习笔记2-相机取图及参数设置
相机取图及参数设置 1. 关联相机-相机管理界面 除了以上两类外,第三方相机都可以通过全局相机进行连接 2. 相机参数设置 相机连接 跨网段IP,枚举 图像缓存数量 实时取流,断线重连 只有支持组播的相机才可以实时取流 触发设置 触发源 LINE0 可以保护电路 LINE2 可配置输入输出…...

【网络】【Linux】Linux内核中连接的组织形式与全连接队列
Linux内核中连接的组织形式与全连接队列 文章目录 1.前言2.Linux内核中连接的组织形式2.1套接字和文件描述符2.2创建连接 & 获取连接 3.全连接队列3.1为什么有全连接队列?3.2全连接队列的长度 1.前言 TCP是面向连接的,TCP的各种可靠性机制实际都不…...

记录一次 npm ERR! cb() never called! 解决过程
gitlab cicd过程,使用docker部署Vue3前端项目,报错如下: 针对 npm ERR! cb() never called! 这个报错,网上有很多解决方案,大都是清空缓存,重新运行npm 之类的。笔者全都试过,无法解决问题。笔者…...

WEB渗透免杀篇-加载器免杀
SSI加载 https://github.com/DimopoulosElias/SimpleShellcodeInjector生成payload(c) msfvenom -p windows/meterpreter/reverse_tcp lhost192.168.0.108 lport12138 -f c -o shellcode.c执行 cat shellcode.c |grep -v unsigned|sed "s/\"\\\x//g"|sed &quo…...

什么是反人性设计?
目录 一、什么是人性? 二、什么是反人性设计? 三、有哪些反人性设计? 一、什么是人性? 人性,通常指的是人类共有的基本特质和行为倾向,它涵盖了一系列心理、情感和社会属性。人性可以从多个角度来理解&a…...

如何进行长截图的两种方法
前言 本文主要讲2种截图方式,分别是谷歌和QQ。 谷歌分为Web端 和 移动端,选一种即可。 第一种:谷歌浏览器控制台自带的 1.先把控制台语言更改为中文,方便查看 ①.按F12,点击设置面板 ②.修改语言为中文并关闭 ③.点击…...

基于轨迹的汽车跟随系统横向控制方法
A Trajectory-Based Approach for the Lateral Control of Vehicle Following Systems 基于轨迹的汽车跟随系统横向控制方法 Abstract Abstract| A crucial task for steering an autonomous vehicle along a safe path in a vehicle following scenario is the lateral cont…...

2024年8月15日嵌入式学习
今日主要学习线程和线程的互斥锁 pthread_cancel函数 它用于取消一个线程,当一个线程收到取消的申请时,他不会立即停止,而是在下一个取消点处结束运行,取消点是程序中一个特定的位置。如果线程在执行一个不可中断的系统调用&…...

C++引用和指针的区别还分不清楚?
不像其他语言,c既有引用的概念、又有指针的概念。 很多人用着用着就懵了。 不用慌,给你画个表格协助判断。 总体上,我们可以总结为以下五个区别: 一、定义方式: 指针通过使用 * 来定义,例如࿱…...

【Cesium开发实战】相机捕捉功能,获取当前视图,设定分辨率可下载当前视图图片
Cesium有很多很强大的功能,可以在地球上实现很多炫酷的3D效果。今天给大家分享一个相机捕捉功能,支持可以按照设定的分辨率下载当前视角的缩略图。 1.话不多说,先展示 相机快照 2.设计思路 根据项目需求要求,点击快照捕捉按钮可截取当前视角视图为缩略图,并弹框可输入视…...

基于spring boot的疫情信息管理系统
TOC springboot255基于spring boot的疫情信息管理系统 绪论 1.1研究背景与意义 信息化管理模式是将行业中的工作流程由人工服务,逐渐转换为使用计算机技术的信息化管理服务。这种管理模式发展迅速,使用起来非常简单容易,用户甚至不用掌握…...

【秋招笔试】8.11大疆秋招(第二套)-测开岗
🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收…...