点餐服务项目笔记
点餐服务项目笔记
前言
这是一份写给自己的笔记,主要记录瑞吉外卖项目中自己没有了解过的知识点。我将按照功能来分别解析
项目结构
除了基本的Controller,Service,Mapper,Config,Util,Entity层之外,还有我之前没了解过的common层和dto层
common层包含整个应用程序使用的公共辅助方法,和util层类似,但更有普适性,common层类中的方法对绝大多数功能都有辅助作用,如R类的作用是返回给前端通用格式的结果,能简化整个controller类的开发
dto层,DTO全称为Data Transfer Object,数据传输对象,起到数据封装与隔离的作用
common层工具
返回结果类R
此类是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面
1 |
|
业务逻辑
新增信息
以前的项目中,我在Controller方法中设置形参都是把一个个字段设置为形参,然后在DAO层分别写每个字段的添加方法,这样十分麻烦,我们可以把接收的形参设置为JSON实体类对象,再由mabatis-plus自动生成sql语句,就能省很多事了
1 |
|
菜品分页查询
在前端回显数据时,我们要回显菜品分类的名称,但是菜品名称不在Dish这张表中,因此Dish实体类中也没有菜品名称的字段,因此我们用DishDto来扩展Dish,来辅助后端给前端发数据
1 |
|
1 |
|
其他技术要点
前端知识
localStorage和SessionStorage的区别:
localStorage 和 sessionStorage 属性允许在浏览器中存储 key/value 对的数据。
localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。
localStorage 属性是只读的。
如果你只想将数据保存在当前会话中,可以使用 sessionStorage 属性, 该数据对象临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。
JS对长整型数据进行处理时会损失精度
因此当我们数据库中id为长整型时,把id交给前端之前,先把id转成String类型,这样就不会损失精度了
java基础
Lambda表达式
Lambda表达式超详细总结_Code0cean的博客-CSDN博客_lambda表达式详细总结
Lambda表达式简化了函数式接口(只有一个抽象方法的接口)的实现操作,用Lambda表达式可以很快地创建函数式接口的实现对象。
当要传递给Lambda体的操作,已经有实现的方法了,就可以使用方法引用
1 |
|
Stream 流
Java 8 Stream | 菜鸟教程 (runoob.com)
Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合_云深i不知处的博客-CSDN博客_java stream 分组聚合
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
全局异常处理
使用全局异常处理可以避免重复在每一个业务逻辑里面写try…catch来捕获异常
1 |
|
自定义业务异常类
我们可以通过自定义业务异常类来抛出自定义的信息
1 |
|
使用时代码如下
1 |
|
在全局异常处理器中捕获自定义异常
1 |
|
Serliazeable
在Redis中存储对象,该对象是需要被序列化的,而对象要想被成功的序列化,就必须得实现 Serializable 接口.Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里(系列化),并且可以从其它地方把该Byte 流里的数据读出来(反序列化)
只要让类继承Serialzable接口就可以实现序列化
1 |
|
Spring 基础
接收前端发来的JSON数据
1 |
|
Springboot中 json序列化与反序列化
Springboot集成并封装了Jackson,使用Jackson来操作json,JSON的序列化与反序列化我们可以通过@Responsebody
和@RequestBody
轻松实现
Lombok
Lombok是一个可以减少java模板代码的工具,我们可以用Lombok来简化Entity类的代码,省去了get,set及构造方法,接下来介绍lombok的常用注解
@Data
在Entity类中使用该注解,在项目编译时,会帮我们自动加上set,get以及toString方法
@Slf4j
在类上使用该注解,我们可以在类中的方法使用log
函数来输出日志信息
MybatisPlus
条件构造器
顾名思义,作用就是封装查询条件,生成sql的where条件
在项目中,查询数据库用到了LambdaQuaryWrapper
1 |
|
公共字段填充
数据库里经常需要填充一些公共字段,如用户id,更新时间等,我们可以把这些操作交给mybatis-plus自动完成
第一步去实体类给要自动填充的字段加注解
1 |
|
第二步在common层添加自定义元数据对象处理器
1 |
|
但是在MyMetaObjectHandler中,我们不能直接获取HttpSession对象,也就不能直接获取session中的用户ID,此项目采用ThreadLocal解决
ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问当前线程对应的值。
我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。 如果在后续的操作中, 我们需要在Controller / Service中要使用当前登录用户的ID, 可以直接从ThreadLocal直接获取。
1 |
|
然后我们在filter中判断用户的登录情况,放行前给ThreadLocal赋值
1 |
|
然后我们就可以在各个地方获取ThreadLocal变量了
项目优化
SpringCache
Spring Cache只是提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。CacheManager是Spring提供的各种缓存技术抽象接口。
针对不同的缓存技术需要实现不同的CacheManager:
CacheManager | 描述 |
---|---|
EhCacheCacheManager | 使用EhCache作为缓存技术 |
GuavaCacheManager | 使用Google的GuavaCache作为缓存技术 |
RedisCacheManager | 使用Redis作为缓存技术 |
注解
在SpringCache中提供了很多缓存操作的注解,常见的是以下的几个:
注解 | 说明 |
---|---|
@EnableCaching | 开启缓存注解功能 |
@Cacheable | 在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中 |
@CachePut | 将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。
例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。
@CachePut注解
@CachePut 说明:
作用: 将方法返回值,放入缓存
value: 缓存的名称, 每个缓存名称下面可以有很多key
key: 缓存的key ———-> 支持Spring的表达式语言SPEL语法
在save方法上加注解@CachePut
当前UserController的save方法是用来保存用户信息的,我们希望在该用户信息保存到数据库的同时,也往缓存中缓存一份数据,我们可以在save方法上加上注解 @CachePut,用法如下:
1 |
|
key的写法如下:
#user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;
#user.name: #user指的是方法形参的名称, name指的是user的name属性 ,也就是使用user的name属性作为key ;
@CacheEvict注解
@CacheEvict 说明:
作用: 清理指定缓存
value: 缓存的名称,每个缓存名称下面可以有多个key
key: 缓存的key ———-> 支持Spring的表达式语言SPEL语法
在 delete 方法上加注解@CacheEvict
当我们在删除数据库user表的数据的时候,我们需要删除缓存中对应的数据,此时就可以使用@CacheEvict注解, 具体的使用方式如下:
1 |
|
在更新数据的时候也需要删除缓存,以免数据不同步
@Cacheable注解
@Cacheable 说明:
作用: 在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
value: 缓存的名称,每个缓存名称下面可以有多个key
key: 缓存的key ———-> 支持Spring的表达式语言SPEL语法
在getById上加注解@Cacheable
1 |
|
缓存非null值
在@Cacheable注解中,提供了两个属性分别为: condition, unless 。
condition : 表示满足什么条件, 再进行缓存 ;
unless : 表示满足条件则不缓存 ; 与上述的condition是反向的 ;
具体实现方式如下:
1 |
|
==注意: 此处,我们使用的时候只能够使用 unless, 因为在condition中,我们是无法获取到结果 #result的。==
MySQL主从数据库
面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。 ——瑞吉外卖PPT
主从复制
以下内容摘自CSDN博客 要不一起ci个饭的博客-CSDN博客_主从复制
主从复制的定义
主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库。在赋值过程中,一个服务器充当主服务器,而另外一台服务器充当从服务器。
当一台从服务器连接到主服务器时,从服务器会通知主服务器从服务器的日志文件中读取最后一次成功更新的位置。然后从服务器会接收从哪个时刻起发生的任何更新,然后锁住并等到主服务器通知新的更新
做主从复制的好处
做数据的热备
作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
架构的扩展
业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的评率,提高单个机器的I/O性能。
读写分离(重点)
使数据库能支持更大的并发。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。
读写分离
面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。
ShardingJDBC介绍
Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
使用Sharding-JDBC可以在程序中轻松的实现数据库读写分离。
Sharding-JDBC具有以下几个特点:
1). 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
2). 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
3). 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。
Nginx,Swagger
重构项目的时候会详细学一下,目前看这些都是纸上谈兵。
本文作者: Xu Yuhuan
本文链接: https://xuyuhuan.com/article/c8278629/
版权声明: 转载本博客的文章请注明原始出处和作者,谢谢。