Java研学-MybatisPlus(三)

发布于:2025-05-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

四 拓展功能

1 代码生成器

  ① 安装MyBatisPlus插件(可根据表信息生成代码)
在这里插入图片描述
  ② 配置数据库信息 – Config Database

// mp_test为数据库
jdbc:mysql://localhost:3306/mp_test?useSSL=false&serverTimezone=Asia/Shanghai 

在这里插入图片描述
 ③ 生成数据库表

create table tb_student
(
    id    int         not null
        primary key,
    name  varchar(12) not null,
    phone int         null
)
    comment '学生';

  ④ 生成代码 – Code Generator
在这里插入图片描述

2 DB静态工具

  DB功能与IService接口相近,但IService接口中的方法是非静态的,而DB中的方法是静态的,相对于IService,DB需要多传递一个实体类的字节码参数,才能利用反射进行crud(除了save和update因为传了实体类对象),由此可避免循环依赖的问题(ServiceA,ServiceB相互注入),当ServiceA需要调用ServiceB时,直接使用静态工具即可,无需注入,避免循环依赖。
  ① 生成地址VO实体

@Data
@ApiModel(description = "地址VO")
public class AddressVO {
    @ApiModelProperty("id")
    private Integer id;
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("地址")
    private String address;
    @ApiModelProperty("电话")
    private Integer phone;
}

  ② 创建地址表 – 需自行编写测试数据user_id相等即可

create table tb_address
(
    id       int auto_increment
        primary key,
    user_id  int         null,
    username varchar(11) null,
    address  varchar(20) null,
    phone    varchar(11) null
)
    comment '地址';

  ③ 更新用户VO实体

@Data
@ApiModel(description = "用户VO实体")
@TableName("tb_user")
public class UserVo {
    @ApiModelProperty("id")
    private Integer id;
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("是否玩家")
    private Boolean isPlayer;
    @ApiModelProperty("测关键字")
    private Integer order;
    @ApiModelProperty("地址")
    private List<AddressVO> addresses;
}

  ④ 更新 UserController 方法 – 根据单个ID多表查询

@ApiOperation("通过id查询用户接口")
@GetMapping("{id}")
public UserVo queryUserById(@ApiParam("用户id")@PathVariable("id")Long id){
	return userService.queryUserAndAddressById(id);
}

  ⑤ 更新IUserService,IUserServiceImpl

public interface IUserService extends IService<User> {
    UserVo queryUserAndAddressById(Long id);
}
@Override
public UserVo queryUserAndAddressById(Long id) {
    // 1.查询用户
    User user = getById(id);
    if(user==null){
        throw new RuntimeException("用户不存在");
    }
    // 2.查询地址
    List<Address> addressList = Db.lambdaQuery(Address.class)
            .eq(Address::getUserId, id).list();
    // 3.封装VO
    UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
    if(CollUtil.isNotEmpty(addressList)){
    userVo.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));
    }
	return userVo;
}

  ⑥ 多表查询测试 – 通过单个userId查询多个地址
在这里插入图片描述
  ⑦ 更新 UserController 方法 – 根据多个ID多表查询

@ApiOperation("通过id批量查询用户接口")
@GetMapping
public List<UserVo> queryUserByIds(@ApiParam("用户id集合")@RequestParam("ids")List<Long> ids){
	return userService.queryUserAndAddressByIds(ids);
}

  ⑧ 更新IUserService,IUserServiceImpl

List<UserVo> queryUserAndAddressByIds(List<Long> ids);
    @Override
    public List<UserVo> queryUserAndAddressByIds(List<Long> ids) {
        // 1.查询用户
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)){
            // 使用 Collections.emptyList() 返回一个空列表 避免返回 null
            return Collections.emptyList();
        }
        // 2.获取用户id集合,通过user集合获取id集合
        List<Integer> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        // 3.通过用户id查询地址集合(所有用户的所有地址)
        List<Address> addressList = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        // 4.转换地址VO
        List<AddressVO> addressVOList = BeanUtil.copyToList(addressList, AddressVO.class);
        // 5. 若查询结果非空,将地址集合根据用户 ID 进行分组,key为用户ID,Value为用户对应的地址集合
        Map<Integer, List<AddressVO>> addressMap = CollUtil.isEmpty(addressVOList)
                ? Collections.emptyMap()
                : addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        // 6.转VO返回 提前定义集合大小防止临时扩容
        List<UserVo> list = new ArrayList<>(users.size());
        for (User user:users){
            // 转UserPO为VO
            UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
            list.add(userVo);
            // 转地址VO
            userVo.setAddresses(addressMap.get(user.getId()));
        }
        return list;
    }

  ⑨ 多表查询测试 – 通过userId集合查询多个地址
在这里插入图片描述

3 逻辑删除

  基于代码逻辑模拟删除效果,但不删除数据,通常为添加标记字段,删除时将数据标为1,只查询标记为0的数据,若数据本身为0,则不作更改。
  MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。在application.yaml文件中配置逻辑删除的字段名称和值即可
  需注意由于逻辑删除实际上并未删除数据,且会增加sql语句的逻辑判断,导致影响效率,因此可以考虑将要删除的数据迁移到其他表中,完成删除操作。
  ① 填写yaml文件

mybatis-plus:
  # 因mp不善于多表查询,故多表查询仍需手写
  type-aliases-package: com.tj.mp.demo.domain
  mapper-locations: classpath*:mapper/**/*.xml
  global-config:
    db-config:
      #设置全局逻辑删除的实体字段名,字段类型可以是boolean、integer
      logic-delete-field: deleted
      #值设为1表示删除(默认为1)
      logic-delete-value: 1
      #值设为0表示未删除(默认为0)
      logic-not-delete-value: 0

  ② 数据库增添逻辑删除字段 – deleted

