java每日精进 5.20【MyBatis 联表&分页查询】

1. MyBatis XML 实现分页查询

1.1 实现方式

MyBatis XML 是一种传统的 MyBatis 使用方式,通过在 XML 文件中编写 SQL 语句,并结合 Mapper 接口和 Service 层实现分页查询。分页需要手动编写两条 SQL 语句:一条查询分页数据列表,另一条查询总记录数。分页参数(如页码和每页大小)通过 LIMIT 语句手动实现。

1.2 代码解析

以下是文档中提供的 MyBatis XML 分页查询代码的解析:

1.2.1 xml代码

sql 复制代码
// 获取所有用户(分页,支持用户名模糊查询)
    @GetMapping("/xmluserpage")
    public IPage<User> getUsersByPage(@RequestParam(defaultValue = "1") int page,
                                      @RequestParam(defaultValue = "10") int size,
                                      @RequestParam(required = false) String username) {
        return userService.getUserPage(page, size, username);
    }
java 复制代码
@Override
    public IPage<User> getUserPage(int page, int size, String username) {
        Page<User> userPage = new Page<>(page, size);
        return userMapper.selectPageCustom(userPage, username);
    }
java 复制代码
// 自定义分页查询(单表)
    IPage<User> selectPageCustom(Page<User> page, @Param("username") String username);
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.home.mapper.UserMapper">

    <!-- 自定义分页查询 -->
    <select id="selectPageCustom" resultType="com.home.pojo.User">
        SELECT id, username, password, email, create_time, update_time
        FROM user
        <where>
            <if test="username != null and username != ''">
                AND username LIKE CONCAT('%', #{username}, '%')
            </if>
        </where>
    </select>

</mapper>

2. MyBatis XML 联表查询

2.1背景代码

html 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>CRUDSystem</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging> <!-- 建议改为jar,Spring Boot默认打包为可执行jar -->
  <name>CRUDSystem</name>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <spring-boot.version>2.7.18</spring-boot.version>
    <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
    <mybatis-plus-join.version>1.4.10</mybatis-plus-join.version>
  </properties>

  <dependencies>
    <!-- MyBatis Plus -->
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>${mybatis-plus.version}</version>
    </dependency>
    <!-- MyBatis Plus Join -->
    <dependency>
      <groupId>com.github.yulichang</groupId>
      <artifactId>mybatis-plus-join-boot-starter</artifactId>
      <version>${mybatis-plus-join.version}</version>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-extension</artifactId>
      <version>${mybatis-plus.version}</version>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>

    <!-- Spring Boot 核心 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>${spring-boot.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>${spring-boot.version}</version>
    </dependency>

    <!-- Lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.28</version>
    </dependency>

    <!-- 测试依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <version>${spring-boot.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>CRUDSystem</finalName>
    <plugins>
      <!-- Spring Boot 打包插件 -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring-boot.version}</version>
        <configuration>
          <excludes>
            <exclude>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
            </exclude>
          </excludes>
        </configuration>
      </plugin>

      <!-- 编译插件 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>17</source>
          <target>17</target>
          <release>17</release>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
java 复制代码
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String username;
    private String password;
    private String email;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
@Data
@TableName("`order`")
public class Order {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Long productId;
    private Integer quantity;
    private BigDecimal totalPrice;
    private Integer status;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
//字段平铺
@Data
public class UserOrderDetailDO extends User {
    private Long orderId;
    private BigDecimal totalPrice;
    private Integer orderStatus;
}
//字段内嵌
@Data
public class UserOrderDetail2DO extends User {
    private Order order;
}

2.2字段平铺

java 复制代码
// 字段平铺:获取用户订单详情(分页,订单状态和用户名过滤)
    @GetMapping("/orders")
    public IPage<UserOrderDetailDO> getUserOrders(@RequestParam(defaultValue = "1") int page,
                                                  @RequestParam(defaultValue = "10") int size,
                                                  @RequestParam Integer status,
                                                  @RequestParam(required = false) String username) {
        return userService.getUserOrderPage(page, size, status, username);
    }
    @Override
    public IPage<UserOrderDetailDO> getUserOrderPage(int page, int size, Integer status, String username) {
        Page<UserOrderDetailDO> userOrderPage = new Page<>(page, size);
        return userMapper.selectListByStatusAndUsername(userOrderPage, status, username);
    }
// 字段平铺:联表查询用户和订单(状态为已支付,用户名模糊匹配)
    default IPage<UserOrderDetailDO> selectListByStatusAndUsername(Page<UserOrderDetailDO> page,
                                                                   @Param("status") Integer status,
                                                                   @Param("username") String username) {
        return selectJoinPage(page, UserOrderDetailDO.class, new MPJLambdaWrapper<User>() // 改为 selectJoinPage
                .selectAll(User.class)
                .selectAs(Order::getId, UserOrderDetailDO::getOrderId)
                .selectAs(Order::getTotalPrice, UserOrderDetailDO::getTotalPrice)
                .selectAs(Order::getStatus, UserOrderDetailDO::getOrderStatus)
                .eq(Order::getStatus, status)
                .leftJoin(Order.class, Order::getUserId, User::getId)
                .like(username != null, User::getUsername, username));
    }

等价于以下SQL

SELECT

u.*,

o.id AS orderId,

o.total_price AS totalPrice,

o.status AS orderStatus

FROM

user u

LEFT JOIN

`order` o ON u.id = o.user_id -- 注意这里应该是 user_id 而不是 o.id

WHERE

o.status = 1

AND u.username LIKE '%username%'

查询结果:

javascript 复制代码
{
    "records": [
        {
            "id": 1,
            "username": "user1",
            "password": "password123",
            "email": "user1@example.com",
            "createTime": "2025-05-20T10:15:33",
            "updateTime": "2025-05-20T10:15:33",
            "orderId": 1,
            "totalPrice": 999.99,
            "orderStatus": 1
        }
    ],
    "total": 2,
    "size": 1,
    "current": 1,
    "orders": [],
    "optimizeCountSql": true,
    "searchCount": true,
    "maxLimit": null,
    "countId": null,
    "pages": 2
}

2.3字段嵌入

java 复制代码
// 字段内嵌:获取用户订单详情(分页,订单状态和用户名过滤)
    @GetMapping("/orders2")
    public IPage<UserOrderDetail2DO> getUserOrders2(@RequestParam(defaultValue = "1") int page,
                                                    @RequestParam(defaultValue = "10") int size,
                                                    @RequestParam Integer status,
                                                    @RequestParam(required = false) String username) {
        return userService.getUserOrderPage2(page, size, status, username);
    }
@Override
    public IPage<UserOrderDetail2DO> getUserOrderPage2(int page, int size, Integer status, String username) {
        Page<UserOrderDetail2DO> userOrderPage = new Page<>(page, size);
        return userMapper.selectList2ByStatusAndUsername(userOrderPage, status, username);
    }
// 字段内嵌:联表查询用户和订单(状态为已支付,用户名模糊匹配)
    default IPage<UserOrderDetail2DO> selectList2ByStatusAndUsername(Page<UserOrderDetail2DO> page,
                                                                     @Param("status") Integer status,
                                                                     @Param("username") String username) {
        return selectJoinPage(page, UserOrderDetail2DO.class, new MPJLambdaWrapper<User>() // 改为 selectJoinPage
                .selectAll(User.class)
                .selectAssociation(Order.class, UserOrderDetail2DO::getOrder)
                .eq(Order::getStatus, status)
                .leftJoin(Order.class, Order::getUserId, User::getId)
                .like(username != null, User::getUsername, username));
    }

等价SQL:

SELECT

u.*,

o.*

FROM

t_user u

LEFT JOIN

t_order o ON o.user_id = u.id

WHERE

o.status = #{status}

AND (#{username} IS NULL OR u.username LIKE CONCAT('%', #{username}, '%'))

LIMIT #{page.size} OFFSET #{page.offset}

返回值为:

javascript 复制代码
{
    "records": [
        {
            "id": 1,
            "username": "user1",
            "password": "password123",
            "email": "user1@example.com",
            "createTime": "2025-05-20T10:15:33",
            "updateTime": "2025-05-20T10:15:33",
            "order": {
                "id": 1,
                "userId": 1,
                "productId": 1,
                "quantity": 1,
                "totalPrice": 999.99,
                "status": 1,
                "createTime": "2025-05-20T10:16:00",
                "updateTime": "2025-05-20T10:16:00"
            }
        }
    ],
    "total": 2,
    "size": 1,
    "current": 1,
    "orders": [],
    "optimizeCountSql": true,
    "searchCount": true,
    "maxLimit": null,
    "countId": null,
    "pages": 2
}
相关推荐
四谎真好看1 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程1 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t1 小时前
ZIP工具类
java·zip
lang201509282 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan3 小时前
第10章 Maven
java·maven
百锦再3 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说3 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多3 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
百锦再3 小时前
对前后端分离与前后端不分离(通常指服务端渲染)的架构进行全方位的对比分析
java·开发语言·python·架构·eclipse·php·maven
DokiDoki之父4 小时前
Spring—注解开发
java·后端·spring