分类管理

一、 整体认识:这是个什么文件?

这是一个 Spring Boot 后端项目中的“控制器(Controller)”

  • 作用:它就像一个“前台接待员”,专门负责接收浏览器/前端发来的请求(比如“给我看看商品分类列表”、“我要新增一个分类”),然后调用后台的“业务员(Service)”去干活,最后把结果返回给前端。
  • 管理内容:这个类主要管两件事:商品分类(比如“手机”、“电脑”这种分类)和 商品属性(比如“颜色”、“尺寸”这种规格)。

三、 类的定义:给“接待员”挂牌

1
2
3
4
5
6
/**
* Controller
*/
@RestController("sysCategoryController")
@RequestMapping("/sysCategory")
public class SysCategoryController extends ABaseController {
  1. @RestController

    • 它告诉 Spring Boot:“这是一个控制器类,而且我返回的数据直接是 JSON 格式(给前端 App 或网页用的),不是跳转页面。”
    • 括号里的 "sysCategoryController" 是给这个类起个名字,方便 Spring 管理。
  2. @RequestMapping("/sysCategory")
    定义 URL 前缀。

    • 意思是这个类里所有的接口地址,都必须以 /sysCategory 开头。
    • 比如后面有个方法叫 loadCategory,那么完整的访问地址就是:http://域名/sysCategory/loadCategory
  3. extends ABaseController
    继承了一个基类 ABaseController

    • 为什么要继承? 基类里通常写了一些通用的方法(比如后面代码里的 getSuccessResponseVO),这样大家都能用,不用重复写代码。

四、 依赖注入:找“业务员”帮忙

1
2
3
4
5
@Resource
private SysCategoryService sysCategoryService;

@Resource
private SysProductPropertyService sysProductPropertyService;
  1. @Resource
    这是“注入”注解。

    • 就像告诉 Spring:“我这个接待员需要两个业务员帮忙,一个管分类,一个管属性,你帮我把他们叫过来。”
    • Spring 会自动创建好这两个 Service 对象,赋值给下面的变量。
  2. SysCategoryService
    分类“业务员”。具体的增删改查逻辑(比如怎么存数据库、怎么判断分类有没有重复)都写在这个 Service 里,Controller 不操心这些细节。

五、 方法详解:具体的接待业务

1. 查询分类列表 (loadCategory)

这是最复杂的一个方法,用来加载商品分类树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 根据条件分页查询
*/
@RequestMapping("/loadCategory")
public ResponseVO loadCategory(Boolean queryProperty) {
SysCategoryQuery sysCategoryQuery = new SysCategoryQuery();
sysCategoryQuery.setOrderBy("s.sort asc");
if (queryProperty != null && queryProperty) {
sysCategoryQuery.setOrderBy("s.sort asc,sp.property_sort asc");
}
sysCategoryQuery.setQueryProperty(queryProperty);
sysCategoryQuery.setConvert2Tree(true);
return getSuccessResponseVO(sysCategoryService.findListByParam(sysCategoryQuery));
}
  • @RequestMapping("/loadCategory")
    映射 URL,访问这个方法的地址是 /sysCategory/loadCategory

    1. setConvert2Tree(true)
      关键点:设置为 true 表示要把结果转成树形结构
      • 因为分类是有层级的(比如:电子产品 -> 手机 -> 智能手机)。这个设置会让 Service 把平级的数据拼成“父子嵌套”的树状结构返回给前端。

4. 调整分类排序 (changeCategorySort)

1
2
3
4
5
@RequestMapping("/changeCategorySort")
public ResponseVO changeCategorySort(String categoryIds) {
sysCategoryService.changeSort(categoryIds);
return getSuccessResponseVO(null);
}
  • 参数 String categoryIds
    通常是一串用逗号分隔的 ID(例如 "1,3,2,5")。
    • 前端在页面上拖拽分类调整顺序后,把新顺序的 ID 列表传过来。
  • 逻辑
    Service 收到这个 ID 列表,按顺序重新设置每个分类的排序号。

六、 总结:这个类的套路

你会发现这个类里的所有方法都遵循一个标准套路

