mp

MybatisPlus

MybatisPlus 是基于 MyBatis 的增强工具,它简化了 MyBatis 的开发,并提供了一些常用的自动化功能,如 CRUD 操作的自动生成
MybatisPlus 的目标是使得开发者 不再编写重复的 SQL 语句,同时保留 MyBatis 的原有功能,使得 MyBatis 更加轻量、简洁、高效

设计表结构:

balance n.用户余额

引入 MP 依赖

注:mp 依赖包含了 mp 和 mybatis,所以可以删除 mybatis 的依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.7</version>
</dependency>

继承 BaseMapper<T>

这样 mapper 就拥有了 BaseMapper 内置的增删改查方法,简单的方法可以从 BaseMapper 直接继承使用

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

内置方法如:
image

在 service 层,UserService 拥有了 BaseService 的方法 -> 直接调用

@Service
public class UserServiceImpl implements UserService {
    private final UserMapper userMapper;

    public void saveUser(User user) {
        userMapper.insert(user);
    }

MybatisPlus 原理

MybatisPlus 可以自动增删改查的原理:

通过扫描实体类,并基于反射获取实体类信息作为数据库表信息,然后通过 约定 获得数据库字段信息

约定 大于 配置

  • 类名驼峰转下划线作为表名
  • 名为 id 的字段作为主键
  • 变量名驼峰转下划线作为字段名

如果不遵守约定,可用注解:

注解 作用
@TableName 用来指定表名
@Tableld 用来指表中的主键字段信息
@TableField 用来指定表中的普通字段信息

配置

mybatis-plus:
  type-aliases-package: com.wyn.springcloud.pojo # 别名扫描包
  configuration:
    map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      id-type: assign_id # id为雪花算法生成
      update-strategy: not_null # 更新策略:只更新非空字段

条件构造器

BaseMapper 接口含有一些方法,形参 Wrapper 是条件构造器

Wrapper 包装器

image

Wrapper 的继承关系:

image

1.5.1 QueryWrapper 查询

查找名字带o,余额大于等于 1000 元的idusernaminfobalance 字段

eq:等于

ne:不等于

ge:大于等于

gt:大于

le:小于等于

lt:小于

public List<User> ListByUsername(String s) {
    QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
            .select("id", "username", "balance", "info")
            .like("username", s)
            .ge("balance", "1000");
    List<User> users = userMapper.selectList(queryWrapper);
    return users;
}

1.5.2 UpdateWrapper 更新

  • 更新 jack 的余额为 2000

更新方法的传参有两个:一个是 用户实体,一个是 updateWrapper

/**
 * balance set to 2000 by username
 *
 * @param username
 */
@Override
public void updateBalanceByUsername(String username) {
    User user = User.builder()
            .balance(2000)
            .build();
    UpdateWrapper updateWrapper = new UpdateWrapper<>()
            .eq("username", username);
    userMapper.update(user, updateWrapper);
}

这里注意实体类规定了更新的字段,装饰器规定了要更新的人,是与逻辑相反


  • 更新 id1, 2, 3 的用户,余额扣 200
// controller
/**
  * balance minus 100 by ids
  * @param ids
  * @return
  */
@GetMapping("/minusBalance")
public Result MinusBalanceByIds(@RequestParam List<Long> ids){ // notice this annotation 																  // @RequestParam
    userService.minusBalanceByIds(ids);
    return Result.success();
}

// service
public void minusBalanceByIds(List<Long> ids) {
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>()
            .in("id", ids)
            .setSql("balance = balance - 100");
    userMapper.update(null, updateWrapper);
}

对于 List 这样的集合类型需要加上注解 @RequestParam,而普通的数据类型则不用

IService 接口

image

Mybatis 提供了 IService 和其实现类 ServiceImpl

UserService 继承 IService
UserServiceImpl 继承 ServiceImpl,加上泛型,然后实现 UserService

public interface UserService extends IService<User>
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService

由于 UserService 继承了 IService,所以 UserService 拥有 ServiceImpl 的实现

UserMapper 继承 BaseMapper

public interface UserMapper extends BaseMapper<User>

这样就可以直接调用 ServiceImpl 的内置方法

/**
 * get user by id
 *
 * @param id
 * @return
 */
@GetMapping("/getById")
public Result<User> getById(Long id) {
    return Result.success(userService.getById(id));
}

(补充)自动注入

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
}

