网站建设对企业的意义短视频素材下载网站无水印

张小明 2026/1/7 23:05:05
网站建设对企业的意义,短视频素材下载网站无水印,网站建设有什么证,机械产品做那几个网站好目录 MyBatis 进阶详解与图书管理系统实战 第一部分#xff1a;核心知识点深度解析 1. 什么是动态 SQL#xff1f;为什么需要它#xff1f; 2. 动态 SQL 标签详解#xff08;文档核心点扩展#xff09; 2.1 标签#xff1a;最常用的判断逻辑2.2 标签#xff1a;万能…目录MyBatis 进阶详解与图书管理系统实战第一部分核心知识点深度解析1. 什么是动态 SQL为什么需要它2. 动态 SQL 标签详解文档核心点扩展2.1 标签最常用的判断逻辑2.2 标签万能的“修剪工”2.3 标签智能的 WHERE 子句2.4标签用于 UPDATE 更新语句2.5 标签循环遍历3. 项目实战扩展知识点3.1 分页查询的原理 (Pagination)3.2 统一结果封装 (Result Wrapper)3.3 拦截器与强制登录 (Session Interceptor)第二部分完整代码识别与深度注释XML 映射文件详解MyBatis 核心学习建议与总结 背景知识✅ 场景说明✅ 使用标签的例子1. Mapper XML 文件中的写法✅ 执行结果示例情况一只修改了 name 和 email情况二只修改了 age情况三所有字段都不改全为空 总结的核心功能 小贴士✅ 补充为什么不用手动拼接 目标理解 的作用与属性✅ 典型场景批量删除用户 的基本语法✅ 实际例子批量删除用户1. Java 代码准备2. Mapper XML 写法3. 生成的 SQL 效果 各个属性详解带例子 collection要遍历的集合示例1参数是 List 类型示例2参数是 Map集合在 map 中示例3参数是 POJO集合是字段 item当前元素的别名 open 和 close控制括号示例IN 查询需要括号示例没有括号的情况比如批量插入 separator元素之间的分隔符示例1逗号分隔示例2AND 连接条件✅ 更复杂的例子批量插入Mapper XML生成的 SQL⚠️ 注意事项避坑指南✅ 总结 的核心功能 记忆口诀 目标理解“分页查询的扩展”——为什么需要 PageResult怎么用✅ 先回顾一下基础什么是分页 那么“扩展”到底是什么意思✅ 举个实际例子说明 假设数据库中有 25 条用户数据第一步执行 SQL 查询数据带 LIMIT第二步执行另一个 SQL 查询总数 然后我们把这两部分结果封装成一个对象PageResult✅ 如何计算这些值✅ 最终返回的 PageResult 对象内容 为什么要这么做重要❌ 如果不封装会发生什么✅ 封装后的好处✅ 实际开发中的流程图✅ 注意事项文档里说的“注意”为什么不能只查一次优化方案高级✅ 总结“扩展”到底是什么 记忆口诀 目标理解 Result 的作用与使用场景✅ 先看一个真实场景❌ 传统方式不推荐✅ 正确做法统一结果封装✅ 标准结构详解 举个完整例子用户登录接口1. 前端请求2. 后端处理逻辑3. 返回结果JSON✅ 情况一登录成功✅ 情况二密码错误✅ 情况三未登录参数为空✅ 如何实现 Result 类✅ 为什么需要统一封装重点✅ 实际项目中的效果前端代码示例JavaScript✅ 扩展常见状态码建议✅ 总结“统一结果封装”到底是什么 核心思想 记忆口诀 目标理解 Result 的作用与使用场景✅ 先看一个真实场景❌ 传统方式不推荐✅ 正确做法统一结果封装✅ 标准结构详解 举个完整例子用户登录接口1. 前端请求2. 后端处理逻辑3. 返回结果JSON✅ 情况一登录成功✅ 情况二密码错误✅ 情况三未登录参数为空✅ 如何实现 Result 类✅ 为什么需要统一封装重点✅ 实际项目中的效果前端代码示例JavaScript✅ 扩展常见状态码建议✅ 总结“统一结果封装”到底是什么 核心思想 记忆口诀这是一份非常详细的MyBatis 进阶与动态 SQL 实战教程。针对新手小白我会把每一个概念拆碎了讲配合文档中的案例补充大量的背景知识、底层原理以及代码实现的细节。这份教程不仅涵盖了文档内容还扩展了 Spring Boot 整合、RESTful 接口设计、分页原理等实际开发必备知识。MyBatis 进阶详解与图书管理系统实战第一部分核心知识点深度解析1. 什么是动态 SQL为什么需要它概念解释想象你在写 SQL 语句就像在“造句”。静态 SQL句子是死的比如“我要一个苹果”。无论发生什么你只要苹果。动态 SQL句子是活的根据情况变化。比如“如果有红苹果我就要红的如果没有我就要青的如果没钱我就不要了”。在编程中用户的搜索条件是千变万化的。用户可能只输入了名字也可能同时输入了名字和年龄。如果不使用动态 SQL你需要写无数个if-else在 Java 代码里拼接字符串这非常容易出错比如少个空格、多了个逗号而且容易导致 SQL 注入漏洞。MyBatis 动态 SQL提供了一套类似 HTML 标签的语法如if,where让你在 XML 中逻辑清晰地组装 SQLMyBatis 会自动帮你处理空格、逗号等繁琐细节。2. 动态 SQL 标签详解文档核心点扩展2.1if标签最常用的判断逻辑场景用户注册时性别gender是选填项。如果用户没填数据库就用默认值如果填了就插入用户填的值。代码逻辑分析XML!-- test 属性里面写的是 Java 对象的属性名不是数据库字段名 -- if testgender ! null gender, /if新手扩展知识test属性支持 OGNL 表达式。除了判空还可以判断字符串是否为空串name ! 或者数字大小age 18。陷阱在if中判断字符串相等时要小心单引号和双引号的嵌套。2.2trim标签万能的“修剪工”场景拼接 SQL 时最头疼的就是多余的逗号 , 或者多余的 AND。比如INSERT INTO user (name, age,) VALUES (Tom, 18,) —— 这里的结尾逗号会导致 SQL 报错。trim的四大属性prefix在整个内容前面加上什么比如加上(。suffix在整个内容后面加上什么比如加上)。suffixOverrides如果内容最后多出来了什么字符就把它去掉比如去掉,。prefixOverrides如果内容最前面多出来了什么字符就把它去掉比如去掉AND。2.3where标签智能的 WHERE 子句场景多条件查询。传统笨办法WHERE 11。为什么为了后面拼接AND时不出错。MyBatis 办法where标签。如果标签内没有内容所有 if 都不满足它就不会生成WHERE关键字。如果标签内有内容它会自动去掉开头多余的AND或OR。2.4set标签用于 UPDATE 更新语句场景只修改用户修改过的字段没修改的保持原样。它会自动插入SET关键字。它会自动去掉行尾多余的逗号。2.5foreach标签循环遍历场景批量删除DELETE FROM user WHERE id IN (1, 2, 3)。属性解析collection你要遍历的 Java 集合List, Set, Array。item当前遍历到的元素起个别名类似 Java foreach 中的变量名。open循环开始前加什么如(。close循环结束后加什么如)。separator元素之间用什么分隔如,。3. 项目实战扩展知识点3.1 分页查询的原理 (Pagination)文档中提到了LIMIT关键字。公式LIMIT (当前页码 - 1) * 每页条数, 每页条数举例每页显示 10 条。第 1 页LIMIT 0, 10从第0条开始取10条第 2 页LIMIT 10, 10从第10条开始取10条第 3 页LIMIT 20, 10扩展在实际企业开发中通常会封装一个PageResult对象包含ListT records当前页的数据列表。long total总条数用于计算总页数。注意分页通常需要执行两条 SQL一条查数据一条查COUNT(*)总数。3.2 统一结果封装 (Result Wrapper)文档中提到了ResultT类。为什么需要前后端分离开发时前端需要根据一个统一的状态码Status Code来判断请求是成功还是失败而不是去猜。标准结构code业务状态码200成功-1未登录500系统错误。msg提示信息操作成功 或 密码错误。data真正的数据比如查询到的用户对象。3.3 拦截器与强制登录 (Session Interceptor)文档通过HttpSession判断用户是否登录。原理HTTP 是无状态的。服务器通过 Session ID 识别这还是刚才那个用户。进阶在实际 Spring Boot 项目中通常不会在每个 Controller 方法里写if (session null)而是使用Spring Interceptor (拦截器)或AOP (切面)来统一处理登录校验。第二部分完整代码识别与深度注释以下代码基于文档中的“图书管理系统”进行重构和完善。为了让你完全理解我将代码分为Model (实体层)、Controller (控制层)、Service (业务层)、Mapper (持久层)四个部分并提供了完整的 XML 配置。BookManagementSystem.java/** * * 模块一实体类 Model (POJO) * 作用对应数据库中的表结构用于在各层之间传递数据。 * 使用了 Lombok 插件的 Data 注解自动生成 getter/setter/toString 等方法简化代码。 * */ package com.example.demo.model; import lombok.Data; import java.math.BigDecimal; import java.util.Date; // 图书实体类对应数据库表 book_info Data public class BookInfo { // 图书ID主键 private Integer id; // 书名 private String bookName; // 作者 private String author; // 库存数量 private Integer count; // 价格 (涉及金钱通常使用 BigDecimal 避免精度丢失但文档使用了 Decimal/Double此处保持兼容) private BigDecimal price; // 出版社 private String publish; // 状态1-可借阅, 2-不可借阅, 0-已删除(逻辑删除) private Integer status; // 状态的中文描述不存数据库只用于前端展示 private String statusCN; // 创建时间 private Date createTime; // 更新时间 private Date updateTime; } // 分页请求参数类接收前端传来的页码和每页大小 Data public class PageRequest { // 当前页码默认第1页 private int currentPage 1; // 每页显示数量默认10条 private int pageSize 10; // 计算数据库查询的偏移量 (Offset) // MyBatis 查询时使用LIMIT offset, pageSize public int getOffset() { return (currentPage - 1) * pageSize; } } // 分页结果响应类返回给前端的标准分页数据结构 Data public class PageResultT { // 数据库中的总记录数用于前端计算有多少页 private int total; // 当前页的数据列表 private java.util.ListT records; // 回传请求的分页参数方便前端核对 private PageRequest pageRequest; // 构造函数 public PageResult(Integer total, PageRequest pageRequest, java.util.ListT records) { this.total total; this.pageRequest pageRequest; this.records records; } } /** * * 模块二持久层 Mapper Interface * 作用定义访问数据库的接口。MyBatis 会根据 XML 或注解自动生成实现类。 * */ package com.example.demo.mapper; import com.example.demo.model.BookInfo; import com.example.demo.model.PageRequest; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Insert; import java.util.List; Mapper // 标识这是一个 MyBatis 的 Mapper 接口Spring 启动时会扫描并创建 Bean public interface BookInfoMapper { /** * 查询有效图书的总数量 * SQL解释统计 status 不为 0 (0代表被逻辑删除了) 的记录数 * 应用场景分页查询时先要知道总共有多少条数据才能计算总页数 */ Select(select count(1) from book_info where status 0) Integer count(); /** * 分页查询图书列表 * SQL解释 * 1. where status ! 0: 过滤掉已删除的图书 * 2. order by id desc: 按 ID 倒序排列新添加的书在最前面 * 3. limit #{offset}, #{pageSize}: 分页的核心从 offset 开始取 pageSize 条 * 注意#{offset} 会从 PageRequest 对象中调用 getOffset() 方法获取 */ Select(select * from book_info where status ! 0 order by id desc limit #{offset}, #{pageSize}) ListBookInfo queryBookListByPage(PageRequest pageRequest); /** * 添加图书 * 使用 Insert 注解 * #{xxx} 对应 BookInfo 对象中的属性名 */ Insert(insert into book_info (book_name, author, count, price, publish, status) values (#{bookName}, #{author}, #{count}, #{price}, #{publish}, #{status})) Integer insertBook(BookInfo bookInfo); /** * 根据 ID 查询单本图书详情 * 用于“修改图书”页面回显数据 */ Select(select * from book_info where id #{bookId} and status 0) BookInfo queryBookById(Integer bookId); /** * 修改图书信息动态 SQL * 这里的实现比较复杂通常不在注解里写而是配合 XML 文件使用。 * 请看下文的 XML 部分。 */ Integer updateBook(BookInfo bookInfo); /** * 批量删除图书 * 接收一个 ID 列表将这些书的状态置为 0 */ void batchDeleteBook(ListInteger ids); } /** * * 模块三业务层 Service * 作用处理业务逻辑如状态转换、事务控制。它是 Controller 和 Mapper 的中间层。 * */ package com.example.demo.service; import com.example.demo.mapper.BookInfoMapper; import com.example.demo.model.BookInfo; import com.example.demo.model.PageRequest; import com.example.demo.model.PageResult; import com.example.demo.enums.BookStatus; // 假设有一个枚举类定义状态 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; Service // 标识这是一个业务层组件交给 Spring 管理 public class BookService { Autowired // 注入 Mapper 接口MyBatis 已经帮我们生成了代理对象 private BookInfoMapper bookInfoMapper; /** * 获取分页图书列表 * 业务逻辑 * 1. 查询总数。 * 2. 查询当前页数据。 * 3. 遍历数据将数字状态 (1, 2) 转换为中文 (可借阅, 不可借阅)方便前端显示。 */ public PageResultBookInfo getBookListByPage(PageRequest pageRequest) { // 1. 获取总记录数 Integer count bookInfoMapper.count(); // 2. 获取当前页的数据列表 ListBookInfo books bookInfoMapper.queryBookListByPage(pageRequest); // 3. 处理状态显示的业务逻辑 for (BookInfo book : books) { // 这里使用了枚举工具类将 1 - 可借阅 (文档中提及的逻辑) // 假设 BookStatus.getNameByCode 是一个静态方法 // 如果没有枚举可以使用简单的 if-else 替代 if (book.getStatus() 1) { book.setStatusCN(可借阅); } else if (book.getStatus() 2) { book.setStatusCN(不可借阅); } else { book.setStatusCN(无效); } } // 4. 封装结果返回 return new PageResult(count, pageRequest, books); } // 添加图书业务 public Integer addBook(BookInfo bookInfo) { return bookInfoMapper.insertBook(bookInfo); } // 根据ID查询业务 public BookInfo queryBookById(Integer bookId) { return bookInfoMapper.queryBookById(bookId); } // 更新图书业务 public Integer updateBook(BookInfo bookInfo) { return bookInfoMapper.updateBook(bookInfo); } // 批量删除业务 public void batchDeleteBook(ListInteger ids) { bookInfoMapper.batchDeleteBook(ids); } } /** * * 模块四控制层 Controller * 作用接收 HTTP 请求解析参数调用 Service返回 JSON 数据。 * */ package com.example.demo.controller; import com.example.demo.model.*; import com.example.demo.service.BookService; import com.example.demo.common.Result; // 假设有一个统一返回结果类 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpSession; import java.util.List; RestController // 组合注解Controller ResponseBody表示返回的都是数据而非页面 RequestMapping(/book) // 统一定义接口前缀如 /book/addBook public class BookController { Autowired private BookService bookService; // 简单的日志打印实际开发推荐使用 Slf4j // Logger log LoggerFactory.getLogger(BookController.class); /** * 获取图书列表接口 * 请求路径/book/getListByPage?currentPage1pageSize10 */ RequestMapping(/getListByPage) public Result getListByPage(PageRequest pageRequest, HttpSession session) { // 1. 登录校验 (强制登录逻辑) // Constants.SESSION_USER_KEY 是一个常量字符串 if (session.getAttribute(session_user_key) null) { // 用户未登录返回特定的未登录状态码 return Result.unlogin(); } // 2. 调用业务层获取数据 PageResultBookInfo pageResult bookService.getBookListByPage(pageRequest); // 3. 封装统一格式返回成功数据 return Result.success(pageResult); } /** * 添加图书接口 * 请求路径/book/addBook (POST) * 参数自动封装进 BookInfo 对象 */ RequestMapping(/addBook) public String addBook(BookInfo bookInfo) { // 1. 参数校验 (防止空指针和脏数据) if (!StringUtils.hasLength(bookInfo.getBookName()) || !StringUtils.hasLength(bookInfo.getAuthor()) || bookInfo.getCount() null || bookInfo.getPrice() null) { return 输入参数不合法请检查入参!; } try { // 2. 调用服务添加 bookService.addBook(bookInfo); return ; // 按照文档约定返回空字符串代表成功 } catch (Exception e) { // log.error(添加失败, e); return 添加失败: e.getMessage(); } } /** * 更新图书接口 * 包含“逻辑删除”功能前端传 status0 即可 */ RequestMapping(/updateBook) public String updateBook(BookInfo bookInfo) { try { bookService.updateBook(bookInfo); return ; } catch (Exception e) { return e.getMessage(); } } /** * 批量删除接口 * 参数ids1,2,3 (Spring MVC 自动将逗号分隔字符串转为 List) */ RequestMapping(/batchDeleteBook) public boolean batchDeleteBook(RequestParam ListInteger ids) { try { bookService.batchDeleteBook(ids); return true; } catch (Exception e) { return false; } } // 根据ID查询图书用于修改页面的回显 RequestMapping(/queryBookById) public BookInfo queryBookById(Integer bookId) { if (bookId null || bookId 0) return new BookInfo(); return bookService.queryBookById(bookId); } }XML 映射文件详解MyBatis 核心上面 Java 代码中的updateBook和batchDeleteBook方法比较复杂通常配合 XML 文件编写动态 SQL。BookInfoMapper.xml?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd !-- namespace 必须对应 Mapper 接口的全限定名 -- mapper namespacecom.example.demo.mapper.BookInfoMapper !-- 动态更新 SQL 对应接口Integer updateBook(BookInfo bookInfo); 知识点解析 1. set 标签自动添加 SET 关键字并去除最后多余的逗号。 2. if test...判断传入对象的属性是否为空。如果不为空才更新该字段。 这样设计的好处是我们可以重用这个方法实现“只修改价格”、“只修改状态”或“修改全部信息”。 -- update idupdateBook update book_info set if testbookName ! null book_name #{bookName}, /if if testauthor ! null author #{author}, /if if testcount ! null count #{count}, /if if testprice ! null price #{price}, /if if testpublish ! null publish #{publish}, /if if teststatus ! null status #{status}, /if /set where id #{id} /update !-- 批量删除 SQL (实际上是批量更新状态为0) 对应接口void batchDeleteBook(ListInteger ids); SQL 预览update book_info set status0 where id in (1, 2, 3) 知识点解析 1. foreach用于遍历集合。 2. collectionids对应接口参数名Spring Boot MyBatis Starter 默认参数名为 list 或 collection或者使用 Param(ids) 指定。 3. itemid遍历时的临时变量名。 4. open( close) separator,智能生成 (1, 2, 3) 这种格式。 -- update idbatchDeleteBook update book_info set status 0 where id in foreach collectionids itemid separator, open( close) #{id} /foreach /update /mapper学习建议与总结对于新手小白阅读完这份代码后建议按照以下步骤练习理解分层架构明白 Controller 负责接收请求Service 负责业务逻辑Mapper 负责和数据库说话。不要把 SQL 语句写在 Controller 里。多写动态 SQL手动敲一遍 XML 中的if和foreach标签故意写错一点比如多加个逗号看看 MyBatis 的报错这样印象最深。调试分页启动项目在浏览器输入不同的currentPage参数观察控制台打印出的 SQL 语句中LIMIT后面的数字是如何变化的。安全意识始终记住使用#{}也就是预编译参数防止 SQL 注入。虽然文档中没有详细展开$符号但新手要尽量避免使用${}。这份代码和解释基本涵盖了 MyBatis 进阶操作的核心内容如果能完全看懂并跑通说明你已经跨过了 MyBatis 的新手门槛我们来详细解释一下set标签在 MyBatis 中的作用并通过一个具体的例子帮助你理解。 背景知识在 MyBatis 的动态 SQL 中set标签专门用于UPDATE 语句。它的作用是自动插入SET关键字。自动去掉行尾多余的逗号,。这解决了我们在写动态更新语句时常见的两个问题如果某些字段没被修改我们不想把它们加进去但又不能让 SQL 最后多出一个逗号比如SET name张三, age25,❌✅ 场景说明假设有一个用户表user包含字段id, name, email, age现在我们要做一个“只更新用户修改过的字段未修改的保持不变”的功能。例如用户只改了name和email那么age不变但我们希望生成的 SQL 是UPDATE user SET name 李四, email lisiexample.com WHERE id 1;而不是错误地写成UPDATE user SET name 李四, email lisiexample.com, WHERE id 1; -- 多了个逗号✅ 使用set标签的例子1. Mapper XML 文件中的写法update idupdateUser parameterTypeUser UPDATE user set if testname ! null and name ! name #{name}, /if if testemail ! null and email ! email #{email}, /if if testage ! null age #{age} /if /set WHERE id #{id} /update注意每个if条件里我们都写了,包括最后一个字段也写了但这没关系 ——set会自动处理掉多余的逗号✅ 执行结果示例情况一只修改了 name 和 email传入参数User user new User(); user.setId(1); user.setName(李四); user.setEmail(lisiexample.com); // age 没设置null生成的 SQLUPDATE user SET name 李四, email lisiexample.com WHERE id 1✅ 正确没有多余逗号也没有无效字段。情况二只修改了 age传入参数user.setAge(30);生成的 SQLUPDATE user SET age 30 WHERE id 1✅ 只有 age 字段被更新。情况三所有字段都不改全为空生成的 SQLUPDATE user SET WHERE id 1❌ 这样会报错⚠️ 所以通常我们会加个判断确保至少有一个字段要改或者用其他方式避免空 SET。 总结set的核心功能功能说明✅ 自动添加SET即使你没写SET它也会帮你加上✅ 自动去掉末尾逗号即使你在每个条件后面都加了,它也会自动删掉最后一个✅ 支持动态字段只有满足条件的字段才会被加入 SQL 小贴士set标签只能用于UPDATE语句它内部会处理多个if或choose等标签的内容推荐在每个字段后都加上逗号除了最后一个这样逻辑更清晰MyBatis 会自动清理。✅ 补充为什么不用手动拼接如果不使用set你可能得自己判断是否要加SET还要判断是否有字段被修改以及是否要加逗号。代码复杂且容易出错。而用了set后你可以放心地写set if testxxxxxx #{xxx},/if if testyyyyyy #{yyy},/if /setMyBatis 会帮你搞定一切希望这个例子能让你彻底明白set标签的作用 如果有更多 MyBatis 动态 SQL 的问题也可以继续问我当然可以我们来详细、深入地介绍一下 MyBatis 中的foreach标签结合实际场景和代码示例让你彻底理解它的用法。 目标理解foreach的作用与属性foreach是 MyBatis 提供的一个动态 SQL 标签用于在 SQL 中对 Java 集合List、Set、Array进行循环遍历常用于批量插入批量删除批量更新IN 条件查询等✅ 典型场景批量删除用户比如我们要执行DELETE FROM user WHERE id IN (1, 2, 3)其中1, 2, 3是从 Java 传过来的一个列表如ListInteger ids Arrays.asList(1, 2, 3);这时候就可以使用foreach来动态生成这些 ID。foreach的基本语法foreach collection? item? open? close? separator? !-- 循环体内容 -- /foreach属性说明collection要遍历的集合对象List、Set、Array 等对应 Java 参数中的集合变量名item每次遍历时当前元素的别名相当于 Java 中的i或elementopen循环开始前添加的内容例如(close循环结束后添加的内容例如)separator元素之间的分隔符例如,✅ 实际例子批量删除用户1. Java 代码准备ListInteger ids new ArrayList(); ids.add(1); ids.add(2); ids.add(3); // 调用 Mapper 方法 userMapper.deleteUsersByIds(ids);2. Mapper XML 写法delete iddeleteUsersByIds parameterTypejava.util.List DELETE FROM user WHERE id IN foreach collectionids itemid open( close) separator, #{id} /foreach /delete3. 生成的 SQL 效果MyBatis 会自动把ids列表里的每个值都拿出来拼成DELETE FROM user WHERE id IN (1, 2, 3)✅ 完美 各个属性详解带例子collection要遍历的集合示例1参数是 List 类型foreach collectionids itemid ...ids是你传入的ListInteger对象的名字。示例2参数是 Map集合在 map 中MapString, Object params new HashMap(); params.put(ids, Arrays.asList(1, 2, 3));foreach collectionids itemid ...示例3参数是 POJO集合是字段class UserBatch { private ListInteger userIds; // getter/setter }foreach collectionuserIds itemid ...⚠️ 注意如果集合是数组collection可以写为array如果是数组类型或直接写数组名。item当前元素的别名foreach collectionids itemid ... #{id} /foreach每次循环时id就代表当前的数字如 1、2、3你可以换成任意名字比如item、value、uid都行foreach collectionids itemuserId ... #{userId} /foreachopen和close控制括号示例IN 查询需要括号foreach collectionids itemid open( close) separator, #{id} /foreach→ 输出(1, 2, 3)示例没有括号的情况比如批量插入INSERT INTO user(name) VALUES foreach collectionnames itemname open close separator, (#{name}) /foreach→ 输出(张三), (李四), (王五)separator元素之间的分隔符值效果,最常用用于 IN 查询;用于多条语句AND用于多个条件连接示例1逗号分隔foreach collectionids itemid separator, #{id} /foreach→ 输出1, 2, 3示例2AND 连接条件where foreach collectionconditions itemcond separator AND #{cond} /foreach /where✅ 更复杂的例子批量插入假设我们要批量插入多个用户ListUser users Arrays.asList( new User(张三, zhangsanexample.com), new User(李四, lisiexample.com) );Mapper XMLinsert idbatchInsertUsers parameterTypejava.util.List INSERT INTO user(name, email) VALUES foreach collectionusers itemuser separator, (#{user.name}, #{user.email}) /foreach /insert生成的 SQLINSERT INTO user(name, email) VALUES (张三, zhangsanexample.com), (李四, lisiexample.com)✅ 成功批量插入⚠️ 注意事项避坑指南防止 SQL 注入使用#{}而不是${}避免拼接出问题。例如#{id}是安全的${id}不安全。空集合处理如果集合为空foreach不会生成任何内容可能导致 SQL 错误。建议加判断if testids ! null and ids.size() 0 foreach collectionids itemid open( close) separator, #{id} /foreach /if性能注意批量操作建议不要超过 1000 条否则可能超限。MySQL 默认max_allowed_packet大小限制了单条 SQL 的长度。集合类型命名规范如果参数是List? listcollection写list如果是List? ids写ids如果是UserBatch batch且内部有ListInteger userIds则写userIds✅ 总结foreach的核心功能功能说明✅ 遍历集合支持 List、Set、Array✅ 自动拼接生成符合 SQL 规范的字符串✅ 控制格式通过open,close,separator控制输出格式✅ 安全性使用#{}防止 SQL 注入 记忆口诀“集collection中取项item开open闭close分隔separator”如果你还有其他场景想看比如批量更新多条件 IN 查询动态构建 WHERE 条件也可以继续问我我会给你更多例子 你提到的“扩展”部分是关于分页查询在实际开发中的封装方式。我们来一步步、详细地解释清楚让你彻底看懂这个“扩展”的含义。 目标理解“分页查询的扩展”——为什么需要PageResult怎么用✅ 先回顾一下基础什么是分页分页就是把数据分成一页一页显示比如每页 10 条第 1 页第 1~10 条第 2 页第 11~20 条...MySQL 中使用LIMIT实现LIMIT 起始位置, 每页条数起始位置 (当前页码 - 1) × 每页条数例如第 1 页(1-1)*10 0→LIMIT 0, 10第 2 页(2-1)*10 10→LIMIT 10, 10第 3 页(3-1)*10 20→LIMIT 20, 10✅ 这个公式和例子你已经懂了没问题 那么“扩展”到底是什么意思“扩展”是指在真实项目中我们不会只返回一个 List 数据而是会封装成一个更完整的对象 ——PageResult。这就像你点外卖商家不会只给你一盒饭而是给你一个袋子里面有饭 饮料 筷子 收据信息完整。同样分页结果不只是“数据”还应该包括当前页的数据列表总记录数总页数是否有下一页等✅ 举个实际例子说明假设我们要查用户列表每页 10 条当前是第 2 页。 假设数据库中有 25 条用户数据第一步执行 SQL 查询数据带 LIMITSELECT * FROM user LIMIT 10, 10; -- 取第 11~20 条→ 返回 10 条用户数据第 2 页第二步执行另一个 SQL 查询总数SELECT COUNT(*) FROM user;→ 返回25 然后我们把这两部分结果封装成一个对象PageResultpublic class PageResultT { private ListT records; // 当前页的数据列表如 10 条用户 private long total; // 总条数如 25 private int pageNum; // 当前页码如 2 private int pageSize; // 每页条数如 10 private int totalPages; // 总页数25 / 10 3 页 private boolean hasNext; // 是否有下一页第2页 → 有 private boolean hasPrev; // 是否有上一页第2页 → 有 }✅ 如何计算这些值字段计算方式total执行COUNT(*)得到pageNum传入参数如 2pageSize传入参数如 10totalPages(total pageSize - 1) / pageSize向上取整→(25 10 - 1) / 10 3hasNextpageNum totalPages→2 3→ truehasPrevpageNum 1→2 1→ true✅ 最终返回的PageResultUser对象内容{ records: [ {id: 11, name: 张三}, {id: 12, name: 李四}, ... ], total: 25, pageNum: 2, pageSize: 10, totalPages: 3, hasNext: true, hasPrev: true } 为什么要这么做重要❌ 如果不封装会发生什么ListUser users userMapper.selectByPage(pageNum, pageSize); // 你只能拿到 10 条数据但不知道总共有多少条 // 用户界面无法显示“共 25 条共 3 页”✅ 封装后的好处优点说明✅ 前端展示完整显示“共 25 条共 3 页当前第 2 页”✅ 支持分页控件可以实现“上一页”、“下一页”按钮✅ 提高代码复用性所有接口都返回统一格式的分页结果✅ 更易维护不需要每个接口都手动计算总页数✅ 实际开发中的流程图前端请求/users?page2size10 ↓ Controller 接收参数 ↓ Service 层调用 Mapper 1. selectUsers(page, size) → 返回 ListUser 2. selectTotal() → 返回 long total ↓ 封装成 PageResultUser ↓ 返回给前端✅ 注意事项文档里说的“注意”“分页通常需要执行两条 SQL一条查数据一条查 COUNT(*) 总数。”为什么不能只查一次因为LIMIT只能控制取多少条不能告诉你总共有多少条。所以必须单独查一次COUNT(*)。优化方案高级使用数据库的窗口函数如 MySQL 8.0 的ROW_NUMBER()或者使用 MyBatis Plus、PageHelper 等工具自动处理但底层还是两个 SQL只是帮你封装了✅ 总结“扩展”到底是什么内容说明核心思想把分页结果封装成一个对象而不仅仅是 List关键对象PageResultT包含数据列表 总条数 分页信息为什么需要为了让前端能完整展示分页信息总页数、是否有下一页等如何实现两条 SQL一条查数据一条查总数然后封装 记忆口诀“分页不止要数据还要总条数封装成 PageResult前端才好做”如果你还想看如何写PageResult类如何在 MyBatis 中实现分页查询如何用 PageHelper 插件简化分页也可以继续问我 非常好我们来详细、清晰地用一个完整的例子带你理解“统一结果封装Result Wrapper”这个概念。 目标理解ResultT的作用与使用场景在前后端分离开发中后端不能直接返回数据而是要返回一个标准化的响应格式让前端能准确判断请求是否成功并获取提示信息。这就是“统一结果封装”的核心目的。✅ 先看一个真实场景❌ 传统方式不推荐假设你调用一个接口查询用户信息{ name: 张三, email: zhangsanexample.com }但如果你登录失败了返回的是{ msg: 未登录 }或者系统出错了返回{ error: 数据库连接失败 } 前端怎么知道这是成功还是失败是登录问题还是系统错误❌ 完全靠猜容易出错✅ 正确做法统一结果封装我们定义一个通用的响应类ResultTpublic class ResultT { private int code; // 状态码 private String msg; // 提示信息 private T data; // 实际数据 // 构造函数、getter/setter 略 }然后所有接口都返回这种格式。✅ 标准结构详解字段说明示例code业务状态码200: 成功-1: 未登录500: 系统错误msg提示信息操作成功,密码错误,系统繁忙data真正的数据查询到的用户对象、列表等 举个完整例子用户登录接口1. 前端请求POST /api/login Content-Type: application/json { username: zhangsan, password: 123456 }2. 后端处理逻辑PostMapping(/login) public ResultUser login(RequestBody LoginRequest request) { String username request.getUsername(); String password request.getPassword(); // 1. 检查是否登录 if (username null || password null) { return Result.fail(-1, 用户名或密码不能为空); } // 2. 查询用户 User user userService.findByUsername(username); if (user null) { return Result.fail(404, 用户不存在); } // 3. 验证密码简化 if (!password.equals(user.getPassword())) { return Result.fail(401, 密码错误); } // 4. 登录成功 return Result.success(登录成功, user); }3. 返回结果JSON✅ 情况一登录成功{ code: 200, msg: 登录成功, data: { id: 1, name: 张三, email: zhangsanexample.com } }✅ 情况二密码错误{ code: 401, msg: 密码错误, data: null }✅ 情况三未登录参数为空{ code: -1, msg: 用户名或密码不能为空, data: null }✅ 如何实现ResultT类public class ResultT { private int code; private String msg; private T data; // 私有构造函数防止外部随意创建 private Result(int code, String msg, T data) { this.code code; this.msg msg; this.data data; } // 成功方法 public static T ResultT success(String msg, T data) { return new Result(200, msg, data); } // 失败方法 public static T ResultT fail(int code, String msg) { return new Result(code, msg, null); } // getter/setter 略 }✅ 为什么需要统一封装重点优点说明✅ 前端可以统一处理不管哪个接口只要看code就知道成功还是失败✅ 避免歧义不再猜测返回的是什么类型✅ 支持国际化msg可以支持多语言✅ 易于扩展后续可以加 token、时间戳等字段✅ 便于调试开发者一眼就能看出是成功还是失败✅ 实际项目中的效果前端代码示例JavaScriptaxios.post(/api/login, formData) .then(response { const { code, msg, data } response.data; if (code 200) { alert(msg); // 登录成功 localStorage.setItem(user, JSON.stringify(data)); window.location.href /dashboard; } else if (code 401) { alert(msg); // 密码错误 } else if (code -1) { alert(msg); // 用户名或密码不能为空 } }) .catch(err { alert(网络异常请稍后重试); });✅ 前端不需要关心具体接口返回什么只需要判断code✅ 扩展常见状态码建议code含义说明200成功操作成功-1未登录用户未登录或 Token 过期400参数错误请求参数不合法401未授权密码错误、权限不足404资源不存在用户不存在、文章已删除500系统错误数据库异常、服务崩溃✅ 总结“统一结果封装”到底是什么它是一种约定俗成的 API 响应格式确保前后端通信清晰、可靠。 核心思想所有接口都返回ResultT对象前端通过code判断成功或失败通过msg给用户提示通过data获取实际数据 记忆口诀“统一封装很重要code 决定成败msg 告诉用户啥data 才是真数据”如果你还想看如何在 Spring Boot 中全局返回ResultT如何用拦截器自动包装返回值如何处理异常并返回标准格式也可以继续问我 非常好我们来详细、清晰地用一个完整的例子带你理解“统一结果封装Result Wrapper”这个概念。 目标理解ResultT的作用与使用场景在前后端分离开发中后端不能直接返回数据而是要返回一个标准化的响应格式让前端能准确判断请求是否成功并获取提示信息。这就是“统一结果封装”的核心目的。✅ 先看一个真实场景❌ 传统方式不推荐假设你调用一个接口查询用户信息{ name: 张三, email: zhangsanexample.com }但如果你登录失败了返回的是{ msg: 未登录 }或者系统出错了返回{ error: 数据库连接失败 } 前端怎么知道这是成功还是失败是登录问题还是系统错误❌ 完全靠猜容易出错✅ 正确做法统一结果封装我们定义一个通用的响应类ResultTpublic class ResultT { private int code; // 状态码 private String msg; // 提示信息 private T data; // 实际数据 // 构造函数、getter/setter 略 }然后所有接口都返回这种格式。✅ 标准结构详解字段说明示例code业务状态码200: 成功-1: 未登录500: 系统错误msg提示信息操作成功,密码错误,系统繁忙data真正的数据查询到的用户对象、列表等 举个完整例子用户登录接口1. 前端请求POST /api/login Content-Type: application/json { username: zhangsan, password: 123456 }2. 后端处理逻辑PostMapping(/login) public ResultUser login(RequestBody LoginRequest request) { String username request.getUsername(); String password request.getPassword(); // 1. 检查是否登录 if (username null || password null) { return Result.fail(-1, 用户名或密码不能为空); } // 2. 查询用户 User user userService.findByUsername(username); if (user null) { return Result.fail(404, 用户不存在); } // 3. 验证密码简化 if (!password.equals(user.getPassword())) { return Result.fail(401, 密码错误); } // 4. 登录成功 return Result.success(登录成功, user); }3. 返回结果JSON✅ 情况一登录成功{ code: 200, msg: 登录成功, data: { id: 1, name: 张三, email: zhangsanexample.com } }✅ 情况二密码错误{ code: 401, msg: 密码错误, data: null }✅ 情况三未登录参数为空{ code: -1, msg: 用户名或密码不能为空, data: null }✅ 如何实现ResultT类public class ResultT { private int code; private String msg; private T data; // 私有构造函数防止外部随意创建 private Result(int code, String msg, T data) { this.code code; this.msg msg; this.data data; } // 成功方法 public static T ResultT success(String msg, T data) { return new Result(200, msg, data); } // 失败方法 public static T ResultT fail(int code, String msg) { return new Result(code, msg, null); } // getter/setter 略 }✅ 为什么需要统一封装重点优点说明✅ 前端可以统一处理不管哪个接口只要看code就知道成功还是失败✅ 避免歧义不再猜测返回的是什么类型✅ 支持国际化msg可以支持多语言✅ 易于扩展后续可以加 token、时间戳等字段✅ 便于调试开发者一眼就能看出是成功还是失败✅ 实际项目中的效果前端代码示例JavaScriptaxios.post(/api/login, formData) .then(response { const { code, msg, data } response.data; if (code 200) { alert(msg); // 登录成功 localStorage.setItem(user, JSON.stringify(data)); window.location.href /dashboard; } else if (code 401) { alert(msg); // 密码错误 } else if (code -1) { alert(msg); // 用户名或密码不能为空 } }) .catch(err { alert(网络异常请稍后重试); });✅ 前端不需要关心具体接口返回什么只需要判断code✅ 扩展常见状态码建议code含义说明200成功操作成功-1未登录用户未登录或 Token 过期400参数错误请求参数不合法401未授权密码错误、权限不足404资源不存在用户不存在、文章已删除500系统错误数据库异常、服务崩溃✅ 总结“统一结果封装”到底是什么它是一种约定俗成的 API 响应格式确保前后端通信清晰、可靠。 核心思想所有接口都返回ResultT对象前端通过code判断成功或失败通过msg给用户提示通过data获取实际数据 记忆口诀“统一封装很重要code 决定成败msg 告诉用户啥data 才是真数据”如果你还想看如何在 Spring Boot 中全局返回ResultT如何用拦截器自动包装返回值如何处理异常并返回标准格式也可以继续问我
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

