面向对象程序设计大作业

前端部分准备

NodeJS安装

Vue,是一款用于构建用户界面的渐进式的JavaScript(1)框架,通过使用vue官方提供的脚手架create-vue帮我们完成前端的工程化,而要想使用create-vue来创建vue项目,则必须安装依赖环境:NodeJS
(1)HTML负责网页的结构,CSS负责网页的表现,JavaScript让网页具备一定的交互效果,具有一定的动作行为

1. 双击安装包

image-20221008213131316
image-20220818155659933

2. 选择安装目录

安装到一个,没有中文,没有空格的目录下(新建一个文件夹NodeJS)
image-20220818160024929
image-20220818160241172

3. 验证NodeJS环境变量

NodeJS 安装完毕后,会自动配置好环境变量,我们验证一下是否安装成功,通过: node -v
image-20220818160357897

4. 配置npm的全局安装路径

image-20220818161218016 使用管理员身份运行命令行,在命令行中,执行如下指令:
1
npm config set prefix "D:\develop\NodeJS"

注意:E:\develop\NodeJS 这个目录是NodeJS的安装目录

5. 切换npm的淘宝镜像

使用管理员身份运行命令行,在命令行中,执行如下指令:

1
npm config set registry https://registry.npmmirror.com

6. 安装Vue-cli

使用管理员身份运行命令行,在命令行中,执行如下指令:

1
npm install -g @vue/cli

image-20220818161134576
npm:Node Package Manager,是NodeJS的软件包管理器。
在开发前端项目的过程中,我们需要相关的依赖,就可以直接通过 npm install xxx 命令,直接从远程仓库中将依赖直接下载到本地了。

Vue项目创建

项目创建

命令行形式

创建一个工程化的Vue项目,执行命令:npm create vue@3.3.4

图形化界面方式

首先,在桌面创建vue文件夹,双击进入文件夹,地址目录处输入cmd,然后进入到vue文件夹的cmd窗口界面,直接输入命令vue ui进入到vue的图形化界面,选择创建按钮,在vue文件夹下创建项目,然后预设模板选择手动,在功能页面开启路由功能,然后再配置页面选择语言版本和语法检查规范,不保存预设,然后创建项目。最后我们只需要等待片刻,即可进入到创建创建成功的界面。

注:Vue的组件有两种不同的风格:组合式API和选项式API。Vue3提供了组合式API(没有this对象的,this对象是undefined )
执行上述指令之一,将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具

项目结构

pEWuNff.png

启动项目

点击NPM脚本中的dev后的运行按钮

项目流程

Snipaste 2025 04 13 23 09 25
其中*.vue是Vue项目中的组件文件,在Vue项目中也称为单文件组件(SFC,Single-File Components)。Vue 的单文件组件会将一个组件的逻辑 (JS),模板 (HTML) 和样式 (CSS) 封装在同一个文件里(*.vue)

利用ElementPlus辅助开发

Element:是饿了么公司前端开发团队提供的一套基于 Vue3 的网站组件库,用于快速构建网页。
Element 提供了很多组件(组成网页的部件)供我们使用。例如 超链接、按钮、图片、表格等。
官方网站

使用

在当前工程的目录下,执行如下命令

1
2
3
npm install element-plus@2.4.4 --save

npm install pinia //这样才能使用图标

main.js中代码修改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(ElementPlus, {locale: zhCn})
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')

常用到的组件有表格组件,分页条组件,对话框组件,表单组件等

VueRouter

  • Vue Router:Vue的官方路由。 为Vue提供富有表现力、可配置的、方便的路由。
  • Vue中的路由,主要定义的是路径与组件之间的对应关系。

比如,我们打开一个网站,点击左侧菜单,地址栏的地址发生变化。 地址栏地址一旦发生变化,在主区域显示对应的页面组件。
VueRouter主要由以下三个部分组成,如下所示:
Snipaste 2025 04 13 23 21 25

1
2
3
VueRouter:路由器类,根据路由请求在路由视图中动态渲染选中的组件
<router-link>:请求链接组件,浏览器会解析成<a>
<router-view>:动态视图组件,用来渲染展示与路由路径对应的组件

安装VueRouter

1
npm install vue-router@4(Vue 3)或 npm install vue-router@3(Vue 2)

