- service层接收employeeDTO之后不能直接传入下一层,要转换为实体对象,使用
BeanUtils.copyProperties(employeeDTO, employee);
-
当涉及多张表使用逻辑外键关联时(例如
dish_id
),由于在插入主表数据时无法立即获得主表的id
,因此在主表的insert
语句中应该使用useGeneratedKeys="true"
和keyProperty="id"
,以便在插入主表后,自动获取生成的主键,并将其用于副表的外键关联。<insert id="insert" useGeneratedKeys="true" keyProperty="id"
>- useGeneratedKeys="true":让 MyBatis 在插入主表时自动获取生成的主键
- keyProperty="id":把生成的主键值赋给主表实体(例如
Dish
类的id
属性),以便后续插入副表时使用
-
分页查询使用的DTO类与普通DTO类不同,分页查询 DTO 会包含
page
(页码)、pageSize
(每页大小)等分页参数。根据这些参数,数据库查询就能知道从哪一页开始查询,并且限制每页显示多少数据。 -
分页查询service层逻辑:
- controller层从用户请求中接收参数后,设置用户请求中的分页参数,使用
PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
employeePageQueryDTO.getPage()
:表示当前页码。employeePageQueryDTO.getPageSize()
:表示每页显示的条数。
- 执行分页查询,使用
mapper对象.pageQuery(分页DTO对象)
使用page列表对象接受以便进行后续操作 - 获取分页结果,page.getTotal()为总记录数,page.getResult()为当前页面数据
- 返回分页结果,这里要注意参数名称和前端名称一致
- controller层从用户请求中接收参数后,设置用户请求中的分页参数,使用
-
DTO与VO的区别:
- DTO(Data Transfer Object):用于在不同层之间传递数据,通常会包含一些额外的处理逻辑(如分页信息、过滤条件等),而且可以是 可变的,通常用于业务逻辑层的交互。
- VO(Value Object):更倾向于传递 业务值 或者是 前端展示层 所需的数据,通常会是 不可变的,并且不含有业务逻辑。
-
连接操作可以让你把不同表中有关联的数据结合起来,生成一个包含多表数据的结果集。通过连接,可以:
- 获取多个表中相关的数据。
- 在一个查询中完成多个表的查询任务。
- 避免冗余数据,通过不同的表结构进行更高效的数据存储。
-
不同类型的连接可以根据需求得到不同的结果:
- INNER JOIN:只返回两表中都有匹配记录的行。
- LEFT OUTER JOIN (LEFT JOIN):返回左表的所有行和右表匹配的行。如果右表没有匹配行,右表字段会返回
NULL
。 - RIGHT OUTER JOIN (RIGHT JOIN):返回右表的所有行和左表匹配的行。如果左表没有匹配行,左表字段会返回
NULL
。 - FULL OUTER JOIN:返回两表中所有的行,如果某一表没有匹配行,对应的字段为
NULL
。
-
关于controller层接收前端参数的注解
@RequestParam
:获取查询参数或表单数据,获取简单的查询字符串或表单数据@RequestBody
:获取请求体中的数据并转换为 Java 对象(常用于 JSON、XML),@PathVariable
:获取 URL 路径中的变量部分。/{id}@RequestHeader
:获取请求头中的参数。@CookieValue
:获取请求中的 Cookie 参数。@ModelAttribute
:将请求参数绑定到 Java 对象(适用于表单数据)。@RequestPart
:用于处理文件上传等多部分请求。
-
插入:controller层注意传入参数前的注解;service层注意DTO类与普通类的转换
删除:注意判断删除前的条件,能否删除
修改:多张表关联时先修改主表(插入删除同理),再根据逻辑外键修改附表
查询:xml文件书写sql语句时注意多表连接的逻辑,sql语句头部foreach collection=“此处名字与mapper方法传入的形参名一致”例如:
<foreach item="id" collection="ids" open="(" close=")" separator=","> #{id} </foreach>
- 查询操作时controller层返回值使用普通类还是VO类如何判断?
- 普通类(POJO):适用于直接返回数据库模型的情况,没有特殊要求的时候用这个就足够。
- VO 类:适用于需要定制化数据格式或进行字段加工、处理的情况,尤其在前后端分离的项目中尤为常见。
- 关于查询操作中xml文件的resultType
resultType
用来告诉 MyBatis 查询结果应该如何映射到 Java 对象。- 当你执行查询时,数据库返回的是一组 行记录(每一行都是一个数据库表的记录),而 MyBatis 会将每一行记录转换成一个 Java 对象。
resultType="Dish"
表示每一行记录都应该映射成Dish
类型的对象。
- 为什么
parameterType
可以是List
而resultType
通常是List
里的元素类型?parameterType
:当你传递多个参数时,通常需要将它们作为集合传递,这时使用List
作为parameterType
可以更方便地处理多个参数。MyBatis 会自动拆解List
,并将每个元素作为单独的参数传递到 SQL 查询中resultType
:查询的结果集是多行数据,每一行数据对应一个实体(或 POJO),所以resultType
是这个实体的类型。当查询结果是多行时,MyBatis 会将每一行映射为一个对象,最终返回一个包含这些对象的List
。
- Id数据通常用Long型保存
- 书写sql逻辑时,#{}相关注意事项
#{}
中的名称应当与方法参数的名称一致,或者使用@Param
注解指定自定义名称。#{}
用于参数占位符,它会被方法传入的实际参数值所替代。- 使用
@Param
注解时,可以自定义参数名称,不必与方法参数名一致。
- 涉及到多张表的更新时,可以先改主表内容然后删除副表再插入新内容进入副表
- set中子句加逗号where中不用
-
Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。
- MySQL:是关系型数据库,设计时就是为了持久化存储数据,它能够确保数据不会丢失,即使是断电或系统崩溃之后,也能恢复。
- Redis:默认情况下是内存数据库,数据存储在内存中,速度非常快,但它本身并不提供完全的持久化机制
- 在一些特定的场景下,如果数据量较小、对持久化要求不高且高访问频率要求很高,理论上是可以全用 Redis 的。例如,缓存系统、消息队列、排行榜等。但是,对于大规模的业务应用,尤其是需要数据持久化、复杂查询和事务支持的系统,完全使用 Redis 可能会带来数据丢失、管理困难、性能瓶颈等问题。
-
redis常用数据类型
-
字符串(string):普通字符串,Redis中最简单的数据类型
- 底层实现:
- Redis 使用 SDS(Simple Dynamic String) 作为字符串的底层数据结构。SDS 是一种更加高效的字符串表示方式,避免了传统 C 字符串的许多缺点,如定长、频繁复制等。
- SDS 具有动态扩展和自适应大小的特性,可以有效减少内存浪费。
- SDS 会存储字符串的长度信息,避免每次计算字符串长度的开销。
- 应用场景:常用于存储文本、计数器、标记等。
- 底层实现:
-
哈希(hash):也叫散列,类似于Java中的HashMap结构
- 底层实现:
- Redis 使用 哈希表(Hash Table)来实现哈希类型的数据结构。
- 在内部,Redis 会根据哈希表的大小和负载因子决定是否重新哈希(即扩展哈希表)。
- Redis 中的哈希表采用线性探测法来处理哈希冲突,确保哈希表的操作仍然是高效的。
- 应用场景:存储对象的属性(如用户资料),配置信息、缓存的数据对象等。
- 底层实现:
-
列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
- 底层实现:
- Redis 列表底层使用 双向链表(ziplist 或 linkedlist)。
- Ziplist:当列表较小或元素较少时,Redis 会选择使用 Ziplist(压缩的顺序列表)。Ziplist 是一个内存优化的顺序结构,能够节省存储空间。
- Linkedlist:当列表较大或操作频繁时,Redis 会切换到双向链表,支持快速的插入和删除操作。
- 操作效率:O(1) 时间复杂度的从两端插入和删除操作,适合需要快速插入/删除数据的场景。
- 应用场景:消息队列、任务队列、历史记录等。
- 底层实现:
-
集合(set):无序集合,没有重复元素,类似于Java中的HashSet
- 底层实现:
- Redis 使用 哈希表(Hash Table) 来实现集合。在集合中,每个元素的值本身就是一个键,值总是空的。
- 为了提高性能,Redis 在内部使用一个优化的哈希表数据结构,能够实现 O(1) 的查找、插入和删除操作。
- 应用场景:存储独立项、标签系统、去重操作、实时计数等。
- 底层实现:
-
有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
- 底层实现:
- Redis 使用 跳表(Skip List) 和 哈希表(Hash Table) 组合的方式来实现有序集合。
- 跳表是一个类似于多级索引的结构,能够高效地执行范围查询和排序操作。它支持 O(log N) 时间复杂度的插入、删除、查询等操作。
- 哈希表用来存储元素和分数的映射,以便高效的查找和更新。
- 应用场景:排行榜、按分数排序的消息队列、实时排名系统等。
- 底层实现:
-
-
@RestController("user")
中的"user"
是为控制器类指定一个 bean 名称,这个名称可以用来在 Spring 上下文中唯一标识该控制器,类似注解也有相同用法。 -
static与final关键字的使用
-
static
和final
经常一起使用,特别是在定义常量时。static
确保常量是类级别的,所有实例共享;final
确保常量的值一旦设置就不能修改。 -
使用
static
的场景:- 类级别共享的数据:如果变量是属于类而不是某个实例,应该使用
static
。例如,类的常量、计数器、配置等。 - 工具方法或常用函数:如果某个方法不依赖于对象实例,而是可以通过类直接调用,那么可以考虑使用
static
,比如Math.max()
或Arrays.sort()
。 - 性能优化:如果某个方法可以被类直接调用,不依赖实例,使用
static
可以避免对象创建的开销。
- 类级别共享的数据:如果变量是属于类而不是某个实例,应该使用
-
使用
final
的场景:- 常量:如果某个变量的值在整个应用中都不应该改变,应该使用
final
来修饰。例如,public static final double PI = 3.14159;
。 - 防止修改的类、方法或变量:如果某个方法不应该被重写,某个类不应该被继承,或者某个变量的值不应该改变,应该使用
final
。 - 提高代码安全性:如果希望某个值在系统中不可改变,使用
final
可以避免无意中的修改,增强代码的健壮性。
- 常量:如果某个变量的值在整个应用中都不应该改变,应该使用
-
-
HttpClient:是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。 HttpClient的核心API:
- HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
- HttpClients:可认为是构建器,可创建HttpClient对象。
- CloseableHttpClient:实现类,实现了HttpClient接口。
- HttpGet:Get方式请求类型。
- HttpPost:Post方式请求类型。
HttpClient发送请求步骤:
- 创建HttpClient对象
- 创建Http请求对象
- 调用HttpClient的execute方法发送请求
-
微信登陆功能开发
- Controller层首先接收Service层传来的User对象;然后根据用户密钥、超时时间、userId关联用户的map对象生成jwt令牌;封装UserVO对象。
- Service层将传入的登录码转换为json对象,进而获取其中的openid;根据openid判断用户是否已注册,若未注册直接进行注册。
- Mapper层在用户注册时起作用,插入新用户
-
用户发送请求并得到结果的整个过程的总结
- 前端将用户名和密码以
POST
请求的形式发送到后端的登录接口创建HttpClient对象 - 后端会验证用户名和密码。如果验证通过,后端会创建一个包含用户信息的 JWT 令牌。创建Http请求对象
- 后端将JWT令牌返回给前端,前端通常将令牌存储在
localStorage
或sessionStorage
中。 - 后续每次用户访问需要身份验证的接口时,前端都会将存储的JWT令牌添加到请求头中发送。
- 后端会有一个 JWT拦截器(通常是
Filter
或Interceptor
),它会在请求到达具体的 Controller 之前检查请求头中的 JWT 令牌。有效则保存,无效会抛401。 - 如果JWT令牌验证成功,后端的Controller会接收到请求,并执行相应的业务逻辑。
- Controller处理完请求后,将结果以HTTP响应的形式返回给前端。
- 前端将用户名和密码以
-
使用redis缓存热点数据
- 手写定义:创建redisTemplate对象以便使用相关方法(如opsForValue.get/set)操作数据库,判断查询内容是否已存在,若无则加入redis中,有则直接使用而不用访问磁盘中的mysql。
- 使用注解:
- @EnableCaching,开启缓存注解功能,通常加在启动类上
- @Cacheable,在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中
- @CachePut,将方法的返回值放到缓存中
- @CacheEvict,将一条或多条数据从缓存中删除
-
新增购物车数据时出现了不报错但不更新购物车表的问题:
- 原因:没有添加用户jwt拦截器导致接收不到userId,而购物车表与用户id关联,在拦截器生效之前,可能因为没有身份信息,查询语句无法正确执行,导致购物车表的数据无法显示。
- 解决:在WebMvc配置类中添加用户jwt拦截器
registry.addInterceptor(jwtTokenUserInterceptor) .addPathPatterns("/user/**") .excludePathPatterns("/user/user/login") .excludePathPatterns("/user/shop/status");
- 支付功能一般不自己实现,需要申请商家权限,比较复杂
- 分页查询传参时status通常是包装类型Integer,表示可以为null,灵活适配数据库查询语句
- 为什么使用
CollectionUtils.isEmpty()
而不是!ordersList.isEmpty()
?- 能处理
null
情况,避免NullPointerException
。 ordersList.isEmpty()
需要先确保ordersList
不为null
,否则会抛出NullPointerException
。
- 能处理
- 接单拒单和完成订单本质上是改变订单状态的操作,通常只新建对象并修改特定字段,然后将新对象传入update方法:
- 防止不必要的字段更新:
orders
只包含id
和status
,确保只更新status
,不会改动其他字段。
- 防止“脏数据”:
- 避免更新
ordersDB
中的所有字段,只改status
。
- 避免更新
- 优化数据库操作:
update(orders)
只更新status
,减少 SQL 语句的复杂度,提高性能。
- 防止不必要的字段更新:
-
Spring Task
- Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。(注意:需要在启动类添加注解 @EnableScheduling 开启任务调度)
- 使用cron表达式,其实就是一个字符串,来定义任务触发的时间
-
WebSocket
- 基于TCP的新的网络协议它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。
- HTTP协议和WebSocket协议对比:
- HTTP是短连接,WebSocket是长连接
- HTTP通信是单向的,基于请求响应模式,WebSocket支持双向通信
- HTTP和WebSocket底层都是TCP连接
-
WebSocket使用方法:定义一个哈希表,然后把要传输的内容写入,使用
JSON.toJSONString()
方法把哈希表转为JSON格式对象,然后调用如webSocketServer.sendToAllClient()方法将内容传输到前端。