哪家公司做网站开发做得比较好北京网站设计精选刻

Tsukimi开源媒体播放器:你的终极跨平台媒体中心解决方案 【免费下载链接】tsukimi A simple third-party Emby client 项目地址: https://gitcode.com/gh_mirrors/ts/tsukimi Tsukimi开源媒体播放器是一款基于Rust语言构建的高性能Emby客户端,专为…

张小明 2026/1/7 23:04:33 网站建设

网站建设工程师待遇门户是什么意思

Grammarly高级会员Cookie自动搜索工具完整指南 【免费下载链接】autosearch-grammarly-premium-cookie 项目地址: https://gitcode.com/gh_mirrors/au/autosearch-grammarly-premium-cookie 想要免费使用Grammarly Premium高级语法检查功能吗?这个Grammarly…

张小明 2026/1/7 23:03:28 网站建设

番禺区建设局网站html静态网页制作

Gboard输入法词库升级指南:让你的打字速度翻倍提升 【免费下载链接】gboard_dict_3 Gboard 词库 Magisk 模块, 基于《现代汉语词典》 项目地址: https://gitcode.com/gh_mirrors/gb/gboard_dict_3 还在为输入法词汇量不足而频繁翻页选词吗?想要在…

张小明 2026/1/7 23:02:56 网站建设

做一个小说网站需要多少钱电子商务网站建设 以为例

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 快速生成一个Node.js内存监控原型,功能包括:1. 基础内存使用仪表盘;2. 阈值警报功能;3. 简单的问题诊断;4. 一键生成报告…

张小明 2026/1/7 23:02:23 网站建设

网站开发asp 视频教程长沙本土网站建设公司

活动目录的监控与维护指南 在管理活动目录(Active Directory,简称 AD)时,有效的监控和维护是确保其稳定运行的关键。下面将详细介绍 AD 监控与维护的相关内容。 1. AD 可靠性工作簿概述 AD 可靠性工作簿包含多个工作表,具体如下: - 概述表 :提供一系列定义。 - …

张小明 2026/1/7 23:01:51 网站建设

梅州建站网络科技有限公司外贸建英文网站的重要性

Unity游戏引导系统实现:从基础到进阶的完整指南 【免费下载链接】Unity3DTraining 【Unity杂货铺】unity大杂烩~ 项目地址: https://gitcode.com/gh_mirrors/un/Unity3DTraining 引导系统核心功能解析 Unity游戏引导系统是提升玩家体验的关键组件&#xff0…

张小明 2026/1/7 23:01:19 网站建设