快速上手

  1. App.vue中修改部分代码为
    1
    2
    3
    4
    5
    <script setup>
    </script>
    <template>
    <router-view></router-view>
    </template>
  2. 创建router文件夹,里面创建index.js这一文件。注意要嵌套路由
  3. 在 views/layout/index.vue 中,调整代码,具体调整位置如下:
    1
    2
    3
    在左侧菜单栏的 <el-menu> 标签上添加 router 属性,这会让 Element Plus 的 <el-menu> 组件自动根据路由来激活对应的菜单项。
    使用 <router-view> 组件来渲染根据路由动态变化的内容。
    确保每个 <el-menu-item> 的 index 属性值与你想要导航到的路径相匹配。
    4.安装axios
    Ajax: 全称Asynchronous JavaScript And XML,异步的JavaScript和XML。其作用有如下2点:
  • 与服务器进行数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。
    前端资源被浏览器解析,但是前端页面上缺少数据,前端可以通过Ajax技术,向后台服务器发起请求,后台服务器接受到前端的请求,从数据库中获取前端需要的资源,然后响应给前端,前端在通过我们学习的vue技术,可以将数据展示到页面上,这样用户就能看到完整的页面了。
  • 异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用的校验等等。
    使用原生的Ajax请求的代码编写起来比较繁琐,所以使用更加简单的发送Ajax请求的技术Axios 。Axios是对原生的AJAX进行封装,简化书写
    安装:
    1
    npm install axios
    5.结构优化
    在前端项目开发时,通常会定义一个请求处理的工具类 - src/utils/request.js。在这个工具类中,对axios进行了封装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import axios from 'axios'
    import { ElMessage } from 'element-plus'
    import router from '../router'

    //创建axios实例对象
    const request = axios.create({
    baseURL: '/api',
    timeout: 600000
    })

    //axios的请求 request 拦截器, 每次请求获取localStorage中的loginUser, 从中获取到token, 在请求头token中携带到服务端
    request.interceptors.request.use(
    (config) => {
    let loginUser = JSON.parse(localStorage.getItem('loginUser'))
    console.log(localStorage.getItem('loginUser'))
    if (loginUser) {
    config.headers.token = loginUser.token
    }
    return config
    }
    )

    //axios的响应 response 拦截器
    request.interceptors.response.use(
    (response) => { //成功回调
    return response.data
    },
    (error) => { //失败回调
    //如果响应的状态码为401, 则路由到登录页面
    if (error.response.status === 401) {
    ElMessage.error('登录失效, 请重新登录')
    router.push('/login')
    }else{
    ElMessage.success('接口访问异常')
    }
    return Promise.reject(error)
    }
    )
    而与服务端进行异步交互的逻辑,通常会按模块,封装在一个单独的API中。

后端项目启动

前置基础:Maven、HTTP协议、SpringBootWeb基础、IOC、DI(依赖控制,注入反转)、MySQL、JDBC、Mybatis等

Maven

介绍

Apache Maven是一个项目管理和构建工具,它基于项目对象模型(Project Object Model , 简称: POM)的概念,通过一小段描述信息来管理项目的构建、报告和文档。
Maven的作用:

  1. 方便的依赖管理
  2. 统一的项目结构
  3. 标准的项目构建流程

Maven仓库分为:

  • 本地仓库:自己计算机上的一个目录(用来存储jar包)
  • 中央仓库:由Maven团队维护的全球唯一的。仓库地址:https://repo1.maven.org/maven2/
  • 远程仓库(私服):一般由公司团队搭建的私有仓库
    当项目中使用坐标引入对应依赖jar包后,
  • 首先会查找本地仓库中是否有对应的jar包
    • 如果有,则在项目直接引用
    • 如果没有,则去中央仓库中下载对应的jar包到本地仓库
  • 如果还可以搭建远程仓库(私服),将来jar包的查找顺序则变为: 本地仓库 –> 远程仓库–> 中央仓库

安装

Maven安装配置步骤:

  1. 解压安装
  • bin目录 : 存放的是可执行命令。(mvn 命令重点关注)
  • conf目录 :存放Maven的配置文件。(settings.xml配置文件后期需要修改)
  • lib目录 :存放Maven依赖的jar包。(Maven也是使用java开发的,所以它也依赖其他的jar包)
  1. 配置仓库
  2. 配置阿里云私服
    由于中央仓库在国外,所以下载jar包速度可能比较慢,而阿里公司提供了一个远程仓库,里面基本也都有开源项目的jar包。
    进入到conf目录下修改settings.xml配置文件:
    (1) 使用超级记事本软件,打开settings.xml文件,定位到160行左右
    (2)
    1
    2
    3
    4
    5
    6
    7
    8
    在<mirrors>标签下为其添加子标签<mirror>,内容如下:
    <mirror>
    <id>alimaven</id>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>
    </mirror>
    注意配置的位置,在<mirrors> ... </mirrors>中间添加配置。
  3. 配置Maven环境变量
    Maven环境变量的配置类似于JDK环境变量配置一样
    (1)在系统变量处新建一个变量MAVEN_HOME。 MAVEN_HOME环境变量的值,设置为maven的解压安装目录
    (2)在Path中进行配置。 PATH环境变量的值,设置为:%MAVEN_HOME%\bin

IDEA集成Maven

创建Maven项目

选择 IDEA中 File => close project => Customize => All settings;打开 All settings , 选择 Build,Execution,Deployment => Build Tools => Maven。配置工程的编译版本为17。这里所设置的maven的环境信息,并未指定任何一个project,此时设置的信息就属于全局配置信息。 以后,我们再创建project,默认就是使用我们全局配置的信息。创建一个空项目,命名为 web-project01, 创建好项目之后,进入项目中,要设置JDK的版本号。选择小齿轮,选择 Project Structure, 创建模块,选择Java语言,选择Maven。 填写模块的基本信息。在maven项目中,创建HelloWorld类,并运行。
以刚创建的共项目为例,Maven项目的目录结构:
maven-project01
|— src (源代码目录和测试代码目录)
|— main (源代码目录)
|— java (源代码java文件目录)
|— resources (源代码配置文件目录)
|— test (测试代码目录)
|— java (测试代码java目录)
|— resources (测试代码配置文件目录)
|— target (编译、打包生成文件存放目录)

pom文件详解

1
2
3
4
5
6
7
8
9
- <project> :pom文件的根标签,表示当前maven项目
- <modelVersion>:声明项目描述遵循哪一个POM模型版本
- 虽然模型本身的版本很少改变,但它仍然是必不可少的。目前POM模型版本是4.0.0
- 坐标 :
- <groupId> <artifactId> <version>
- 定位项目在本地仓库中的位置,由以上三个标签组成一个坐标
- <maven.compiler.source> :编译JDK的版本
- <maven.compiler.target> :运行JDK的版本
- <project.build.sourceEncoding> : 设置项目的字符集

Maven坐标

什么是坐标?

  • Maven中的坐标是资源的唯一标识 , 通过该坐标可以唯一定位资源位置
  • 使用坐标来定义项目或引入项目中需要的依赖

Maven坐标主要组成:

  • groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima)
  • artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service)
  • version:定义当前项目版本号
    • SNAPSHOT: 功能不稳定、尚处于开发中的版本,即快照版本
    • RELEASE: 功能趋于稳定、当前更新停止,可以用于发行的版本

导入Maven项目

在IDEA中导入Maven项目,有两种方式。

  • 方式一:File -> Project Structure -> Modules -> Import Module -> 选择maven项目的pom.xml。
  • 方式二:Maven面板 -> +(Add Maven Projects) -> 选择maven项目的pom.xml。

生命周期

Maven的生命周期就是为了对所有的构建过程进行抽象和统一。 描述了一次项目构建,经历哪些阶段。在Maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理,编译,测试及部署。虽然大家都在不停地做构建工作,但公司和公司间、项目和项目间,往往使用不同的方式做类似的工作。Maven从大量项目和构建工具中学习和反思,然后总结了一套高度完美的,易扩展的项目构建生命周期。这个生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成等几乎所有构建步骤。Maven对项目构建的生命周期划分为3套(相互独立):

  • clean:清理工作。
  • default:核心工作。如:编译、测试、打包、安装、部署等。
  • site:生成报告、发布站点等。
    每套生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。我们看到这三套生命周期,里面有很多很多的阶段,这么多生命周期阶段,其实我们常用的并不多,主要关注以下几个:
  • clean:移除上一次构建生成的文件
  • compile:编译项目源代码
  • test:使用合适的单元测试框架运行测试(junit)
  • package:将编译后的文件打包,如:jar、war等
  • install:安装项目到本地仓库
    Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际工作。在Maven的设计中,实际任务(如源代码编译)都交由插件来完成。IDEA工具为了方便程序员使用maven生命周期,在右侧的maven工具栏中,已给出快速访问通道。
  • 生命周期的顺序是:clean –> validate –> compile –> test –> package –> verify –> install –> site –> deploy,而我们需要关注的就是:clean –> compile –> test –> package –> install。
    说明:在同一套生命周期中,我们在执行后面的生命周期时,前面的生命周期都会执行。
    思考:当运行package生命周期时,clean、compile生命周期会不会运行?
    clean不会运行,compile会运行。 因为compile与package属于同一套生命周期,而clean与package不属于同一套生命周期。
    补充:三套生命周期又包含哪些具体的阶段呢, 我们来看下面这幅图:
    pExbS6U.png

Maven常见问题

  • 问题现象:Maven项目中添加的依赖,未正确下载,造成右侧Maven面板中的依赖报红,再次reload重新加载也不会再下载。
  • 产生原因:由于网络原因,依赖没有下载完整导致的,在maven仓库中生成了xxx.lastUpdated文件,该文件不删除,不会再重新下载。双击 del.bat这个批处理脚本,就可以递归删除该目录下所有的 xxx.lastUpdated 文件。

一些基础

学习前端网页开发的三剑客HTML、CSS、JS之后,我们就可以制作前端页面了。最终,这些页面资料,我们就可以部署在服务器上,然后打开浏览器就可以直接访问服务器上部署的前端页面了。而像HTML、CSS、JS 以及图片、音频、视频等这些资源,我们都称为静态资源。 所谓静态资源,就是指在服务器上存储的不会改变的数据,通常不会根据用户的请求而变化。那与静态资源对应的还有一类资源,就是动态资源。那所谓动态资源,就是指在服务器端上存储的,会根据用户请求和其他数据动态生成的,内容可能会在每次请求时都发生变化。比如:Servlet、JSP等(负责逻辑处理)。而Servlet、JSP这些技术现在早都被企业淘汰了,现在在企业项目开发中,都是直接基于Spring框架来构建动态资源。而对于我们java程序开发的动态资源来说,我们通常会将这些动态资源部署在Tomcat,这样的Web服务器中运行。 而浏览器与服务器在通信的时候,基本都是基于HTTP协议的。那上述所描述的这种浏览器/服务器的架构模式呢,我们称之为:BS架构。
Spring发展到今天已经形成了一种开发生态圈,Spring提供了若干个子项目,每个项目用于完成特定的功能。spring家族的技术被称为spring全家桶。本项目在项目开发时,选择的是springboot。

springboot一点点解析

