【Mybatis-Plus】——第二章 快速入门与核心功能(下)

目录

3. 扩展功能

3.1. 代码生成

3.1.1 安装插件

3.1.2 使用步骤

3.2. 静态工具 Db

实战案例:查询用户及收货地址

3.3. 逻辑删除

配置步骤

测试

3.4. 通用枚举

3.4.1 定义枚举

3.4.2 修改实体类

3.4.3 配置枚举处理器

3.4.4 测试

3.5. JSON 类型处理器

3.5.1 定义实体

3.5.2 使用类型处理器

3.5.3 修改 VO

3.6. 配置加密(选学)

3.6.1 生成密钥

3.6.2 修改配置

3.6.3 测试

4. 插件功能

4.1. 分页插件

4.1.1 配置分页插件

4.1.2 分页 API

4.2. 通用分页实体

4.2.1 定义实体

4.2.2 开发接口


本文介绍了MyBatisPlus的扩展功能和插件使用。主要包括:

1)使用IDEA插件简化代码生成;

2)通过Db静态工具解决循环依赖问题;

3)逻辑删除配置及注意事项;

4)枚举类型处理器的应用;

5)JSON字段处理方案;

6)配置文件加密方法。插件部分重点讲解了分页插件的配置使用,并展示了通用分页实体的实现方案,包括分页条件封装、结果转换等。这些功能可显著提升开发效率,简化CRUD操作,建议根据项目需求选择性使用。

3. 扩展功能

3.1. 代码生成

MyBatis Plus 官方提供了代码生成器,但使用起来比较麻烦。推荐使用 IDEA 插件:

3.1.1 安装插件

在 IDEA 的 plugins 市场中搜索并安装 MyBatis Plus 插件:

然后重启 IDEA 即可使用。

3.1.2 使用步骤
  1. 配置数据库连接:在 IDEA 顶部菜单中,找到 OtherConfig Database

在弹出的窗口中填写数据库连接的基本信息:

点击 OK 保存。

  1. 生成代码:再次点击 IDEA 顶部菜单中的 Other,然后选择 Code Generator

在弹出的表单中填写信息:

最终,代码自动生成到指定的位置了;

3.2. 静态工具 Db

Service 之间相互调用可能出现循环依赖问题,MyBatis Plus 提供了静态工具类 Db

示例代码如下:

@Test
void testDbGet() {
    User user = Db.getById(1L, User.class);
    System.out.println(user);
}
​
@Test
void testDbList() {
    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");
}
实战案例:查询用户及收货地址

定义 AddressVO

@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("是否是默认") private Boolean isDefault;
    @ApiModelProperty("备注") private String notes;
}

改造 UserVO,添加地址属性:

@Data
@ApiModel(description = "用户VO实体")
public class UserVO {
    // ... 原有属性
    @ApiModelProperty("收货地址列表")
    private List<AddressVO> addresses;
}

在 Service 中实现查询:

@Override
public UserVO queryUserAndAddressById(Long userId) {
    User user = getById(userId);
    if (user == null) {
        return null;
    }
    List<Address> addresses = Db.lambdaQuery(Address.class)
            .eq(Address::getUserId, userId)
            .list();
    UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
    userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
    return userVO;
}

3.3. 逻辑删除

对于重要数据,采用逻辑删除方案(标记删除而非物理删除)。

配置步骤
  1. 添加逻辑删除字段

ALTER TABLE address ADD deleted BIT DEFAULT b'0' NULL COMMENT '逻辑删除';
  1. 在实体类中添加字段

@TableLogic
private Integer deleted;
  1. 在配置文件中配置

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted       # 全局逻辑删除字段名
      logic-delete-value: 1             # 逻辑已删除值
      logic-not-delete-value: 0         # 逻辑未删除值
测试
@Test
void testDeleteByLogic() {
    addressService.removeById(59L);  // 实际执行 UPDATE address SET deleted=1 WHERE id=59
}

方法与普通删除一样,但底层的 SQL 逻辑变了:

查询一下:

@Test
void testQuery() {
    List<Address> list = addressService.list();  // 自动过滤已删除数据
    list.forEach(System.out::println);
}

会发现 id 为 59 的没有查询出来,而且 SQL 中也对逻辑删除字段做了判断:

开启了逻辑删除功能以后,就可以像普通删除一样做 CRUD,基本不用考虑代码逻辑问题。

注意:逻辑删除也有缺点(无法真正删除、影响查询性能),建议采用数据迁移到其他表的方案。

3.4. 通用枚举

User 类中有用户状态字段(Integer 类型),业务判断时需要手动转换枚举,非常麻烦。MyBatis Plus 提供了枚举类型转换器。

3.4.1 定义枚举
package com.example.mp.enums;
​
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
​
@Getter
public enum UserStatus {
    NORMAL(1, "正常"),
    FREEZE(2, "冻结");
​
    @EnumValue  // 标记数据库存储值
    private final int value;
    
    @JsonValue  // 标记 JSON 序列化展示值
    private final String desc;
​
    UserStatus(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }
}
3.4.2 修改实体类
public class User {
    // ... 其他字段
    private UserStatus status;  // 将 Integer 改为枚举类型
}
3.4.3 配置枚举处理器
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
3.4.4 测试
@Test
void testService() {
    List<User> list = userService.list();
    list.forEach(System.out::println);  // status 字段为枚举类型
}

