当前位置: 首页 > news >正文

软件工程实践四:MyBatis-Plus 教程(连接、分页、查询)

文章目录

    • 概述
    • 1. 依赖与连接配置
      • 1.1 Maven 依赖(片段)
      • 1.2 application.yml 连接
      • 1.3 启动类与包扫描
      • 1.4 分页插件配置
    • 2. 示例模型与 Mapper
      • 2.1 表结构(示例)
      • 2.2 实体 Entity
      • 2.3 Mapper 接口
    • 3. 常见查询(QueryWrapper / Lambda)
      • 3.1 基础查询
      • 3.2 LambdaQueryWrapper(更安全的字段引用)
      • 3.3 按条件可选拼装
    • 4. 分页查询(Page)
    • 5. JOIN(连接)查询的常见做法
      • 5.1 一对一(User ↔ UserProfile)
      • 5.2 一对多(User ↔ Orders)
      • 5.3 多对多(User ↔ Role,经 user_role 关联)
    • 6. Service 层(IService / ServiceImpl)
    • 7. Controller 层(CRUD + 分页)
    • 8. 实用补充与优化建议
    • 9. 小结

概述

  • 目标:用最少代码完成单表 CRUD、分页查询,并给出 JOIN 查询的常见做法。
  • 环境:Spring Boot 3.x + MyBatis-Plus 3.5+ + MySQL 8.x。

原文链接:https://blog.ybyq.wang/archives/1104.html


1. 依赖与连接配置

1.1 Maven 依赖(片段)

<dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.12</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

1.2 application.yml 连接

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCusername: rootpassword: root
mybatis-plus:configuration:map-underscore-to-camel-case: truemapper-locations: classpath*:mapper/**/*.xml

1.3 启动类与包扫描

@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 若在接口上使用 @Mapper,可不加 @MapperScan
public class DemoApplication {public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }
}

1.4 分页插件配置

@Configuration
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}

2. 示例模型与 Mapper

2.1 表结构(示例)