我们在创建springboot项目的时候,选择了web开发的起步依赖 spring-boot-starter-web。而spring-boot-starter-web依赖,又依赖了spring-boot-starter-tomcat,由于maven的依赖传递特性,那么在我们创建的springboot项目中也就已经有了tomcat的依赖,这个其实就是springboot中内嵌的tomcat。 而我们运行引导类中的main方法,其实启动的就是springboot中内嵌的Tomcat服务器。 而我们所开发的项目,也会自动的部署在该tomcat服务器中,并占用8080端口号 。
起步依赖:

  • 一种为开发者提供简化配置和集成的机制,使得构建Spring应用程序更加轻松。起步依赖本质上是一组预定义的依赖项集合,它们一起提供了在特定场景下开发Spring应用所需的所有库和配置。
    • spring-boot-starter-web:包含了web应用开发所需要的常见依赖。
    • spring-boot-starter-test:包含了单元测试所需要的常见依赖。
  • 官方提供的starter:https://docs.spring.io/spring-boot/docs/3.1.3/reference/htmlsingle/#using.build-systems.starters
    每一个起步依赖,都用于开发一个特定的功能。
    举例:当我们开发中需要使用redis数据库时,只需要在SpringBoot项目中,引入:springboot-starter-redis ,即可导入redis开发所需要的依赖。
    不论使用IDEA创建SpringBoot项目,还是直接在官方网站利用骨架生成SpringBoot项
    目,项目的结构和pom.xml文件中内容是相似的。此外,我们通过maven引入的依赖,是没有指定具体的依赖版本号的。因为每一个SpringBoot工程,都有一个父工程。依赖的版本号,在父工程中统一管理。

计网一些小知识复习–HTTP协议

HTTP协议:

  • 基于TCP协议: 面向连接,安全
  • 基于请求-响应模型
  • HTTP协议是无状态协议

HTTP请求协议:浏览器将数据以请求格式发送到服务器。包括:请求行、请求头 、请求体( - GET请求的请求参数在请求行中,故不需要设置请求体)
HTTP响应协议:服务器将数据以响应格式返回给浏览器。包括:响应行 、响应头 、响应体
注:
1.响应状态码
pExbRuF.png
2.Web服务器(Tomcat)对HTTP协议的请求数据进行解析,并进行了封装(HttpServletRequest),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷;Web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。

请求与响应

那么在后端程序中,如何接收传递过来的普通参数数据呢?
常见的有两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1. 原始方式 
Tomcat接收到http请求时:把请求的相关信息封装到HttpServletRequest对象中。在Controller中,我们要想获取Request对象,可以直接在方法的形参中声明HttpServletRequest 对象。然后就可以通过该对象来获取请求信息:
2. SpringBoot方式
先下载软件postman.Postman原是Chrome浏览器的插件,可以模拟浏览器向后端服务器发起任何形式(如:get、post)的HTTP请求。在Springboot的环境中,对原始的API进行了封装,接收参数的形式更加简单。
(1)简单参数
如果是简单参数,参数名与形参变量名相同,定义同名的形参即可接收参数;请求参数名和controller方法中的形参名不一致时,无法接收到请求数据,解决方案为在方法形参前面加上Spring提供的@RequestParam,然后通过value属性执行请求参数名,从而完成映射。
注:@RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错。如果该参数是可选的,可以将required属性设置为false。比如:@RequestParam(name = "name", required =
false

(2)实体参数
如果请求参数比较多,通过上述的方式一个参数一个参数的接收,会比较繁琐。此时,我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守如下规则:请求参数名与实体类的属性名相同。
【1】简单实体对象: 定义POJO实体类,把name和age作为实体类的属性
【2】复杂实体对象: 复杂实体对象指的是,在实体类中有一个或多个属性,也是实体对象类型的。比如User类中有一个Address类型的属性(Address是一个实体类,包含private String province;private String city;)复杂实体对象的封装,需要遵守如下规则:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。比如:http://localhost:8081/complexPojo?name=Tom&age=7&address.province=beijing&address.city=beijing

(3)数组集合参数
数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数在前端请求时,有两种传递形式:
方式一: xxxxxxxxxx?hobby=game&hobby=java
方式二:xxxxxxxxxxxxx?hobby=game,java

集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系。比如说请求参数只是由http://localhost:8081/arrayParam?hobby=java,game变成了http://localhost:8081/listParam?hobby=java,game,但是方法参数就要从String []hobby变成@RequestParam List<String> hobby
(4)日期参数
对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。
@DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。
后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。
(5)JSON参数
在前后端进行交互时,如果是比较复杂的参数,前后端通过会使用JSON格式的数据进行传输。注意方法参数前面要加上@RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)
如下图所示
(6)路径参数
前端:通过请求URL直接传递参数 比如 http://localhost:8081/path/3/aaa
后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数 比如 (@PathVariable Integer id,@PathVariable String number)

pExLUoj.png
IDEA控制台上输出的是sout输出的前端参数。刚才模拟了浏览器向后端服务器发起任何形式的HTTP请求,那么controller方法中的return的结果,怎么就可以响应给浏览器呢?答案:使用@ResponseBody注解

1
2
3
4
5
@ResponseBody注解:
类型:方法注解、类注解
位置:书写在Controller方法上或类上
作用:将方法返回值直接响应给浏览器
如果返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器

