商品管理 Demo

发布于 2026-07-30 20:15 更新于 2026-07-30 20:15 1159 字 6 min read ... 访问量

本文演示了基于Spring Boot、MyBatis-Plus、MySQL和Druid的商品与类别分页关联查询实现。通过逆向工程生成基础代码,配置分页插件,并在Mapper中实现商品表与类别表的关联查询,最终通过测试验证分页功能和关联字段正确性,同时指出常见问题如SQL映射错误、分页不生效及字段歧义等解决方案。

商品管理 Demo

本示例使用 Spring Boot、MyBatis-Plus、MySQL 和 Druid,实现商品与类别的分页关联查询。

一、准备数据库

创建 java2601 数据库,并导入 goods.sql 中的表结构和测试数据。示例至少需要以下两张表:

  • goods:商品表。
  • category:商品类别表。

商品表中的 cid 用于关联类别表的 cid

二、创建 Spring Boot 工程

引入依赖

pom.xml 中加入以下依赖。版本号可根据课程项目的 Spring Boot 版本统一管理。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
        <version>3.5.6</version>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-3-starter</artifactId>
        <version>1.2.23</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

配置数据源和 MyBatis-Plus

application.yml 中配置数据库连接和 MyBatis-Plus

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      url: jdbc:mysql://localhost:3306/java2601?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      initial-size: 2
      min-idle: 3
      max-active: 10
      max-wait: 10000

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  type-aliases-package: com.hyxy.goods.po
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto
      # table-prefix: tb_
      # logic-delete-field: deleteFlag
      # logic-delete-value: 1
      # logic-not-delete-value: 0

开发环境可以直接写用户名和密码,实际项目应通过环境变量或外部配置管理数据库凭据。

创建目录结构

建议按照控制器、业务层、数据访问层和实体类分包。

image-001
image-001

使用逆向工程生成基础代码

可以使用 MyBatis-Plus 代码生成器生成实体类、Mapper、Service 和对应 XML 文件。

image-002
image-002

生成后需要检查包名、表名、主键策略以及生成文件的保存位置,不能直接假定所有配置都符合当前项目。

三、扫描 Mapper 接口

在启动类上使用 @MapperScan,统一扫描 Mapper 接口:

package com.hyxy.goods;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.hyxy.goods.mapper")
public class GoodsApplication {

    public static void main(String[] args) {
        SpringApplication.run(GoodsApplication.class, args);
    }
}

使用 @MapperScan 后,通常不需要再在每个 Mapper 接口上添加 @Mapper。两种方式选择一种即可。

package com.hyxy.goods.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hyxy.goods.po.Category;

public interface CategoryMapper extends BaseMapper<Category> {
}
package com.hyxy.goods.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hyxy.goods.po.Goods;

public interface GoodsMapper extends BaseMapper<Goods> {
}

四、配置分页插件

config 包中创建配置类,并注册 MyBatis-Plus 分页拦截器:

package com.hyxy.goods.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 MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

本课件使用 MyBatis-Plus 3.5.6。若升级到 3.5.9 或更高版本,分页插件相关解析器已拆分为可选依赖,需要按照对应版本的官方说明补充模块。

五、实现商品与类别的关联查询

查询商品时,同时显示所属类别名称。

在商品实体类中添加非表字段

cname 来自关联查询,不是 goods 表中的真实字段,因此需要使用 @TableField(exist = false)

package com.hyxy.goods.po;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

@Data
public class Goods {

    // 省略 goods 表中的其他字段

    @TableField(exist = false)
    private String cname;
}

在 Mapper 接口中声明查询方法

分页参数应放在方法参数列表前面,查询条件使用 @Param 显式命名:

package com.hyxy.goods.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyxy.goods.po.Goods;
import org.apache.ibatis.annotations.Param;

public interface GoodsMapper extends BaseMapper<Goods> {

    IPage<Goods> selectJoinCategory(
            Page<Goods> page,
            @Param("gname") String gname,
            @Param("cid") Integer cid
    );
}

编写 Mapper XML

resources/mapper 目录下创建 GoodsMapper.xmlnamespace 必须与 Mapper 接口的全限定类名一致。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hyxy.goods.mapper.GoodsMapper">

    <select id="selectJoinCategory" resultType="Goods">
        SELECT
            g.gid,
            g.gname,
            g.price,
            g.stock,
            g.cid,
            g.sales,
            g.onsale,
            g.createtime,
            c.cname AS cname
        FROM goods AS g
        LEFT JOIN category AS c ON g.cid = c.cid
        <where>
            <if test="gname != null and gname != ''">
                AND g.gname LIKE CONCAT('%', #{gname}, '%')
            </if>
            <if test="cid != null">
                AND g.cid = #{cid}
            </if>
        </where>
        ORDER BY g.gid DESC
    </select>

</mapper>

关联查询中的列应尽量添加表别名,避免两张表存在同名字段时产生歧义。

六、编写测试

使用 Spring Boot 测试类验证分页和关联查询结果:

package com.hyxy.goods;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyxy.goods.mapper.GoodsMapper;
import com.hyxy.goods.po.Goods;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class GoodsApplicationTests {

    @Resource
    private GoodsMapper goodsMapper;

    @Test
    void selectJoinCategory() {
        Page<Goods> page = new Page<>(1, 3);

        IPage<Goods> pageResult = goodsMapper.selectJoinCategory(
                page,
                null,
                null
        );

        pageResult.getRecords().forEach(System.out::println);
        System.out.println("总记录数:" + pageResult.getTotal());
        System.out.println("总页数:" + pageResult.getPages());
    }
}

测试时应确认控制台生成了带分页限制的 SQL,并检查每个商品对象中的 cname 是否正确映射。

七、常见问题

Mapper 方法找不到对应 SQL

检查以下内容:

  • XML 的 namespace 是否与 Mapper 接口全限定名一致。
  • <select>id 是否与接口方法名一致。
  • mapper-locations 是否覆盖 XML 文件所在目录。

分页不生效

确认已经注册 MybatisPlusInterceptorPaginationInnerInterceptor,并保证分页参数位于 Mapper 方法参数列表前面。

类别编号条件报错或查询结果异常

在多表查询中使用 g.cid,不要直接写没有表别名的 cid,否则可能出现字段歧义。

喜欢的话,留下你的评论吧~

... 访问量
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka