一、数据库环境
二、公共字段自动填充
只要有表使用了这些字段,就能在被使用时自动填充值。
update_time自动填充出问题01
update_time自动填充出问题02
实体类中:
@TableField(fill = FieldFill.INSERT) //插入时填充字段
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新时填充字段
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
实现MetaObjectHandler接口:
package com.hisi.pex.common.mpConfig;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* MyBatisPlus提供的对公共字段的操作
*/
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入数据时的操作
*
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
//查看是否有setter方法,来判定该字段是否存在(即是否要填充该字段)
if (metaObject.hasSetter("createTime")) {
metaObject.setValue("createTime", LocalDateTime.now());
}
if (metaObject.hasSetter("updateTime")) {
metaObject.setValue("updateTime", LocalDateTime.now());
}
}
/**
* 更新数据时的操作
*
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
if (metaObject.hasSetter("updateTime")) {
// metaObject.setValue("updateTime", LocalDateTime.now());
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
}
}
ThreadLocal
工具类BaseContext:
/**
* 基于ThreadLocal封装工具类,用于设置和获取当前登录用户的ID
*/
public class BaseContext {
//new一个ThreadLocal,作用于Long id,所以T->Long
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
private BaseContext() {
}
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
}
在Filter的doFilter()方法中使用工具类设置当前用户id(或者另两个方法里)
//3. 判断登录状态,如果已登录,直接放行
if (request.getSession().getAttribute("employee") != null) {
log.info("用户已登录,id为{}", request.getSession().getAttribute("employee"));
//使用工具类,存储当前用户id到ThreadLocal
Long empId = (Long) request.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);
filterChain.doFilter(request, response);
return;
}
创建元数据对象处理器:
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入数据时的操作
*
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("插入数据时操作:...");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
/**
* 更新数据时的操作
*
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("更新数据时操作:...");
log.info(metaObject.getOriginalObject().toString());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}
三、文件上传与下载
(一)文件上传
代码实现(将文件上传到了指定位置,在yml配置了基础文件路径):
@Value("${reggie.path}")
private String basePath;
/**
* 上传文件
*
* @param file
* @return
*/
@PostMapping("/upload")
public Result<String> upLoad(MultipartFile file) {
//file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
//1. 获取原始文件名,以及文件后缀
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//2. 使用UUID生成文件名,防止文件名重复导致的文件被覆盖
String fileName = UUID.randomUUID().toString() + suffix;
//3. 创建目录对象,判断该目录是否存在
File dir = new File(basePath);
if (!dir.exists()) {
//目录不存在就创建
dir.mkdirs();
}
//4. 将临时文件转存到指定位置
try {
file.transferTo(new File(basePath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
//返回文件名,方便图片通过文件名存入数据库
return Result.success(fileName);
}
(二)文件下载
代码实现:
/**
* 文件下载,通过response输出流传给前端展示
*
* @param name
* @param response
*/
@GetMapping("/download")
public void downLocal(String name, HttpServletResponse response) {
try {
//1. 通过name读取本地文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(basePath + name));
//2. 获取输出流
ServletOutputStream outputStream = response.getOutputStream();
//设置返回类型为图片
response.setContentType("image/*");
//3. 通过IOUtils工具实现输入输出流对拷
IOUtils.copy(bis, outputStream);
outputStream.close();
bis.close();
} catch (IOException e) {
// e.printStackTrace();
throw new BusinessException("文件下载失败,请稍后重试");
}
}
四、DTO
- DTO(Data Transfer Object),数据传输对象,一般用于展示层和服务层之间的数据传输。
- 一般DTO都有自己单独的包,不放在domain里。
五、规范
- 不能在service层注入其他mapper,service层只允许调用自己的DAO层,这是规范,想要在自己的service层调用别的DAO层要靠相应的service层去调。
六、开发工具
- 对象拷贝工具
//对象拷贝(因为Dish中没有封装categoryName,所以需要查询多个表)
//通过spring提供的工具,复制属性方法,进行对象拷贝,参数:(原始对象,目标对象,"忽略的属性名")
BeanUtils.copyProperties(dishPage, dishDtoPage, "records");
七、Linux相关配置
(一)配置网络
网络配置文件目录:cd /etc/sysconfig/network-scripts/
找到对应文件vi,大致内容如下
TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="static"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="ens33"
UUID="11527653-4284-4ad0-8c74-51b153b5473f"
DEVICE="ens33"
ONBOOT="yes"
IPADDR="192.168.32.10"
PREFIX="24"
GATEWAY="192.168.32.2"
DNS1="114.114.114.114"
IPV6_PRIVACY="no"
(1)配置防火墙等
- 关闭防火墙
systemctl stop firewalld.service
- 关闭防火墙自启动
chkconfig firewalld off
- 关闭Selinux
vim /etc/sysconfig/selinux
(修改如下内容)
SELINUX=disabled
- 使SElinux立即生效,参数0是宽容模式(Prmissive),参数1是强制模式(Enforcing)
setenforce 0
查看Selinux状态,
getenforce
(二)配置yum源
(1)本地yum源
mkdir /mnt/centos
将镜像文件传入根目录中进行挂载
mount /CentOS-7-x86_64-DVD-1511.iso /mnt/centos/
- 将自带的
.repo
文件移动到其他地方,并自行创建一个repo
mkdir /opt/repo
mv /etc/yum.repos.d/* /opt/repo/
vi /etc/yum.repos.d/local.repo
- 内容如下
[local]
name=local
baseurl=file:///mnt/centos/
gpgcheck=0
enabled=1
yum clean all
清除缓存
yum makecache
生成缓存
yum list
查看列表- 安装vim和自动补全,然后重新登录以生效
yum install vim bash-completion -y
(2)网络yum源
- 配置阿里yum源
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
- 清除缓存并重新生成
(3)yum安装软件
(三)常用命令复习
cat [-n] fileName
查看显示文件内容,-n表示从1开始对行数编号。more fileName
以分页形式显示内容
回车:向下滚动一行
空格:向下滚动一屏
b:返回上一屏
q或ctrl+c:退出tail [-20] [-f] fileName
显示文件末尾内容
默认显示末尾10行
-20表示显示末尾20行
-f:动态读取末尾内容,主要用于日志监控mkdir [-p] dirName
创建目录
-p表示多级目录创建,如果父目录不存在,直接创建一个rm [-rf] fileName
删除文件或目录
-r:将目录和其中子目录、文件一同删除
-f:无需确认,直接删除cp [-r] 源文件位置 目标文件位置
-r:如果复制的是目录,则必须用此选项- mv命令包含了改名的功能
tar [-zcxvf] fileName [files]
对文件进行打包、解包、压缩、解压
.tar
表示只打包,没压缩;.tar.gz
表示打包并压缩
-z:gzip,通过gzip命令处理文件,gzip可以进行压缩或解压
-c:create,创建新的包文件
-x:extract,从包文件中还原文件
-v:verbose,显示命令执行过程
-f:file,指定包文件名称
[files]:不指定,默认将所在目录下的所有文件打包;指定文件名,将目标文件打包
一般打包时,需要指定包文件名称-f
和目标文件files
;tar -cv test.tar test/
解压并解包时tar -zxvf test.tar.gz
,解压完之后就有test目录echo "hhh" >> test/java.txt
find dirName -option fileName
指定目录查找文件;一般-option为-name,用文件名称来查找
eg:find . -name "*.java"
查找当前目录后缀为.java的文件grep wordName fileName
指定文件查找文本内容
eg:grep Hello Test.txt
在Test文件中查找文本Hello
grep Hello *.txt
在所有后缀为txt中查找文本Hello
(1)Vim
三种模式:
- 命令模式
- 插入模式
- 底行模式:可以通过命令对文件内容进行查找、显示行号、退出vim等,
[:或/]
进入该模式,/
进入查找模式,如/name
,n查找下一个,N查找上一个;set nu
显示行号;
(四)常用软件安装
(1)Java
二进制安装:
- 上传.tar.gz到Linux。java历史版本
- tar命令解压
tar -zxvf jdk-xxxx.tar.gz -C /usr/local
- 配置环境变量,/etc/profile末尾加入如下
JAVA_HOME=/usr/local/jdkxxxx
PATH=$JAVA_HOME/bin:$PATH
- 重载profile文件,使更改立即生效
sourcce /etc/profile
- java -version
(2)TomCat
二进制安装:
- 上传.tar.gz到Linux。tomcat历史版本
- 解压到/usr/local
- 进入tomcat的bin目录启动服务
sh startup.sh
或./startup.sh
- 验证是否启动成功
ps -ef | grep tomcat
ps -ef
查看当前运行的所有进程的详细信息 - 停止服务命令
sh shutdown.sh
或./shutdown.sh
或者结束tomcat进程,通过ps -ef找到进程号,然后kill -9 进程号
(3)MySQL
rpm安装:
- 首先查看系统是否安装了MySQL和mariadb(mariadb和mysql有冲突)
rpm -qa | grep mysql
rpm -qa | grep mariadb
- 卸载冲突软件
rpm -e --nodeps mariadb-libs...(刚找到的软件名称)
- 将.tar.gz上传到Linux。mysql历史版本(linux选RPM Bundle)
mkdir /usr/local/mysql
新建一个文件夹,因为这个mysql包解压后会有很多个文件- 解压
tar -zxvf 包名 -C /usr/local/mysql
(解压后有多个rpm包) - rpm包安装顺序,必须按照该顺序安装:
rpm -ivh 以下包名依次安装
mysql-community-common-5.7.25-1.el7.x86_64.rpm
mysql-community-libs-5.7.25-1.el7.x86_64.rpm
mysql-community-devel-5.7.25-1.el7.x86_64.rpm
mysql-community-libs-compat-5.7.25-1.el7.x86_64.rpm
mysql-community-client-5.7.25-1.el7.x86_64.rpm
yum install net-tools
mysql-community-server-5.7.25-1.el7.x86_64.rpm
- 启动mysql
systemctl status mysqld
自启动systemctl enable mysqld
netstat -tunlp
查看已启动服务(还有端口等信息yum install net-tools提供) - 登录Mysql,查看临时密码
cat /var/log/mysqld.log | grep password
mysql -uroot -p
然后输入临时密码 - 修改配置密码
# 密码长度最低位数
set global validate_password_length=4;
# 密码安全等级
set global validate_password_policy=LOW;
# 密码
set password = password("0000");
# 开启访问权限(外部也能访问mysql)
grant all on *.* to 'root'@'%' identified by 'root';
flush privileges;
(4)lrzsz
该软件用于在Linux中文件上传和下载的软件(比如宿主机和虚拟机的文件互传)。
yum安装:
- 搜索是否有该安装包
yum list lrzsz
- 安装
yum install lrzsz.x86_64 -y
rz
命令就可以打开文件互传界面
八、项目部署
(一)手动部署
- 将项目打为jar包(packge)
java -jar xxx.jar
运行该项目- 改为后台运行该项目,并将日志输出到日志文件
nohup command [Arg...] [&]
不挂断运行指定命令,退出终端不会影响程序执行
command:要执行的命令
Arg:一些参数,可以指定输出文件
&:让命令在后台运行
eg:nohup java -jar xxx.jar &> hello.log &
后台运行java -jar命令,并将日志输出到指定log文件 - nohup启动的命令如何停止
ps -ef | grep 'java -jar'
,找到进程号,kill -9 进程号
(二)Shell脚本部署
步骤:
- Linux中安装Git(本地编好的代码push到仓库,在Linux中直接pull代码)
- Linux安装Maven
- 编写Shell脚本(拉取代码、编译、打包、启动)
- 授予Shell脚本执行权限
- 执行Shell脚本
(1)安装Git
yum install git -y
安装完成后,直接输入git命令就可以执行- 克隆项目到本地
git clone 地址
(2)安装Maven
- 上传.tar.gz到Linux
- 解压
tar -zxvf /apache-maven-3.5.4-bin.tar.gz -C /usr/local
vim /etc/profile
export MAVEN_HOME=/usr/local/apache-maven-3.5.4
export PATH=$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH
source /etc/profile
- 查看是否成功
mvn -version
vim /usr/local/apache-maven-3.5.4/conf/settings.xml
修改配置文件如下,该目录作为本地仓库目录(先mkdir /usr/local/repo
)
<localRepository>/usr/local/repo</localRepository>
(3)编写Shell脚本
(4)Shell脚本授权
ll
查看该文件权限情况
- 授予权限
chmod 777 文件名
如何计算该数字?
权限共有三组,rwx对应2^2
,2^1
,2^0
,加起来最大权限就是7。
九、项目优化
(一)Redis缓存优化
(1)环境搭建
- 导入Reids的maven坐标
- 配置文件.yum,添加Redis的内容
- 配置类(非必须,但可以加一个)
(2)缓存登录验证码
之前的验证码是在userController里设置放在Session里的(默认保存30分钟),需要优化到缓存里(实际只需要用redis保存5分钟就够了)
(3)缓存菜品数据
(4)Spring Cache
注意!!!!!!!!!!!!!!!!!!!!!!!!!
使用注解方式实现缓存,其对象必须实现序列化(implements Serializable)
简介
Spring提供的用于整合其他缓存产品的框架,可以通过注解的方式来加缓存,简化代码开发。
默认使用Map作为缓存,Map跟随内存,如果服务被重启,则之前缓存的数据会消失。
可以更改底层缓存为对应的缓存产品,比如Redis。
常用注解
@CachePut()
,通常用在新增中(不是一定用在增,根据业务逻辑的不同来)
@CacheEvicat()
,通常用在删改中,有时也用于增
@Cacheable()
,通常用在查询中
更改底层缓存技术为Redis
(5)缓存套餐数据
注意:Result类要实现Serializable
(二)MySQL读写分离
(1)主从复制
介绍
是一个异步复制过程,底层基于MySQL自带的二进制日志功能,从库通过复制解析该二进制日志来保证数据一致。
配置
主库Master配置:
- 修改Mysql配置文件
etc/my.cnf/
[mysqld]
log-bin=mysql-bin #启用二进制日志
server-id=100 # 服务器唯一ID,自定义
- 重启Mysql
systemctl restart mysqld
- 创建一个新Mysql用户,用于主库和从库之间的通信(slave必须被mater授予必要的权限才能从主库复制)
注意:密码复杂度要求大写+小写+特殊符+数字
GRANT REPLICATION SLAVE ON *.* to 'slaveuser'@'%' identified by 'Aa!000000';
- 数据库中查看Master状态,并记录下File(日志文件名)和Position(日志文件位置)值;查看时不要执行任何操作,因为其他操作会导致File和Position有变动。
show master status;
从库Slave配置:
(如果是vmware克隆,用uuidgen
重新生成uuid并更改,然后再重新生成mysql的uuid):
- 修改配置文件
/etc/my.cnf
[mysqld]
server-id=101 # 服务器唯一ID,自定义
- 重启Mysql
- 登录mysql,执行SQL
change master to master_host='192.168.32.10',master_user='slaveuser',master_password='Aa!000000',master_log_file='mysql-bin.000001',master_log_pos=442;
- 启动从库
start slave;
- 查看从库状态
show slave status\G
要保证下面两个都为yes
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
测试
主库新建数据库,看从库是否也存在。
(2)读写分离案例
只需要导入Maven,并修改yml配置文件就够了
背景
Sharding-JDBC介绍
入门案例
- 导入Maven坐标。
- 配置文件.yml中配置读写分离规则。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/db1?serverTimezone=UTC&userUnicode=true&charsetEncoding=utf-8&useServerPrepStmts=true
username: root
password: "000000"
type: com.alibaba.druid.pool.DruidDataSource
shardingsphere:
datasource:
names: master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jsbc:mysql://192.168.32.10:3306/db1?characterEncoding=utf-8
username: root
password: 000000
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jsbc:mysql://192.168.32.20:3306/db1?characterEncoding=utf-8
username: root
password: 000000
masterslave:
# 读写分离配置(某一个策略,目前策略为轮询)
load-balance-algorithm-type: round_robin
# 最终的数据源名称
name: dataSource
# 主库数据源名称
master-data-source-name: master
# 从库数据源名称列表,多个用逗号分割
slave-data-source-names: slave
props:
sql:
# 开启sql显示
show: true
main:
# 允许Bean定义覆盖,当前有shardingsphere和Druid的dataSource,启动时会报错,所以要允许后创建的覆盖先创建的Bean
allow-bean-definition-overriding: true
- 配置文件.yml中配置运行bean定义覆盖配置项(上面已经配置了,main.allow-bean…)
功能测试
(3)项目实现读写分离
将上面的配置文件,复制到yml文件,覆盖datasource
的内容就够了。
(三)Nginx反向代理
(四)前后端分离
前后端分离开发后,前后端代码不再混合在同一个Maven工程中,而是分为前端工程和后端工程。
(1)Yapi
主要用于定义接口(定制接口)
视频讲解
(2)Swagger
前后端分离开发时,后端常用技术,帮助生成接口文档
5.网页查看,localhost:8080/doc.html
Swagger常用注解
通过使用Swagger注解,可以在查看文档时更清晰的看见项目描述
(3)项目部署
部署架构
部署环境说明
部署前端项目
部署后端项目