编译器提示不建议使用字段注入

接下来使用构造函数

@RestController
@RequestMapping("/users")
public class UserController {
	private final UserService userService;
	public UserController(UserService userService) {
    	this.userService = userService;
	}
}

使用Lombok简化:

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
}

使用 @RequiredArgsConstructor注解时,Lombok 会自动生成一个包含所有 final字段和使用 @NonNull注解标记的字段的构造函数

综合应用

image


  • 新增用户
/**
 * save user by JSON
 * @param userDTO
 * @return
 */
@PostMapping
public Result saveUser(@RequestBody UserDTO userDTO) {
    User user = new User();
    BeanUtil.copyProperties(userDTO, user); // hutool
    userService.save(user);
    return Result.success();
}

需要引入hutool工具包

<dependency>
 <groupId>cn.hutool</groupId>
 <artifactId>hutool-all</artifactId>
 <version>5.8.20</version>
</dependency>

  • 根据id批量查询
/**
 * query users by ids
 *
 * @param ids
 * @return
 */
@Override
public List<UserVO> selectByIds(List<Long> ids) {
    List<UserVO> userVOList = new ArrayList<>();
    ids.forEach(id -> {
        UserVO userVO = new UserVO();
        User user = userMapper.selectById(id);
        BeanUtil.copyProperties(user, userVO);
        userVOList.add(userVO);
    });
    return userVOList;
}

遍历 ids,通过 BaseMapperselectById 一个一个找,然后加入 list

条件查询

username:用户名关键字,可以为空

status:用户状态,可以为空

minBalance:最小余额,可以为空

maxBalance:最大余额,可以为空

// Controller
/**
 * query by name,status,maxBalance,minBalance
 *
 * @param userQueryDTO
 * @return
 */
@PostMapping("/query")
public Result<List<UserVO>> queryByCondition(UserQueryDTO userQueryDTO) { // custom class
    List<UserVO> userVOList = userService.queryByCondition(userQueryDTO);
    return Result.success(userVOList);
}

// Service
    public List<UserVO> queryByCondition(UserQueryDTO userQueryDTO) {
        List<UserVO> userVOList = new ArrayList<>();
        String username = userQueryDTO.getUsername();
        Integer status = userQueryDTO.getStatus();
        Integer maxBalance = userQueryDTO.getMaxBalance();
        Integer minBalance = userQueryDTO.getMinBalance();

        List<User> list = lambdaQuery()
                .like(username != null, User::getUsername, username)
                .eq(status != null, User::getStatus, status)
                .ge(minBalance != null, User::getBalance, minBalance)
                .le(maxBalance != null, User::getBalance, maxBalance)
                .list();
        list.forEach(user -> {
            UserVO userVO = new UserVO();
            BeanUtil.copyProperties(user, userVO);
            userVOList.add(userVO);
        });
        return userVOList;
    }

DB 静态工具类

DB 是 mp 的静态工具类,为了防止 循环依赖

使用时可以 不注入 UserMapper

循环依赖(Circular Dependency)是指两个或多个对象(类、模块、Bean 等)相互依赖,形成一个循环的依赖关系。具体来说,如果对象 A 依赖对象 B,而对象 B 又依赖对象 A,这种情况就会导致循环依赖问题。循环依赖可能导致系统无法正确初始化,尤其是在依赖注入框架(如 Spring)中,会抛出异常,无法实例化相关的对象

加入一张地址表

image

UserVO 视图对象中,加入每个UserVO对应的AddressVO,有多个,所以用List<AddressVO>

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserVO {
    // ...
    private List<AddressVO> addressList;
}

查询User和对应的Address

// Controller
/**
 * get user and address by id
 *
 * @param id
 * @return
 */
@GetMapping("/getById")
public Result<UserVO> getUserAndAddressById(Long id) {
    return Result.success(userService.getUserAndAddressById(id));
}

// Service
public UserVO getUserAndAddressById(Serializable id) {
    User user = getById(id);
    UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
    List<Address> addressList = Db.lambdaQuery(Address.class)
        .eq(Address::getUserId, id)
        .list();
    //        List<AddressVO> addressVOList = new ArrayList<>();
    //        addressList.forEach(address -> {
    //            AddressVO addressVO = BeanUtil.copyProperties(address, AddressVO.class);
    //            addressVOList.add(addressVO);
    //        });
    //        userVO.setAddressList(addressVOList);
    userVO.setAddressList(BeanUtil.copyToList(addressList, AddressVO.class)); // reflection
    return userVO;
}

