苍穹外卖需要注意的地方

news2026/4/6 7:25:33
公共字段自动填充自定义注解AutoFill公共字段自动填充和反射有很大的关系公共字段填充中自定义注解AutoFill -反射在查找给某个方法进行公共字段填充的时候的标识反射与注解认识注解属性名后面要加在使用的时候把注解写在方法上括号内为属性名赋值特殊情况在注解只有一个属性value的时候在方法上面给value赋值的时候可以不写value注解的原理就是注解本质上是一个接口继承了Annotation方法在给注解中的属性赋值的时候实际上是在实现注解给注解创造实现类对象又因为继承的特殊性实现了子类注解也就实现了Annotation注解元注解Target注解说明注解可以在哪里使用Retention注解说明注解的保留周期注解的解析自定义切面在执行update和insert方法的时候开启公共字段的自动填充通过反射获取方法签名从而获取签名中的对数据库的操作类型通过反射获取方法先获取类再获取方法通过反射获取的不同方法对于不同的方法设置不同的数据/* 自定义切面实现公共字段自动填充处理逻辑 */ //加入切面注解 Aspect //Bean类交给spring容器管理 Component Slf4j public class AutoFillAspect { /* 切入点 */ Pointcut(execution(com.sky.annotation.AutoFill * com.sky.mapper.*.*(..))) public void autoFillPointCut() { } /* 前置通知在通知中进行公共字段的赋值 */ Before(autoFillPointCut()) public void autoFill(JoinPoint joinPoint) { log.info(开始进行公共字段的自动填充); //获取到当前被拦截的方法上的数据库的操作类型 //1.获取方法签名对象 MethodSignature signature (MethodSignature) joinPoint.getSignature(); //2.获取方法上的注解对象 AutoFill autoFill signature.getMethod().getAnnotation(AutoFill.class); //3.获取数据库的操作类型 OperationType operationType autoFill.value(); //获取到当前被拦截的方法的参数--实体对象 //做出一个约定把实体对象放在参数的第一个 Object[] args joinPoint.getArgs(); if(args null args.length 0){ return; } Object entity args[0]; //准备赋值的数据 LocalDateTime now LocalDateTime.now(); Long currentId BaseContext.getCurrentId(); //根据当前不同的操作类型为参数的不同属性通过反射赋值 if(operationType.equals(OperationType.INSERT)){ //为四个公共字段赋值 try { Method setCreateTime entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class); Method setCreateUser entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class); Method setUpdateTime entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); //通过反射为对象属性赋值 setCreateTime.invoke(entity,now); setCreateUser.invoke(entity,currentId); setUpdateTime.invoke(entity,now); setUpdateUser.invoke(entity,currentId); } catch (Exception e) { throw new RuntimeException(e); } }else if(operationType.equals(OperationType.UPDATE)){ try { Method setUpdateTime entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); //通过反射为对象属性赋值 setUpdateTime.invoke(entity,now); setUpdateUser.invoke(entity,currentId); } catch (Exception e) { throw new RuntimeException(e); } } } }查询回显-一对多多表查询—两张表分开查询Service 层核心分开两次查询Service public class CategoryServiceImpl implements CategoryService { Autowired private CategoryMapper categoryMapper; Autowired private SetmealMapper setmealMapper; // 注入套餐Mapper /** * 分开查询分类 套餐一对多 */ Override public CategoryVO getCategoryWithSetmeal(Long categoryId) { // 第一次查询查 主表一的一方 Category category categoryMapper.getById(categoryId); // 第二次查询查 从表多的一方 // 根据分类ID查所有套餐 ListSetmeal setmealList setmealMapper.getByCategoryId(categoryId); // 手动封装成 VO CategoryVO vo new CategoryVO(); BeanUtils.copyProperties(category, vo); vo.setSetmealList(setmealList); return vo; } }—另一种方法XML 核心一对多查询最关键resultMap idCategoryWithSetmealMap typecom.sky.vo.CategoryVO !-- 一的一方分类 -- id columnc_id propertyid/ result columnc_name propertyname/ !-- 多的一方套餐一对多核心 -- collection propertysetmealList ofTypecom.sky.entity.Setmeal id columns_id propertyid/ result columns_name propertyname/ result columns_price propertyprice/ /collection /resultMap !-- 一对多关联查询 SQL -- select idgetCategoryWithSetmeal resultMapCategoryWithSetmealMap SELECT c.id AS c_id, c.name AS c_name, s.id AS s_id, s.name AS s_name, s.price AS s_price FROM category c LEFT JOIN setmeal s ON c.id s.category_id WHERE c.id #{categoryId} /select表套表--修改的复杂情况口味可能被是被删掉了也可能是被修改了不好说调用哪个接口所以采用先删除后上传的方法在新增菜品的时候如果还要新增口味就只能在菜品添加完毕并返回主键ID之后才能添加口味insert idinsert useGeneratedKeystrue keyPropertyid insert into dish(name, category_id, price, image, description, create_time, update_time, create_user, update_user, status) values (#{name},#{categoryId},#{price},#{image},#{description}, #{createTime},#{updateTime},#{createUser},#{updateUser},#{status}) /insertTransactional Override public void saveWithFlavor(DishDTO dishdto) { Dish dish new Dish(); BeanUtils.copyProperties(dishdto,dish); //向菜品表插入1条数据 dishMapper.insert(dish); //获取insert语句生成的主键值 Long dishIddish.getId(); //向口味表插入n条数据,支持批量插入 ListDishFlavor flavors dishdto.getFlavors(); if(flavors!null !flavors.isEmpty()){ for(DishFlavor flavor:flavors){ flavor.setDishId(dishId); } dishFlavorMapper.insertBatch(flavors); } }接口参数规则解析这张表是一个 **【修改套餐状态】** 的接口文档我帮你把参数的含义、位置、以及前后端怎么对接彻底讲清楚。一、接口核心信息请求方式PUT / POST通常是修改状态请求格式JSONBody 体路径参数status在 URL 路径中Query 参数id在 URL 问号后二、参数位置与规则3 个参数全覆盖1. Header请求头参数名值说明Content-Typeapplication/json必须填表示我发送的是 JSON 格式数据缺了这个后端解析不到数据。2. 路径参数Path Param参数名示例备注status1套餐状态1 起售打开售卖0 停售禁止售卖这个参数拼在 URL 路径里。3. Query 参数Query Param参数名是否必须示例备注id是101套餐 ID要修改的那个套餐的 ID比如 101 号套餐这个参数拼在 URL?后面。三、正确的请求 URL拼接规则根据接口文档请求的 URL 要这样写/api/setmeal/[status]?id[套餐id]实际例子修改 101 号套餐为起售PUT http://localhost:8080/api/setmeal/1?id101/1路径参数status1表示起售?id101Query 参数id101表示修改 ID 为 101 的套餐四、后端代码怎么接Java 解析因为参数在路径和Query两个地方后端 Controller 要分开接收。1. Controller 写法核心PutMapping(/setmeal/{status}) // 这里捕获路径参数 status ApiOperation(修改套餐状态) public ResultString updateStatus( // 接收路径参数 PathVariable Integer status, // 接收 Query 参数 RequestParam Long id ) { log.info(修改套餐状态id{}, status{}, id, status); // 调用 Service 处理逻辑 setmealService.updateStatus(id, status); return Result.success(修改成功); }2. DTO / 实体类不需要写 DTO因为参数很少直接用RequestParam和PathVariable接不用写SetmealDTO如果是复杂的新增 / 修改才用 DTO。五、前端请求代码Vue 示例前端必须按照这个规则发送请求URL 格式不能错。// 1. 定义参数 const status 1; // 起售 const id 101; // 套餐ID // 2. 发送请求 // 注意URL 要拼接 /status?idxxx await axios.put(/api/setmeal/${status}?id${id});六、特别注意避坑指南Content-Type必须是application/json虽然这个接口传的参数很少只有 id但因为是 PUT 请求后端通常要求接收 JSON 体。如果后端报错Required request body is missing说明你没加这个 Header或者没传 JSON 体。参数位置不要搞混status写在路径里/1id写在查询参数里?id101不要把id写到路径里严格按照文档来。Path 参数 vs Query 参数我用最通俗的方式把本质区别、使用场景、前后端写法一次性讲透帮你彻底分清。一、本质区别一句话总结维度Path 参数路径参数Query 参数查询参数位置URL路径中/xxx/{id}URL? 后面?idxxxnamexxx作用标识资源本身比如「哪个套餐」「哪个用户」对资源做筛选、分页、条件、附加操作是否必须通常是必填缺了就找不到资源通常是可选不传用默认值格式直接嵌入路径无key只有值keyvalue键值对多参数用分隔缓存友好性路径变了 资源变了适合做缓存同一资源不同参数缓存需特殊处理二、直观对比用你刚才的套餐接口举例1. 路径参数Path ParamURL 示例/admin/setmeal/1含义1是路径参数代表「状态 1起售」直接嵌在 URL 路径里特点是 URL 的一部分缺了这个路径就不存在404适用场景资源的状态、类型、分类比如/setmeal/{status}、/user/{id}2. 查询参数Query ParamURL 示例/admin/setmeal/1?id101含义id101是查询参数在?后面是对路径资源的附加条件特点不是 URL 的核心路径不传也能访问后端做默认值处理适用场景分页、筛选、排序、附加参数比如?page1pageSize10、?status1三、核心使用场景怎么选✅ 什么时候用 Path 参数标识唯一资源/user/{userId}、/order/{orderId}必须传否则找不到资源资源的分类 / 状态/setmeal/{status}、/category/{type}状态是资源的属性嵌在路径里RESTful 风格接口符合「URL 代表资源HTTP 方法代表操作」的设计规范✅ 什么时候用 Query 参数分页查询/setmeal/page?page1pageSize10条件筛选/setmeal?categoryId13status1排序 / 搜索/setmeal?name套餐sortcreateTime可选附加参数非必须不传不影响核心资源访问四、后端代码写法SpringBoot 对比1. Path 参数PathVariable// 路径参数{status} 嵌在URL里 PutMapping(/setmeal/{status}) public Result updateStatus( PathVariable Integer status, // 绑定路径中的{status} RequestParam Long id // 绑定?后面的id ) { // 逻辑... }必须用PathVariable注解参数名要和路径占位符{status}完全一致路径参数是 URL 的一部分缺了会 4042. Query 参数RequestParam/ 自动绑定// Query参数?idxxxnamexxx GetMapping(/setmeal/page) public Result page(SetmealPageQueryDTO dto) { // 自动绑定?后面的参数到DTO } // 或者单独接收 GetMapping(/setmeal) public Result list( RequestParam(required false) String name, RequestParam(defaultValue 1) Integer page ) { // 逻辑... }用RequestParam或直接用 DTO接收支持requiredfalse可选、defaultValue默认值多参数用分隔顺序不影响五、前端请求写法Axios 对比1. Path 参数// 路径参数直接拼在URL里 const status 1; const id 101; axios.put(/admin/setmeal/${status}?id${id});2. Query 参数// Query参数用params对象自动拼接 axios.get(/admin/setmeal/page, { params: { page: 1, pageSize: 10, categoryId: 13, status: 1 } }); // 自动生成URL/admin/setmeal/page?page1pageSize10categoryId13status1六、关键避坑指南1. 不要搞混参数位置路径参数是URL 的一部分不能用?包裹Query 参数必须在?后面多参数用分隔错误示例/setmeal?status1?id101?只能有一个后面全是 Query 参数2. Path 参数的 RESTful 规范路径参数尽量用单数/user/{userId}不要/users/{userId}不要把多个参数都塞路径里/setmeal/{id}/{status}不如/setmeal/{id}?status{status}清晰3. Query 参数的空值处理Query 参数支持requiredfalse不传时后端用默认值路径参数通常是必填不传会 404适合做强校验七、一句话总结面试 / 工作都能用Path 参数是「资源的身份证」用来定位唯一资源Query 参数是「资源的筛选条件」用来对资源做附加操作。遵循 RESTful 风格URL 定位资源Query 描述操作。pagehelper的细节PageHelper 原理一、PageHelper 到底是什么它是一个MyBatis 拦截器Interceptor作用自动帮你拼接分页 SQL不用你自己写LIMIT ?,?二、核心原理一句话PageHelper 在执行你的查询 SQL 之前偷偷拦截自动帮你改成 分页 SQL然后再执行。三、它的工作流程4 步走1. 你写PageHelper.startPage(1, 10);作用把 page1、pageSize10 存到当前线程里ThreadLocal2. 你执行查询ListSetmealVO list mapper.page(dto);3. PageHelper 拦截器工作拦截你的 SQL从 ThreadLocal 取出page1, pageSize10*自动计算offset (1-1)10 0把你的 SQL 改成分页 SQLSELECT * FROM table LIMIT 0,104. 返回分页结果 Page/PageInfo总条数当前页数据总页数四、为什么你之前page 不生效只有 pageSize 生效因为你违反了 PageHelper 最核心的规则 规则 1必须紧跟在查询方法前面中间不能有任何代码java运行// 正确 PageHelper.startPage(1,10); List list mapper.select(); // 错误分页失效 PageHelper.startPage(1,10); 其他代码(); List list mapper.select(); 规则 2查询方法必须返回 List 类型你之前写xmlresultTypePageResult→ 返回的不是 List→ PageHelper无法拦截→ 只能拼出sqlLIMIT 10→page 失效 规则 3page 不能是 0 或 nulljava运行PageHelper.startPage(0,10);→ 生成 SQLsqlLIMIT 10→ 只有 pageSize 生效五、PageHelper 最关键的 3 个知识点1. 基于ThreadLocal存储分页参数2. 基于MyBatis 拦截器改写 SQL3. 只对紧跟的第一条查询生效六、你之前的错误总结XML 返回类型错误返回 PageResult 而不是 VOPageHelper 无法拦截生成错误 SQLLIMIT 10page 不生效只有 pageSize 生效七、正确写法最终版java运行// 1. 开启分页 PageHelper.startPage(pageNum, pageSize); // 2. 立刻查询必须紧跟 ListSetmealVO list setmealMapper.page(dto); // 3. 封装分页 PageInfoSetmealVO pageInfo new PageInfo(list); return new PageResult(pageInfo.getTotal(), pageInfo.getList());select idpage resultTypeSetmealVO ... /selectPageHelper 自动帮你拼 LIMIT 的拦截器必须紧跟查询、必须返回 List、page 不能为 0PageHelper 原理极简版PageHelper.startPage(page, pageSize)把分页参数存到当前线程的ThreadLocal里。执行查询紧接着的第一条List?查询会被 PageHelper 的MyBatis 拦截器截获。自动改 SQL拦截器根据线程里的分页参数自动计算offset (page-1)*pageSize给你的 SQL 加上LIMIT offset, pageSize。封装分页结果查询完返回Page/PageInfo包含总条数、当前页数据。核心记住这 3 条只对紧跟的第一条查询生效必须返回List才能分页基于ThreadLocal MyBatis 拦截器实现以后再出现 “只有 pageSize 生效”你就知道要么page 是 0要么没紧跟查询要么返回不是 List。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2483695.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…