但是在我们所书写的Controller中,只在类上添加了@RestController注解、方法添加了@RequestMapping注解。原因:在类上添加的@RestController注解,是一个组合注解。@RestController = @Controller + @ResponseBody
所以在前后端分离的项目中,一般直接在请求处理类上加@RestController注解,就无需在方法上加@ResponseBody注解了。
定义一个统一的返回结果,包含:响应状态码,状态码信息,返回的数据:给前端响应的数据(字符串、对象、集合)。我们定义在一个实体类Result来包含以上信息。这样我们返回比如说return Result.success(hobby)。在postman中可以看到{
“code”: 1,
“msg”: “success”,
“data”: [
“java”,
“game”
]
}

IOC与DI

先分层

先前的小案例代码太过于臃肿,全都放在Controller层中了。在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些。按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层:
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
Service:业务逻辑层。处理具体的业务逻辑。
Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的
增、删、改、查。
基于三层架构的程序执行流程:
前端发起的请求,由Controller层接收(Controller响应数据给前端)
Controller层调用Service层来进行逻辑处理(Service层处理完后,把处理结果返回给Controller层)
Serivce层调用Dao层(逻辑处理过程中需要用到的一些数据要从Dao层获取)
Dao层操作文件中的数据(Dao拿到的数据会返回给Service层)
三层架构的好处:

  1. 复用性强
  2. 便于维护
  3. 利用扩展

再分离解耦

软件设计原则:高内聚低耦合。
高内聚指的是:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间
的联系程度越高,则内聚性越高,即 “高内聚”。
低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。
之前我们在编写代码时,需要什么对象,就直接new一个就可以了。 这种做法呢,层与层之间代码就耦
合了,当service层的实现变了之后, 我们还需要修改controller层的代码。那只能不new了,就意味着没有业务层对象(程序运行就报错)。我们的解决思路是:提供一个容器,容器中存储一些对象(例:EmpService对象),然后
controller程序从容器中获取EmpService类型的对象。我们想要实现上述解耦操作,就涉及到Spring中的两个核心概念:
控制反转IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器
依赖注入DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。程序运行时需要某个资源,此时容器就为其提供这个资源。例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpServic对象。
IOC容器中创建、管理的对象,称之为:bean对象。

IOC细节

在之前的入门案例中,要把某个对象交给IOC容器管理,需要在类上添加一个注解:@Component(粗暴)
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
@Controller (标注在控制层类上Controller) 再说一遍:在类上添加的@RestController注解,是一个组合注解。@RestController = @Controller + @ResponseBody.所以在前后端分离的项目中,一般直接在请求处理类上加@RestController注解,就无需在方法上加@ResponseBody注解了。
@Service (标注在业务层类上Service)
@Repository (标注在数据访问层类上Dao)
不属于以上三类时用@Component
在IOC容器中,每一个Bean都有一个属于自己的名字,可以通过注解的value属性指定bean的名字。比如
如@Repository(“daoA”)
果没有指定,默认为类名首字母小写。
使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean
只能用@Controller。

DI细节

在入门程序案例中,我们使用了@Autowired(自动装配)这个注解,完成了依赖注入的操作。
@Autowired注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入
操作)。那如果在IOC容器中,存在多个相同类型的bean对象,程序运行会报错。
Spring提供了以下几种解决方案:
@Primary
@Qualifier
@Resource
pExLz1P.png

1
2
3
4
5
6
7
8
使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现。
使用@Qualifier注解:指定当前要注入的bean对象。 在@Qualifier的value属性中,指定注入的
bean的名称。
@Qualifier注解不能单独使用,必须配合@Autowired使用
使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
@Autowired 与 @Resource的区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入

小问题

使用四大注解声明的bean,要想生效,还需要被组件扫描注解@ComponentScan扫描。
@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了引导类声明注解@SpringBootApplication 中, 默认扫描的范围是SpringBoot启动类所在包及其子包。
推荐做法:将我们定义的controller,service,dao这些包呢,都放在引导类所在包com.itheima的子包下,这样我们定义的bean就会被自动的扫描到

数据库

MySQL已经安装
MySQL、Oracle、DB2、SQLServer这些都是属于关系型数据库,里面都是基于二维表存储数据的。
注:不是基于二维表存储数据的数据库,就是非关系型数据库(比如Redis)。
SQL语句根据其功能被分为四大类:DDL、DML、DQL、DCL
DDL 数据定义语言,用来定义数据库对象(数据库,表,字段)
DML 数据操作语言,用来对数据库表中的数据进行增删改
DQL 数据查询语言,用来查询数据库中表的记录
DCL 数据控制语言,用来创建数据库用户、控制数据库的访问权限

图形化工具

连接数据库步骤:
1.打开IDEA自带的Database
2、配置MySQL
3、输入相关信息
4、下载MySQL连接驱动
5、测试数据库连接
6、保存配置
其实工具底层也是通过DDL语句操作的数据库,只不过这些SQL语句是图形化界面工具帮我们自动
完成的。

DDL

常见操作:查询、创建、使用、删除

操作数据库