CREATE TABLE `t_user` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(64) NOT NULL,`age` INT NOT NULL,`email` VARCHAR(128),`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2.2 实体 Entity

@Data
@TableName("t_user")
public class User {@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;@TableField("created_at")private LocalDateTime createdAt;
}

2.3 Mapper 接口

public interface UserMapper extends BaseMapper<User> {}

3. 常见查询(QueryWrapper / Lambda)

3.1 基础查询

@Autowired private UserMapper userMapper;// 主键
User u1 = userMapper.selectById(1L);// 批量主键
List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L));// 条件查询(QueryWrapper)
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id", "name", "age").eq("age", 20).likeRight("name", "A").orderByDesc("id");
List<User> list = userMapper.selectList(qw);// 计数
Long count = userMapper.selectCount(new QueryWrapper<User>().ge("age", 18));

3.2 LambdaQueryWrapper(更安全的字段引用)

LambdaQueryWrapper<User> lw = Wrappers.lambdaQuery(User.class).select(User::getId, User::getName).between(User::getAge, 18, 30).like(User::getName, "Tom").orderByAsc(User::getCreatedAt);
List<User> list2 = userMapper.selectList(lw);

3.3 按条件可选拼装

String name = req.getName();
Integer minAge = req.getMinAge();LambdaQueryWrapper<User> w = Wrappers.lambdaQuery(User.class).like(StringUtils.hasText(name), User::getName, name).ge(minAge != null, User::getAge, minAge);
List<User> result = userMapper.selectList(w);

4. 分页查询(Page)

需先注册分页插件(见 1.4)。

int pageNo = 1, pageSize = 10;
Page<User> page = new Page<>(pageNo, pageSize);
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).ge(User::getAge, 18).orderByDesc(User::getId);Page<User> resultPage = userMapper.selectPage(page, wrapper);
List<User> records = resultPage.getRecords();
long total = resultPage.getTotal();
long pages = resultPage.getPages();

可在 Controller 中直接返回分页结果:

@GetMapping("/users")
public Page<User> list(@RequestParam int page, @RequestParam int size,@RequestParam(required = false) Integer minAge) {Page<User> p = new Page<>(page, size);LambdaQueryWrapper<User> w = Wrappers.lambdaQuery(User.class).ge(minAge != null, User::getAge, minAge).orderByDesc(User::getId);return userMapper.selectPage(p, w);
}

5. JOIN(连接)查询的常见做法

MyBatis-Plus 聚焦单表 CRUD;多表 JOIN 推荐使用自定义 SQL(注解或 XML)。

5.1 一对一(User ↔ UserProfile)

CREATE TABLE `user_profile` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT,`user_id` BIGINT NOT NULL UNIQUE,`phone` VARCHAR(32),`address` VARCHAR(255)
);
  • DTO
@Data
public class UserProfileDTO {private Long id;private Long userId;private String phone;private String address;
}@Data
public class UserWithProfileDTO {private Long id;private String name;private Integer age;private String email;private UserProfileDTO profile;
}
  • Mapper 接口
public interface UserProfileQueryMapper {UserWithProfileDTO selectUserWithProfile(@Param("userId") Long userId);
}
  • XML(resources/mapper/UserProfileQueryMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserProfileQueryMapper"><resultMap id="UserProfileMap" type="com.example.demo.dto.UserProfileDTO"><id property="id" column="p_id"/><result property="userId" column="p_user_id"/><result property="phone" column="p_phone"/><result property="address" column="p_address"/></resultMap><resultMap id="UserWithProfileMap" type="com.example.demo.dto.UserWithProfileDTO"><id property="id" column="u_id"/><result property="name" column="u_name"/><result property="age" column="u_age"/><result property="email" column="u_email"/><association property="profile" resultMap="UserProfileMap"/></resultMap><select id="selectUserWithProfile" resultMap="UserWithProfileMap">SELECTu.id AS u_id, u.name AS u_name, u.age AS u_age, u.email AS u_email,p.id AS p_id, p.user_id AS p_user_id, p.phone AS p_phone, p.address AS p_addressFROM t_user uLEFT JOIN user_profile p ON p.user_id = u.idWHERE u.id = #{userId}</select>
</mapper>

5.2 一对多(User ↔ Orders)

下例演示嵌套集合映射;若只需扁平结果,可参考 5.1/5.3 的 DTO 方式。

CREATE TABLE `order` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT,`user_id` BIGINT NOT NULL,`amount` DECIMAL(6,2)
);
  • DTO
@Data
public class OrderDTO {private Long id;private Long userId;private BigDecimal amount;
}@Data
public class UserWithOrdersDTO {private Long id;private String name;private List<OrderDTO> orders;
}
  • Mapper 接口
public interface UserOrderNestedMapper {UserWithOrdersDTO selectUsersWithOrders(@Param("userId") Long userId);
}
  • XML(resources/mapper/UserOrderNestedMapper.xml)
<mapper namespace="com.example.demo.mapper.UserOrderNestedMapper"><resultMap id="OrderMap" type="com.example.demo.dto.OrderDTO"><id property="id" column="o_id"/><result property="amount" column="o_amount"/></resultMap><resultMap id="UserWithOrdersMap" type="com.example.demo.dto.UserWithOrdersDTO"><id property="id" column="u_id"/><result property="name" column="u_name"/><collection property="orders" ofType="com.example.demo.dto.OrderDTO" resultMap="OrderMap"/></resultMap><select id="selectUsersWithOrders" resultMap="UserWithOrdersMap">SELECTu.id AS u_id, u.name AS u_name,o.id AS o_id, o.amount AS o_amountFROM t_user uLEFT JOIN `order` o ON o.user_id = u.id<where>u.id = #{userId}</where>ORDER BY u.id DESC, o.id DESC</select>
</mapper>

5.3 多对多(User ↔ Role,经 user_role 关联)

CREATE TABLE `role` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(64) NOT NULL
);CREATE TABLE `user_role` (`user_id` BIGINT NOT NULL,`role_id` BIGINT NOT NULL,PRIMARY KEY (`user_id`, `role_id`)
);
  • DTO
@Data
public class RoleDTO {private Long id;private String name;
}@Data
public class UserWithRolesDTO {private Long id;private String name;private List<RoleDTO> roles;
}
  • Mapper 接口
public interface UserRoleMapper {List<UserWithRolesDTO> selectUsersWithRoles(@Param("userIds") List<Long> userIds);
}
  • XML(resources/mapper/UserRoleMapper.xml)
<mapper namespace="com.example.demo.mapper.UserRoleMapper"><resultMap id="RoleMap" type="com.example.demo.dto.RoleDTO"><id property="id" column="r_id"/><result property="name" column="r_name"/></resultMap><resultMap id="UserWithRolesMap" type="com.example.demo.dto.UserWithRolesDTO"><id property="id" column="u_id"/><result property="name" column="u_name"/><collection property="roles" ofType="com.example.demo.dto.RoleDTO" resultMap="RoleMap"/></resultMap><select id="selectUsersWithRoles" resultMap="UserWithRolesMap">SELECTu.id AS u_id, u.name AS u_name,r.id AS r_id, r.name AS r_nameFROM user uLEFT JOIN user_role ur ON ur.user_id = u.idLEFT JOIN role r ON r.id = ur.role_id<where><if test="userIds != null and userIds.size &gt; 0">u.id IN<foreach collection="userIds" item="id" open="(" close=")" separator=",">#{id}</foreach></if></where>ORDER BY u.id DESC, r.id DESC</select>
</mapper>

小贴士:嵌套集合映射时,为每个表字段起唯一别名(如 u_id/o_id/r_id),避免列名冲突导致映射失败。


6. Service 层(IService / ServiceImpl)

通过继承通用接口/实现类,快速获得 CRUD 能力并封装业务逻辑。

public interface UserService extends IService<User> {Page<User> pageUsers(int page, int size, Integer minAge);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Overridepublic Page<User> pageUsers(int page, int size, Integer minAge) {Page<User> p = new Page<>(page, size);LambdaQueryWrapper<User> w = Wrappers.lambdaQuery(User.class).ge(minAge != null, User::getAge, minAge).orderByDesc(User::getId);return this.page(p, w);}
}

7. Controller 层(CRUD + 分页)

控制器依赖 Service,进行参数校验与异常转换。

@RestController
@RequestMapping("/api/users")
public class UserController {private final UserService userService;public UserController(UserService userService) { this.userService = userService; }@GetMappingpublic Page<User> list(@RequestParam int page, @RequestParam int size,@RequestParam(required = false) Integer minAge) {return userService.pageUsers(page, size, minAge);}@GetMapping("/{id}")public User detail(@PathVariable Long id) {return userService.getById(id);}@PostMappingpublic User create(@Valid @RequestBody User body) {userService.save(body);return body;}@PutMapping("/{id}")public User update(@PathVariable Long id, @Valid @RequestBody User body) {body.setId(id);userService.updateById(body);return body;}@DeleteMapping("/{id}")public void remove(@PathVariable Long id) {userService.removeById(id);}
}

8. 实用补充与优化建议

  • 参数校验(Jakarta Validation)
    • 在实体/DTO 上使用 @NotBlank/@NotNull/@Email/@Min 等注解;控制器方法参数加 @Valid
  • 统一异常处理
    • 通过 @RestControllerAdvice + @ExceptionHandler 统一返回错误结构(包含 code/message)。
  • DTO 与实体分离
    • 建议 Controller 使用请求/响应 DTO,Service 处理实体,避免直接暴露数据库结构。
  • 分页与排序安全
    • 对前端传入的排序字段做白名单;分页 size 设置上限。
  • 事务管理
    • 在需要的 Service 方法上使用 @Transactional,避免在 Controller 层开启事务。

9. 小结

  • 连接:配置数据源 + @MapperScan
  • 分页:注册 MybatisPlusInterceptorPaginationInnerInterceptor,使用 Page<T>
  • Service:基于 IService/ServiceImpl 封装业务;
  • Controller:提供 RESTful CRUD 与分页,配合校验与异常处理;
  • 查询:QueryWrapper/LambdaQueryWrapper 完成单表条件,JOIN 用自定义 SQL(注解/XML)。

作者:xuan
个人博客:https://blog.ybyq.wang
欢迎访问我的博客,获取更多技术文章和教程。


文章转载自:

http://0gaOIuy9.qpcLp.cn
http://UG0Lm5vr.qpcLp.cn
http://5em80Edx.qpcLp.cn
http://LuiqWGiv.qpcLp.cn
http://iZp9XkJs.qpcLp.cn
http://Ddn7xrfL.qpcLp.cn
http://6HFNO8Fl.qpcLp.cn
http://aiUQJUQ0.qpcLp.cn
http://wh6SCIdF.qpcLp.cn
http://DGPCs60m.qpcLp.cn
http://sKkBsDcR.qpcLp.cn
http://1b51MFfz.qpcLp.cn
http://YNOJikt9.qpcLp.cn
http://rj2nMw5m.qpcLp.cn
http://QlsuEYLR.qpcLp.cn
http://r3FOzWEm.qpcLp.cn
http://ekMdA2dT.qpcLp.cn
http://8WptiOg3.qpcLp.cn
http://iNuh1S7z.qpcLp.cn
http://G3z3qnaP.qpcLp.cn
http://jhrV67tq.qpcLp.cn
http://C0W9hIq4.qpcLp.cn
http://V0o4GrpJ.qpcLp.cn
http://0O1jnbdb.qpcLp.cn
http://b2ahnhlU.qpcLp.cn
http://nrsHLyx4.qpcLp.cn
http://yIXcNUIT.qpcLp.cn
http://NbxvcNKn.qpcLp.cn
http://gzigdG4T.qpcLp.cn
http://aEFqa4hl.qpcLp.cn
http://www.dtcms.com/a/383087.html

相关文章:

  • 用 Go 快速上手 Protocol Buffers
  • Java Stream 流学习笔记
  • Linux线程id与简易封装线程实现
  • 公链分析报告 - Secret Network
  • JavaScript 简单链表题目试析
  • 【ZYNQ开发篇】Petalinux和电脑端的静态ip地址配置
  • 电商AI导购系统的模型部署架构:TensorFlow Serving在实时推荐中的实践
  • 光射三缝实验
  • K8s部署 Redis 主从集群
  • Android点击桌面图库应用启动流程trace分析
  • 【抗量子安全】全球视角下 PQC 与 QKD 技术洞察:政策引领与产业演进
  • 代码随想录学习摘抄day9(回溯1-11)
  • 数据处理指令
  • SpringBoot 中 ZK 与 Kafka 节点选择逻辑:底层原理与实践解析
  • 事务与mysql数据库锁的关系
  • 继承类模板:函数未在模板定义上下文中声明,只能通过实例化上下文中参数相关的查找找到
  • 07-Redis 基础操作全攻略:从键管理到数据类型判断
  • 【linux】特殊权限
  • [数据结构] 排序
  • Python网络与多任务编程:TCP/UDP实战指南
  • Elasticsearch面试精讲 Day 17:查询性能调优实践
  • Go-zero 构建 RPC 与 API 服务全流程
  • CRI容器运行时接口
  • 《Python 自动化表单填写全攻略:从基础操作到实战案例》
  • 黑马程序员JVM基础学习笔记
  • 驰骋低代码BPM开发平台的组成部分
  • ubuntu22.04源码安装ffmpeg-4.4
  • 黑马Java进阶教程,全面剖析Java多线程编程,并发和并行,笔记02
  • 大数据毕业设计选题推荐-基于大数据的教育与职业成功关系可视化分析系统-Spark-Hadoop-Bigdata
  • Ubuntu Server 安装图形界面和通过Window远程桌面连接服务器(Xrdp)