这里使用 DblambdaQuery 方法,传参是查询的实体类的 字节码(反射)

避免了引入AddressMapper造成 循环依赖

逻辑删除

逻辑删除 是一种软删除策略,即在数据库中并不直接物理删除数据行,而是通过设置某个 标志位 来标记该数据已被删除。这样做的好处是,数据不会从数据库中被永久删除,仍然可以恢复或用于历史查询

配置 logic-delete-field: deleted

mybatis-plus:
  type-aliases-package: com.wyn.springcloud.pojo # 别名扫描包
  configuration:
    map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
  global-config:
    db-config:
      id-type: assign_id # id为雪花算法生成
      update-strategy: not_null # 更新策略:只更新非空字段
      logic-delete-field: deleted # 逻辑删除字段名称

User中配置字段 deleted

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    // ...
    @TableLogic
    private Integer deleted;
}

分页查询

// 通用分页查询实体类
@Data
@ApiModel(description = "分页查询实体类")
public class PageQuery {
    @ApiModelProperty("页码")
    private Integer pageNo;
    @ApiModelProperty("页数")
    private Integer pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;
}

// 用户查询实体类
@ApiModel(description = "user condition select entity")
@Data
public class UserQuery extends PageQuery{ // 继承通用类
    private String name;
    private Integer status;
    private Integer minBalance;
    private Integer maxBalance;
}

// 分页传输类
@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
    @ApiModelProperty("总条数")
    private Integer total;
    @ApiModelProperty("总页数")
    private Integer pages;
    @ApiModelProperty("数据集合")
    private List<T> list;
}
// Controller
@ApiOperation("条件分页查询")
@GetMapping("/page")
public Result<PageDTO<UserVO>> queryUserPage(UserQuery userQuery){
    return Result.success(userService.queryUserPage(userQuery));
}

// Service
public PageDTO<UserVO> queryUserPage(UserQuery userQuery) {
    String name = userQuery.getName();
    Integer status = userQuery.getStatus();
    Integer minBalance = userQuery.getMinBalance();
    Integer maxBalance = userQuery.getMaxBalance();

    Integer pageNo = userQuery.getPageNo();
    Integer pageSize = userQuery.getPageSize();
    String sortBy = userQuery.getSortBy();
    Boolean isAsc = userQuery.getIsAsc();

    Page<User> page = Page.of(pageNo, pageSize);

    OrderItem orderItem = new OrderItem();
    // 排序字段非空才可以排序
    if (!sortBy.isEmpty()) {
        orderItem.setColumn(sortBy);
        orderItem.setAsc(isAsc);
    }
    page.addOrder(orderItem);

    Page<User> p = lambdaQuery()
        .like(name != null, User::getUsername, name)
        .eq(status != null, User::getStatus, status)
        .ge(minBalance != null, User::getBalance, minBalance)
        .ge(maxBalance != null, User::getBalance, maxBalance)
        .page(page);

    // 构造返回结果
    PageDTO<UserVO> dto = new PageDTO<>();
    dto.setTotal(p.getTotal());
    dto.setPages(p.getPages());
    List<User> userList = p.getRecords();
    if (userList.isEmpty()) {
        dto.setList(null);
    } else {
        List<UserVO> userVOList = BeanUtil.copyToList(userList, UserVO.class);
        dto.setList(userVOList);
    }
    return dto;
}

代码可以抽取,作为 PageQueryPageDTO 的方法 -> 复用

这里为什么要返回PageDTO而不是Page——mybatis 的分页类

因为 有些字段不需要

public class Page<T> implements IPage<T> {
 private static final long serialVersionUID = 8545996863226528798L;
 protected List<T> records;
 protected long total;
 protected long size;
 protected long current;
 protected List<OrderItem> orders;
 protected boolean optimizeCountSql;
 protected boolean searchCount;
 protected boolean optimizeJoinOfCountSql;
 protected Long maxLimit;
 protected String countId;
 // ...
}
posted @   RainNan  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示

目录导航