1.查询
查询所有数据库:show databases;
查询当前数据库:select database();
2.创建
语法:– 数据库不存在,则创建该数据库;如果存在则不创建
create database if not extists itcast;
3 使用数据库
语法:use 数据库名 ;
我们要操作某一个数据库下的表时,就需要通过该指令,切换到对应的数据库下,否则不能操作。
4 删除数据库
语法:drop database [ if exists ] 数据库名 ;
如果删除一个不存在的数据库,将会报错。可以加上参数 if exists ,如果数据库存在,再执行删除,否则不执行删除。
说明:上述语法中的database,也可以替换成 schema
如:create schema db01;
如:show schemas;

表结构的操作

创建表、查询表、修改表、删除表。
1.创建表

1
2
3
4
5
6
7
create table 表名(
字段1 字段1类型 [约束] [comment 字段1注释 ],
字段2 字段2类型 [约束] [comment 字段2注释 ],
......
字段n 字段n类型 [约束] [
comment 字段n注释 ]
) [ comment 表注释 ] ;

注意: [ ] 中的内容为可选参数; 最后一个字段后面没有逗号;约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。

约束 描述 关键字
非空约束 限制该字段值不能为null not null
唯一约束 保证字段的所有数据都是唯一、不重复的 unique
主键约束 主键是一行数据的唯一标识,要求非空且唯一 primary key
默认约束 保存数据时,如果未指定该字段值,则采用默认值 default
外键约束 让两张表的数据建立连接,保证数据的一致性和完整性 foreign key

偷个懒:
主键自增:auto_increment
每次插入新的行记录时,数据库自动生成id字段(主键)下的值。具有auto_increment的数据列是一个正数序列开始增长(从1开始自增)

2.查询表

1
2
3
4
查询当前数据库所有表 show tables;
查看指定表结构 desc 表名 ;#可以查看指定表的字段、字段的类型、是否可以为NULL、是否存在默认值
等信息
查询指定表的建表语句show create table 表名 ;

3.修改表
关于表结构的修改操作,工作中一般都是直接基于图形化界面操作。
添加字段alter table 表名 add 字段名 类型(长度) [comment 注释] [约束];
案例: 为tb_emp表添加字段qq,字段类型为 varchar(11)
图形化操作:添加字段

alter table tb_emp add qq varchar(11) comment ‘QQ号码’;
4.删除表
删除字段
案例:删除tb_emp表中的qq_num字段alter table 表名 drop 字段名;
图形化操作:删除字段
alter table tb_emp drop qq_num;

修改表名
案例:将当前的tb_emp表的表名修改为emp rename table 表名 to 新表名;
图形化操作:修改表名
rename table tb_emp to emp

删除
关于表结构的删除操作,工作中一般都是直接基于图形化界面操作。
删除表语法:drop table [ if exists ] 表名;
if exists :只有表名存在时才会删除该表,表名不存在,则不执行删除操作(如果不加该参数
项,删除一张不存在的表,执行将会报错)。
案例:如果tb_emp表存在,则删除tb_emp表
drop table if exists tb_emp; – 在删除表时,表中的全部数据也会被删除。
图形化操作:删除表

MySQL的数据类型

MySQL中的数据类型有很多,主要分为三类:数值类型、字符串类型、日期时间类型
char是定长字符串,指定长度多长,就占用多少个字符,和字段值的长度无关 。而varchar是变长字符串,指定的长度为最大占用长度 。相对来说,char的性能会更高些。
DATE 3 1000-01-01 至 9999-12-31 YYYY-MM-DD 日期值
TIME 3 -838:59:59 至 838:59:59 HH:MM:SS时间值或持续时间
YEAR 1 1901 至 2155 YYYY 年份值
DATETIME 8 1000-01-01 00:00:00 至9999-12-31 23:59:59YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP 4 1970-01-01 00:00:01 至2038-01-19 03:14:07 YYYY-MM-DD HH:MM:SS 混合日期和时间值,时间戳

数据库操作-DML

DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进
行增、删、改操作。
添加数据(INSERT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
向指定字段添加数据insert into 表名 (字段名1, 字段名2) values (值1, 值2); 
全部字段添加数据insert into 表名 values (值1, 值2, ...);
批量添加数据(指定字段)insert into 表名 (字段名1, 字段名2) values (值1, 值2), (值1, 值2);
批量添加数据(全部字段)insert into 表名 values (值1, 值2, ...), (值1, 值2, ...);
案例1:向tb_emp表的username、name、gender字段插入数据
insert into tb_emp(username, name, gender, create_time, update_time)
values ('wuji', '张无忌', 1, now(), now());
案例2:向tb_emp表的所有字段插入数据
insert into tb_emp(id, username, password, name, gender, image, job,
entrydate, create_time, update_time)
values (null, 'zhirou', '123', '周芷若', 2, '1.jpg', 1, '2010-01-01',
now(), now());
案例3:批量向tb_emp表的username、name、gender字段插入数据
insert into tb_emp(username, name, gender, create_time, update_time)
values ('weifuwang', '韦一笑', 1, now(), now()),
('fengzi', '张三疯', 1, now(), now());
注:1.因为设计表时create_time, update_time两个字段不能为NULL,所以也做为要插入的字段
2.插入数据时,指定的字段顺序需要与值的顺序是一一对应的。
3. 字符串和日期型数据应该包含在引号中。
4. 插入的数据大小,应该在字段的规定范围内。

修改数据(UPDATE)

1
2
3
update 表名 set 字段名1 = 值1 , 字段名2 = 值2 , .... [where 条件] ;
案例1:将tb_emp表中id为1的员工,姓名name字段更新为'张三'
update tb_emp set name='张三',update_time=now() where id=1;

注:1. 修改语句的条件可以有,也可以没有,如果没有条件,则会修改整张表的所有数据。
2. 在修改数据时,一般需要同时修改公共字段update_time,将其修改为当前操作时间。
删除数据(DELETE)

1
2
3
4
5
6
7
8
9
10
delete from 表名 [where 条件] ; 
案例1:删除tb_emp表中id为1的员工 delete from tb_emp where id = 1;
案例2:删除tb_emp表中所有员工 delete from tb_emp;

注意事项:
• DELETE 语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数据。
• DELETE 语句不能删除某一个字段的值(可以使用UPDATE,将该字段值置为NULL即
可)。
• 当进行删除全部数据操作时,会提示询问是否确认删除所有数据,直接点击Execute即
可。

DQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DQL查询语句,语法结构如下:
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数

查询多个字段select 字段1, 字段2, 字段3 from 表名;
查询所有字段(通配符) select * from 表名;
注:* 号代表查询所有字段,在实际开发中尽量少用(不直观、影响效率)
设置别名select 字段1 [ as 别名1 ] , 字段2 [ as 别名2 ] from 表名;
去除重复记录select distinct 字段列表 from 表名;
在SQL语句当中构造条件的运算符分为两类:比较运算符,逻辑运算符
列举一些易混淆的
between … and … 在某个范围之内(含最小、最大值)
in(…) 在in之后的列表中的值,多选一
like 占位符 模糊匹配(_匹配单个字符, %匹配任意个字符)
is null 是null(查询为NULL的数据时,不能使用 = null)

where与having区别
执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组
之后对结果进行过滤。
判断条件不同:where不能对聚合函数进行判断,而having可以。

注意事项:如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序

if(表达式, tvalue, fvalue) :当表达式为true时,取值tvalue;当表达式为false时,
取值fvalue
case 表达式 when 值1 then 结果1 [when 值2 then 结果2 …]
[else result] end

多表设计:
一对多关系实现:在数据库表中多的一方,添加字段,来关联属于一这方的主键。
一对一 :在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
多对多实现关系:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

java语言操作数据库,只能通过一种方式:使用sun公司提供的 JDBC 规范。
Java程序操作数据库,现在主流的方式是:Mybatis。MyBatis是一款优秀的 持久层 框架,是对原始的JDBC程序的封装,用于简化JDBC的开发。
原始的JDBC程序,存在以下几点问题:

  1. 数据库链接的四要素(驱动、链接、用户名、密码)全部硬编码在java代码中
  2. 查询结果的解析及封装非常繁琐
  3. 每一次查询数据库都需要获取连接,操作完毕后释放连接, 资源浪费, 性能降低
    分析了JDBC的缺点之后,我们再来看一下在mybatis中,是如何解决这些问题的:
  4. 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件
    application.properties中
  5. 查询结果的解析及封装,由mybatis自动完成映射封装,我们无需关注
  6. 在mybatis中使用了数据库连接池技术,从而避免了频繁的创建连接、销毁连接而带来的资源浪
    费。

在springboot项目中,可以编写application.properties文件,配置数据库连接信息。我们要连
接数据库,就需要配置数据库连接的基本信息

1
2
3
4
5
6
7
8
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url(最后面是你连接的数据库名字)
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234

数据库连接池的好处:

  1. 资源重用
  2. 提升系统响应速度
  3. 避免数据库连接遗漏

常见的数据库连接池:
C3P0
DBCP
Druid
Hikari (springboot默认)
现在使用更多的是:Hikari、Druid (性能更优越)
Hikari(追光者) [默认的连接池]
Druid(德鲁伊)
Druid连接池是阿里巴巴开源的数据库连接池项目
功能强大,性能优秀,是Java语言最好的数据库连接池之一
如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要完成以下两步操作即可:

  1. 在pom.xml文件中引入依赖
    1
    2
    3
    4
    5
    6
    <dependency>
    <!-- Druid连接池依赖 -->
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
    </dependency>
  2. 在application.properties中引入数据库连接配置
    方式1:spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis
    spring.datasource.druid.username=root
    spring.datasource.druid.password=1234
    方式2:
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
    spring.datasource.username=root
    spring.datasource.password=1234

而对于Mybatis来说,我们在开发持久层程序操作数据库时,需要重点关注以下两个方面:

  1. application.properties
  2. Mapper接口(编写SQL语句)

在创建出来的SpringBoot工程中,在src下的test目录下,已经自动帮我们创建好了测试类 ,并且
在测试类上已经添加了注解 @SpringBootTest,代表该测试类已经与SpringBoot整合。
该测试类在运行时,会自动通过引导类加载Spring的环境(IOC容器)。我们要测试那个bean对象,就可以直接通过@Autowired注解直接将其注入进行,然后就可以测试了。
@Mapper注解:表示是mybatis中的Mapper接口
程序运行时:框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理
@Select注解:代表的就是select查询,用于书写select查询语句
@Delete注解:用于编写delete操作的SQL语句
如果mapper接口方法形参只有一个普通类型的参数,#{…} 里面的属性名可以随便写,如:#
{id}、#{value}。但是建议保持名字一致。

Lombok是一个实用的Java类库,可以通过简单的注解来简化和消除一些必须有但显得很臃肿的Java代码。通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。第1步:在pom.xml文件中引入依赖
第2步:在实体类上添加注解@Data,那么这个类在编译时期,就会生成getter/setter、equals、
hashcode、toString等方法。
注意:@Data注解中不包含全参构造方法,通常在实体类上,还会添加上:全参构造、无参构造;
Lombok会在编译时,会自动生成对应的java代码;
在使用lombok时,还需要安装一个lombok的插件(新版本的IDEA中自带)。
在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果。具体操作如下:

  1. 打开application.properties文件
  2. 开启mybatis的日志,并指定输出到控制台
    1
    2
    #指定mybatis输出日志的位置, 输出控制台
    mybatis.configuration.logimpl=org.apache.ibatis.logging.stdout.StdOutImpl

预编译SQL有两个优势:

  1. 性能更高
  2. 更安全(防止SQL注入)
    性能更高:预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句
    时,不会再次编译。(只是输入的参数不同)
    更安全(防止SQL注入):将敏感字进行转义,保障SQL的安全性
    SQL注入:由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    在Mybatis中提供的参数占位符有两种:${...} 、#{...}
    #{...}
    执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值
    使用时机:参数传递,都使用#{…}
    ${...}
    拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题
    使用时机:如果对表名、列表进行动态设置时使用 比如姓名张开头
    where name like '%${name}%'
    where name like concat('%',#{name},'%')
    解决:使用MySQL提供的字符串拼接函数:concat('%' , '关键字' , '%')
    注意事项:在项目开发中,建议使用#{...},生成预编译SQL,防止SQL注入安全。
    说明:#{...} 里面写的名称是对象的属性名
    默认情况下,执行插入操作时,是不会主键值返回的。如果我们想要拿到主键值,需要在Mapper
    接口中的方法上添加一个Options注解,并在注解中指定属性useGeneratedKeys=true和
    keyProperty=”实体类属性名”

实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。
解决方案:

  1. 起别名 在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样
  2. 结果映射 通过 @Results及@Result 进行手动结果映射
    @Results({@Result(column = “dept_id”, property = “deptId”),
    @Result(column = “create_time”, property = “createTime”),
    @Result(column = “update_time”, property = “updateTime”)})
    @Select(“select id, username, password, name, gender, image, job,
    entrydate, dept_id, create_time, update_time from emp where id=#{id}”)
    public Emp getById(Integer id);
  3. 开启驼峰命名
    1
    2
    # 在application.properties中添加:
    mybatis.configuration.map-underscore-to-camel-case=true
    Mybatis的开发有两种方式:
  4. 注解
  5. XML
    在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:
  6. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下
    (同包同名)
  7. XML映射文件的namespace属性为Mapper接口全限定名一致
  8. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。
    配置:XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致

使用XML映射文件后动态SQL规避bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<if> :用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
<if test="条件表达式">
要拼接的sql语句
</if>

<where>
where元素只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的
AND或OR

<set>
动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)

<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每
一次遍历使用的分隔符"
open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
</foreach>

我们可以对重复的代码片段进行抽取,将其通过 <sql> 标签封装到一个SQL片段,然后再通过
<include> 标签进行引用。
<sql> :定义可重用的SQL片段
<sql id="commonSelect">
select id, username, password, name, gender, image, job,
entrydate, dept_id, create_time, update_time from emp
</sql>
<include refid="commonSelect"/>
<include> :通过属性refid,指定包含的SQL片

需要引入的依赖

1
2
3
4
5
6
1.引入dom4j的依赖,用于解析XML文件
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- mybatis起步依赖 -->
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
    </dependency>
    <!-- mysql驱动包依赖 -->
    <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
    </dependency>
    <!-- spring单元测试 (集成了junit) -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
  2. 1
    2
    3
    4
    5
    6
    <!-- 在springboot的父工程中,已经集成了lombok并指定了版本号,故当前引入依赖
    时不需要指定version -->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    </dependency>
    IDEA中需要安装的插件
    1
    2
    3
    1.lombok(老版本需要)
    2.grep console
    3.mybatis x
    正式开始项目!
    后端开发人员:必须严格遵守提供的接口文档进行后端功能开发(保障开发的功能可以和前端对接)
    而在前后端进行交互的时候,我们需要基于当前主流的REST风格的API接口进行交互。
    在REST风格的URL中,通过四种请求方式,来操作数据的增删改查。
  • GET : 查询
  • POST :新增
  • PUT : 修改
  • DELETE :删除
    浏览器中所发起的所有的请求,都是GET方式的请求。
    前后端都在并行开发时,可以借助一些接口测试工具,比如:Postman,后端开发完对应的接口之后,对接口进行请求测试;前端开发过程中,获取到数据,测试页面的渲染展示。
    Snipaste 2025 04 14 19 18 46
    步骤:
  1. 准备数据库表
  2. 创建springboot工程,引入对应的起步依赖(web、mybatis、mysql驱动、lombok)
  3. 配置文件application.properties中引入mybatis的配置信息,准备对应的实体类
  4. 准备对应的Mapper、Service(接口、实现类)、Controller基础结构
    1