3.5. JSON 类型处理器

数据库的 user 表中有一个 info 字段(JSON 类型),目前是 String 类型,读取属性不方便。MyBatis Plus 提供了 JacksonTypeHandler 处理器。

3.5.1 定义实体
package com.example.mp.domain.po;
​
import lombok.Data;
​
@Data
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}
3.5.2 使用类型处理器
@TableName(value = "user", autoResultMap = true)
public class User {
    // ... 其他字段
    @TableField(typeHandler = JacksonTypeHandler.class)
    private UserInfo info;
}
3.5.3 修改 VO
@Data
public class UserVO {
    // ... 其他字段
    private UserInfo info;  // 将 String 改为 UserInfo 类型
}

3.6. 配置加密(选学)

配置文件中的敏感信息(如数据库密码)建议加密存储。

3.6.1 生成密钥
@Test
void contextLoads() {
    // 生成 16 位随机 AES 密钥
    String randomKey = AES.generateRandomKey();
    System.out.println("randomKey = " + randomKey);
    
    // 加密用户名和密码
    String username = AES.encrypt("root", randomKey);
    System.out.println("username = " + username);
    
    String password = AES.encrypt("MySQL123", randomKey);
    System.out.println("password = " + password);
}
3.6.2 修改配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: mpw:QWWVnk1Oal3258x5rVhaeQ==  # 密文以 mpw: 开头
    password: mpw:EUFmeH3cNAzdRGdOQcabWg==
3.6.3 测试

启动项目时添加密钥参数:

--mpw.key=6234633a66fb399f

单元测试时在测试类注解上配置:

@SpringBootTest(properties = {"mpw.key=6234633a66fb399f"})

4. 插件功能

MyBatis Plus 提供了很多插件功能,使用多个插件时需注意定义顺序。

4.1. 分页插件

未引入分页插件时,IServiceBaseMapper 中的分页方法无法正常起效。

4.1.1 配置分页插件
package com.example.mp.config;
​
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
@Configuration
public class MybatisConfig {
​
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
4.1.2 分页 API
@Test
void testPageQuery() {
    Page<User> p = userService.page(new Page<>(2, 2));
    System.out.println("total = " + p.getTotal());
    System.out.println("pages = " + p.getPages());
    p.getRecords().forEach(System.out::println);
}

分页参数 Page 支持排序:

int pageNo = 1, pageSize = 5;
Page<User> page = Page.of(pageNo, pageSize);
page.addOrder(new OrderItem("balance", false));  // 按余额降序
userService.page(page);

4.2. 通用分页实体

实现用户分页查询接口:

4.2.1 定义实体

PageQuery(通用分页条件)

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码") private Long pageNo;
    @ApiModelProperty("每页大小") private Long pageSize;
    @ApiModelProperty("排序字段") private String sortBy;
    @ApiModelProperty("是否升序") private Boolean isAsc;
​
    public <T> Page<T> toMpPage(OrderItem ... orders) {
        Page<T> p = Page.of(pageNo, pageSize);
        if (sortBy != null) {
            p.addOrder(new OrderItem(sortBy, isAsc));
            return p;
        }
        if (orders != null) {
            p.addOrder(orders);
        }
        return p;
    }
​
    public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc) {
        return this.toMpPage(new OrderItem(defaultSortBy, isAsc));
    }
​
    public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {
        return toMpPage("update_time", false);
    }
}

UserQuery(继承分页条件)

@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery extends PageQuery {
    @ApiModelProperty("用户名关键字") private String name;
    @ApiModelProperty("用户状态") private Integer status;
    @ApiModelProperty("余额最小值") private Integer minBalance;
    @ApiModelProperty("余额最大值") private Integer maxBalance;
}

PageDTO(分页结果)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageDTO<V> {
    @ApiModelProperty("总条数") private Long total;
    @ApiModelProperty("总页数") private Long pages;
    @ApiModelProperty("集合") private List<V> list;
​
    public static <V, P> PageDTO<V> empty(Page<P> p) {
        return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());
    }
​
    public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) {
        List<P> records = p.getRecords();
        if (records == null || records.size() <= 0) {
            return empty(p);
        }
        List<V> vos = BeanUtil.copyToList(records, voClass);
        return new PageDTO<>(p.getTotal(), p.getPages(), vos);
    }
​
    public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) {
        List<P> records = p.getRecords();
        if (records == null || records.size() <= 0) {
            return empty(p);
        }
        List<V> vos = records.stream().map(convertor).collect(Collectors.toList());
        return new PageDTO<>(p.getTotal(), p.getPages(), vos);
    }
}
4.2.2 开发接口
@GetMapping("/page")
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
    return userService.queryUsersPage(query);
}

Service 实现

@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
    Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
    page(page);
    return PageDTO.of(page, UserVO.class);
}

自定义转换示例(用户名脱敏):

@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
    Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();
    page(page);
    return PageDTO.of(page, user -> {
        UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
        String username = vo.getUsername();
        vo.setUsername(username.substring(0, username.length() - 2) + "**");
        return vo;
    });
}

总结

MyBatis Plus 通过 BaseMapperIService 简化了单表 CRUD 操作,通过条件构造器实现了复杂查询,通过插件机制扩展了分页等功能。掌握这些核心技能,可以大幅提高开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值