目录
静态工具DB
有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db
,其中的一些静态方法与IService
中方法签名基本一致,也可以帮助我们实现CRUD功能:
循环依赖的定义:
循环依赖(Circular Dependency) 是指 两个或多个组件(类、模块、包、服务等)之间存在直接或间接的相互依赖关系,形成一个闭环,导致系统无法正确初始化、编译或维护。
静态根据DB示例:
@Test
void testDbGet() {
User user = Db.getById(1L, User.class);
System.out.println(user);
}
@Test
void testDbList() {
// 利用Db实现复杂条件查询
List<User> list = Db.lambdaQuery(User.class)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000)
.list();
list.forEach(System.out::println);
}
@Test
void testDbUpdate() {
Db.lambdaUpdate(User.class)
.set(User::getBalance, 2000)
.eq(User::getUsername, "Rose");
}
Db.lambdaQuery(Address.class)中参数位置需要放实体类对象的字节码文件,通过传入实体类的class字节码,拿到字节码就能通过反射拿到实体类的相关信息,从而拿到注解上的信息诸如类名、表名等,以此实现CURD。
示例:
需求一
改造根据id用户查询的接口,查询用户的同时返回用户收货地址列表
(在userService中要注入addressService,需要查询收货地址列表)
首先,我们要添加一个收货地址的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,添加一个地址属性,用来存储当前用户的地址列表属性:
@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;
}
接下来,修改UserController中根据id查询用户的业务接口:
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@PathVariable("id") Long userId){
// 基于自定义service方法查询
return userService.queryUserAndAddressById(userId);
}
由于查询业务复杂,所以要在service层来实现。首先在IUserService中定义方法:
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
public interface IUserService extends IService<User> {
void deduct(Long id, Integer money);
UserVO queryUserAndAddressById(Long userId);
}
然后,在UserServiceImpl中实现该方法:
@Override
public UserVO queryUserAndAddressById(Long Id) {
// 1.查询用户
User user = this.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
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
//转地址VO
if (CollUtil.isNotEmpty(addresses)){
userVO.setAddresses(BeanUtil.copyToList(addresses,AddressVO.class));
}
return userVO;
}
BeanUtil.copyProperties :
将一个对象的属性值拷贝到另一个已存在的对象中(浅拷贝)。
BeanUtil.copyToList:
将一个 List<源对象> 拷贝成一个新的 List<目标对象>,每个元素通过 copyProperties 创建新对象,因为是创建新对象,所以是一种深拷贝。
上面两个方法的第二个参数都可以是实体类的字节码,就比如user.class形式的格式,表示在项目运行的时候,会创建一个user的对象去接收源对象的数据,可以避免自己通过NEW关键字去创建对象接收数据。
在上面的代码中,在查询地址时,我们采用了Db的静态方法,因此避免了注入AddressService,减少了循环依赖的风险。即通过DB的lambdaQuery()方法,在参数中放我们要操作的实体类对象,我们可以避免循环依赖的问题,该方法会通过反射获取实体类的相关信息,然后根据我们的条件对当前实体类的表格进行CRUD操作,这种方式避免了注入依赖,也相当于避免了循环依赖。
需求二
根据id批量查询用户,并查询出用户对应的所有地址
controller层
@GetMapping
@ApiOperation("根据id批量查询用户接口")
public List<UserVO> queryUserByIds(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
return userService.queryUserAndAddressByIds(ids);
}
service层
@Override
public 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.转换地址VO
List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
// 2.4.用户地址集合分组处理,相同用户的放入一个集合(组)中
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) {
// 3.1.转换User的PO为VO
UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
list.add(vo);
// 3.2.转换地址VO
vo.setAddresses(addressMap.get(user.getId()));
}
return list;
}
和查询单个用户的区别在于:根据id对查询出来的地址通过map来分组的实现逻辑。