alter table tb_address
	add `deleted` bit default 0 not null;

  ③ 实体类增添逻辑删除成员变量 – deleted

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_address")
@ApiModel(value="Address对象", description="地址")
public class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private Integer userId;
    private String username;
    private String address;
    private String phone;
    private Boolean deleted;
}

  ④ 测试类

@SpringBootTest
class IAddressServiceTest {
    @Autowired
    private IAddressService addressService;
    @Test
    void testLogicDelete(){
        // 1.根据用户id进行删除
        // UPDATE tb_address SET deleted=1 WHERE id=? AND deleted=0
        addressService.removeById(1);
        // 2.根据用户id进行查询
        // SELECT id,user_id,username,address,phone,deleted FROM tb_address WHERE id=? AND deleted=0
        System.out.println(addressService.getById(1));
    }
}

4 枚举处理器

  根据业务用户需要有多种状态,可供判断与赋值,应使用枚举类型(可使用==进行比较)维护可读性,但数据库中仍为整数类型,此时应做java枚举类型与mysql整数类型的转换(mybatis-plus提供)
  ① 配置yaml文件

mybatis-plus:
  type-aliases-package: com.tj.mp.demo.domain
  mapper-locations: classpath*:mapper/**/*.xml
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
  # 配置全局枚举处理器
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

  ② 更新数据库字段

alter table tb_user
	add status int default 1 null;

  ③ 定义枚举类

@Getter
public enum UserStatus {
    NORMAL(1, "正常"),
    FROZEN(2, "冻结"),
    ;
    // 对应数据库字段与成员变量进行绑定
    @EnumValue
    private final int value;
    // 枚举默认以枚举项名(FROZEN)返回前端,需通过注解告诉springMVC,想返给前端的数据
    @JsonValue
    private final String desc;// 描述

    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}

  ④ 更新PO实体类 – User

@Data
@AllArgsConstructor
@NoArgsConstructor
// PO 是与数据库表结构直接映射的对象,用于表示数据库中的记录。
@TableName("tb_user")
public class User {
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;
    @TableField(value = "username")
    private String username;
    @TableField("is_player")
    private Boolean isPlayer;
    @TableField("`order`")
    private Integer order;
    @TableField(exist = false)
    private String address;
    // 枚举类型状态
    private UserStatus status;
}

  ⑤ 更新PO实体类 – User

@Data
@ApiModel(description = "用户VO实体")
// VO 是为前端视图(如网页、移动端界面)设计的对象,用于展示数据。
@TableName("tb_user")
public class UserVo {
    @ApiModelProperty("id")
    private Integer id;
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("是否玩家")
    private Boolean isPlayer;
    @ApiModelProperty("测关键字")
    private Integer order;
    @ApiModelProperty("地址")
    private List<AddressVO> addresses;
    // 枚举类型状态
    @ApiModelProperty("状态")
    private UserStatus status;
}

  ⑥ 更新所涉及的方法 – User

    @Override
    public UserVo queryUserAndAddressById(Long id) {
        // 1.查询用户 判断用户状态
        User user = getById(id);
        if(user==null||user.getStatus()== UserStatus.FROZEN){
            throw new RuntimeException("用户状态异常");
        }
        // 2.查询地址
        List<Address> addressList = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id).list();
        // 3.封装VO
        UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
        if(CollUtil.isNotEmpty(addressList)){
            userVo.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));
        }
        return userVo;
    }

  ⑦ 测试
在这里插入图片描述

5 JSON处理器

  解决数据库中JSON与java类型的转换
  ① 更新数据库JSON字段

ALTER TABLE tb_user
    ADD info JSON DEFAULT '{}';

  ② 定义信息实体对象

@Data
@NoArgsConstructor
// 为这个构造方法创建一个静态工厂方法,方法名为 of
@AllArgsConstructor(staticName = "of")
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}

  ③ 更新PO实体类JSON成员变量

@Data
@AllArgsConstructor
@NoArgsConstructor
// 设置自动结果映射为true
@TableName(value = "tb_user",autoResultMap = true)
public class User {
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;
    @TableField(value = "username")
    private String username;
    @TableField("is_player")
    private Boolean isPlayer;
    @TableField("`order`")
    private Integer order;
    @TableField(exist = false)
    private String address;
    private UserStatus status;
    // 通过注解绑定JSON类型处理器(只针对当前字段有效)
    @TableField(typeHandler = JacksonTypeHandler.class)
    private UserInfo info;
}

  ④ 更新VO实体类JSON成员变量

@Data
@ApiModel(description = "用户VO实体")
@TableName("tb_user")
public class UserVo {
    @ApiModelProperty("id")
    private Integer id;
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("是否玩家")
    private Boolean isPlayer;
    @ApiModelProperty("测关键字")
    private Integer order;
    @ApiModelProperty("地址")
    private List<AddressVO> addresses;
    @ApiModelProperty("状态")
    private UserStatus status;
    @ApiModelProperty("信息")
    // 通过注解绑定JSON类处理器(只针对当前字段有效)
    @TableField(typeHandler = JacksonTypeHandler.class)
    private UserInfo info;
}

  ⑤ 插入测试

@SpringBootTest
class IUserServiceTest {
    @Autowired
    private IUserService userService;
    @Test
    void testSaveUser(){
        User user = new User();
        user.setId(9);
        user.setUsername("black");
        user.setIsPlayer(true);
        user.setOrder(3);
        user.setInfo(UserInfo.of(22,"项目经理","男"));
        userService.save(user);
    }
}

  ⑥ 查询
在这里插入图片描述