  1. 接收请求:通过 @RequestMapping 接收 URL 请求。
  2. 接收参数:要么是简单的字符串,要么是封装好的对象。
  3. 调用 ServiceController 自己从不碰数据库,全是调用 Service 层去做具体的事。
  4. 返回结果:调用 getSuccessResponseVO 返回统一的 JSON 格式。

这种写法叫分层架构,代码会非常干净,好维护。

数据库知识点

这是一个非常标准且完整的 MyBatis Mapper XML 映射文件,涵盖了 MyBatis 开发中最核心的实战知识点。我帮你分类梳理如下:

一、 MyBatis 基础配置与约束

这是文件的“门面”,定义了文件的身份和规矩。

1. DOCTYPE 文档约束

1
2
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  • 作用:这是 MyBatis 的文档类型定义(DTD)
  • 效果:告诉 IDE(如 IntelliJ IDEA)和 MyBatis 框架,“这是一个合法的 Mapper 文件”,IDE 会根据这个文件给你提供语法提示报错检查

2. Mapper 根标签与命名空间

1
<mapper namespace="com.easymall.mappers.SysCategoryMapper">
  • namespace (命名空间)非常重要
  • 作用:将这个 XML 文件和对应的 Java 接口(SysCategoryMapper绑定在一起。
  • 原理:接口里的方法名(如 selectList)会对应 XML 里的 SQL 标签 ID(如 <select id="selectList">)。

二、 结果映射 (resultMap) —— 核心难点

这是 MyBatis 最强大的功能之一,解决“数据库数据怎么传给 Java 对象”的问题。

1. 基础字段映射

1
2
3
4
5
<resultMap id="base_result_map" type="com.easymall.entity.po.SysCategory">
<result column="category_id" property="categoryId"/>
<result column="category_name" property="categoryName"/>
...
</resultMap>
  • 作用:做“翻译”。
  • column:数据库表里的列名(带下划线,如 category_id)。
  • property:Java 对象里的属性名(驼峰命名,如 categoryId)。
  • 解决的问题:数据库命名风格和 Java 不一样,MyBatis 通过这个映射自动帮你赋值。

2. 一对多关联映射 (<collection>)

1
2
3
<collection property="productPropertyList" ofType="com.easymall.entity.po.SysProductProperty">
...
</collection>
  • 场景:一个分类(Category)下面有多个商品属性(Property)。
  • property:Java 对象里用来装子对象列表的属性名(productPropertyList)。
  • ofType:列表里装的具体是什么类型的对象。
  • 效果:执行关联查询后,MyBatis 会自动把查出来的属性数据,塞进分类对象的 List 里,形成嵌套结构

三、 SQL 片段复用 (<sql><include>)

这是避免写重复代码的利器。

1
2
3
4
5
6
7
8
9
<!-- 定义 -->
<sql id="base_column_list">
s.category_id,s.category_name,s.p_category_id,s.sort
</sql>

<!-- 使用 -->
<select ...>
SELECT <include refid="base_column_list"/> FROM ...
</select>
  • <sql id="...">:定义一段公共的 SQL 代码(比如常用的列名、常用的条件)。
  • <include refid="..."/>:在需要的地方把定义好的代码片段复制粘贴过来。
  • 好处:如果列名变了,只需要改 <sql> 里的一处,所有引用的地方都自动生效,好维护

四、 动态 SQL —— MyBatis 最灵动的部分

动态 SQL 就是“根据条件智能拼接 SQL 语句”,主要靠以下标签实现:

1. <if> 标签:条件判断

1
2
3
<if test="query.categoryId != null and query.categoryId!=''">
and s.category_id = #{query.categoryId}
</if>
  • 作用:如果满足条件,就拼接这段 SQL;不满足就不拼。
  • test:写判断表达式(使用 OGNL 表达式语法)。
  • 场景:用户在页面上“只输入了分类名,没输入ID”,那 SQL 里就不要加 ID 的查询条件。

2. <where> 标签:智能处理 AND/OR

1
2
3
4
<where>
<include refid="base_condition_filed"/>
...
</where>
  • 作用
    1. 只有当 <where> 里面有条件时,才会在 SQL 里加上 WHERE 关键字。
    2. 自动去掉多余的 ANDOR
    • 痛点解决:如果所有条件都没拼上,或者第一个条件前面多了个 AND,SQL 会报错,<where> 完美解决这个问题。

3. <trim> 标签:自定义截取(高级用法)

1
2
3
<trim prefix="(" suffix=")" suffixOverrides=",">
...
</trim>
  • 作用:更灵活的字符串处理。
  • 属性
    • prefix:在内容前面加个前缀(比如 ()。
    • suffix:在内容后面加个后缀(比如 ))。
    • suffixOverrides:去掉内容最后面多余的字符(比如逗号 ,)。
  • 场景:在 INSERT 语句中,动态拼字段列表时,最后一个字段后面不能有逗号,用 suffixOverrides="," 自动去掉。

4. <foreach> 标签:循环遍历(批量操作神器)

1
2
3
<foreach collection="list" item="item" separator=",">
(#{item.categoryId}, ...)
</foreach>
  • 作用:遍历一个 Java 集合(List/Set/Array),生成 SQL 片段。
  • 属性
    • collection:要遍历的集合参数名(通常是 list)。
    • item:当前遍历到的那个元素变量名。
    • separator:每次循环之间用什么分隔(批量插入用逗号 ,,批量更新用分号 ;)。
  • 场景批量插入批量更新IN 查询(如 WHERE id IN (1,2,3))。

五、 CRUD 操作标签

这是最基础的数据库操作:

  1. <select>:查询。
    • resultMap:引用上面定义的映射规则。
    • resultType:直接指定返回类型(如 IntegerString 或简单的 POJO)。
  2. <insert>:插入。
  3. <update>:更新。
  4. <delete>:删除。

六、 参数传递的两种方式 (#{} vs ${})

这是面试高频考点,也是安全关键点。

1. #{} (推荐使用,安全)

1
and s.category_id = #{query.categoryId}
  • 本质预编译(PreparedStatement)
  • 效果:MyBatis 会把它变成 ? 占位符,然后安全地设置参数。
  • 优点防止 SQL 注入,安全性高。

2. ${} (慎用,有风险)

1
order by ${query.orderBy}
  • 本质字符串直接替换
  • 效果:直接把参数值拼接到 SQL 语句里。
  • 缺点有 SQL 注入风险(如果参数是恶意代码,会被执行)。
  • 为什么这里还要用?
    • 因为 ORDER BY 后面跟的是列名或关键字(如 sort asc),这些不能用 ? 占位符,所以只能用 ${}
    • 注意:使用 ${} 时,一定要在代码里对参数做严格校验,防止被攻击。

七、 数据库特有语法与函数

1. ON DUPLICATE KEY UPDATE (MySQL 特有)

1
2
INSERT INTO sys_category (...) VALUES (...)
on DUPLICATE key update ...
  • 作用:“如果存在就更新,不存在就插入”。
  • 场景:通常用于“保存/修改”二合一的功能,或者批量同步数据。

2. IFNULL 函数

1
select ifnull(max(sort), 0) from ...
  • 作用:如果 max(sort) 查出来是 NULL(表里还没有数据),就返回 0
  • 场景:在计算最大排序号时使用,防止空指针异常。

总结

这个 XML 文件就是 MyBatis 的“灵魂”所在:

  1. resultMap 解决数据映射问题。
  2. 动态 SQL (if, where, trim, foreach) 解决 SQL 拼接的麻烦。
  3. #{} 和 ${} 解决参数安全问题。
  4. collection 解决关联查询(一对多)的嵌套问题。