第四阶段:01Vue Cli、02spring、03springmvc、04Mybatis(Spring Test)

在这里插入图片描述

1、Vue CLI:Vue脚手架

——————————————————>       【ppt】      <—————————————————
——————————————————>DAY01-VUE Cli基础-01<—————————————————
0:前提:【关于Vue CLIVue.js(读音:/vjuː/, 类似于:view)是一套构建用户界面的渐进式框架
● Vue.js的官方网址是:https://cn.vuejs.org/index.html

需要注意:在使用传统模式开发的前端工程中,各个页面是相对独立的,
          甚至你可以在本地硬盘上找到对应的html文件并双击直接打开它,
          【而Vue CLI工程与一个Web应用程序一样,是需要启动服务才可以访问的】,
          一定程度上,它的上手难度会更大一些,但是在中大型应用程序的开发中,
          它在开发效率、管理和维护成本上有更大的优势

单页面应用的工程中需要一定的配置,对配置文件的位置也有一定的要求,
整体工程结果并不像传统的前端应用工程那么自由,所以,手动创建这种应用的成本较大,
而【Vue CLI则可以自动生成vue.js + webpack的工程模板,
    是为现代前端工作流提供了Batteries-included(自含全套工具集)的构建设置,
    只需要几分钟的时间甚至更短的时间就可以运行起来。】

由Vue CLI创建的工程,默认即是热部署的,
当你启动了这个工程(非打包后 的工程)的服务后,
在绝大部分情况下,修改源代码后并不需要重新启动服务,即可体验到修改后的内容。
——————————————————————————————————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————————————————————————
关于npm
npm的全称是:Node Package Manager,可译为“Node包管理器”。
在早期没有npm时,如果需要在前端工程中使用某些框架(例如jQuery、 Bootstrap等),
需要找到相关的官网或资源站点,要么下载这些框架的文件,要么使用cdn在线引用这些文件,
其过程相对烦琐,
因为这些框架的具体表现可能不只是一个文件,
如果下载,只能下载打包后的文件并自行解压缩,
如果在线引用,则需要自行添加多条源代码进行引用,
同时,
是这些框架也是在不停维护和更新的,即使开发人员在本地已经下载过这些框架文件,
当需要新版本的框架时,又需要重新下载,或者上网搜索最 新的cdn引用地址。

为了解决这个问题,I s a a c Z. S ch l u e t e r使用运行在Node.js之上的
JavaScript语言开发了npm,它允许配置一个仓库,并将大量框架的文件放在了npm仓库中,
并提供了一系列的管理命令,最终,开发人员只需要使用npm的命令,
就可以在线获取到框架文件。通常,框架的文件会自动的存储到工程目录下,
并在工程的配置文件中进行注册,开发人员只需要在主配置中进行导入及一些简单的配置就
可以开始使用这些框架。
● 一定程度上,npm与MavenGradle是比较相似的。
——————————————————————————————————————————————————————————————————————————————————————
搭建开发环境:

为了使用npm,首先需要下载Node.js安装包:
– https://nodejs.org/dist/v16.14.2/node-v16.14.2-x64.msi
– https://mirrors.nju.edu.cn/nodejs/latest-v16.x/node-v16.14.2-x64.msi
– https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/v16.14.2/node- v16.14.2-x64.msi
Node.js的安装过程没有特别的注意事项,
整个安装过程中都可以不必修改任何内容,
直至其自动安装完成安装Node.js(我的这个文件装在了"C:\Program Files\nodejs\"下)

1.此安装包会自动配置环境变量,安装完成后,可检查npm命令是否可用,
   在操作系统的命令提示符下执行以下命令即可:
(1)/1.右键windows图标,点击管理员(Windows PowerShell(管理员)),
      出现dos窗口后,
      显示【C:\WINDOWS\system32>】,
      在其后输入【npm -v;】查看版本
      ————>注意要用管理员权限去操作      
   /2.或者【windows+R 输入cmd调出dos窗口】输入指令也可以!——————建议用这种!
   注意:你必须在新的命令提示符窗口(安装完Node.js后再打开的窗口) 中运行此命令。

2.配置npm源
  nmp源(即npm仓库,称之为:registry)默认是境外服务器,
  在国内使用时访问速度较慢,通常在初次使用时,应该将npm源更换为国内的服务器地址,
  例如使用https://registry.npm.taobao.org作为npm源
(1)设置淘宝的npm源:【npm config set registry https://registry.npm.taobao.org】

(3)当配置成功后,可通过get命令可查看npm源:
   查看npm源:【npm config get registry】 按回车若显示出上边那个网址即可成功

3.安装Vue CLI脚手架:
(1)执行命令:【npm install -g @Vue/cli】
   注:-g表示全局安装,则当前操作系统中所有用户均可使用,且自动配置Vue环境变量。

(2)出现以下提示就是安装成功:
   added 897 packages in 1m
   npm notice
   npm notice New minor version of npm available! 8.5.0 -> 8.10.0
   npm notice Changelog: https://github.com/npm/cli/releases/tag/v8.10.0
   npm notice Run npm install -g npm@8.10.0 to update!
   npm notice
(3)若安装没有成功,则需要卸载后重新安装npm源:重复以上步骤!!!
(4)继续:查看vue版本号:【vue -V】   即可显示当前的Vue CLI版本号
——————————————————————————————————————————————————————————————————————————————————————

4.创建第1Vue CLI工程————>vue-project-01【ppt:DAY01-VUE Cli基础-01:第29页——46页】
(1)创建工程:把项目创建在该文件夹:Vue-Workspace 中:
             注:[cd..]的意思:可以退回到想创建上面文件夹的路径位置,
             若想在[F:\danei\softone\guobinjava> ]处创建,
             输入:【mkdir Vue-Workspace】  此时该文件夹创建成功,
(2)进入该文件夹:【cd Vue-Workspace(3)当进入Vue Workspace后,就可以创建工程了
   ①使用vue create命令即可创建Vue CLI工程,命令格式是:[vue create 工程名称]
    例如执行:【vue create vue-project-01】
   ②输入以上指令后,按下Enter键将准备创建工程,创建过程可能耗时较长,
    注意:不可反复按Enter键,否则会视为选择各设置选项的默认项
(4)创建过程中会有一些选项:
   最先提示的选项是Please pick a preset,
   表示“请选择一个预设项”,推荐选择【Manually select features】,表示 “手动选择”,
   通过键盘的上下箭头进行选择,选择到目标项后按下键盘的Enter键到下一步:
(5)接下来的选项是Check the features needed for your project,
   表示 “选择你的工程中需要使用的功能”,
   推荐在列表中选择【BabelRouterVuex】这3项,
   使用键盘的上下箭头移动,使用【空格】选中或取消选中,
   选择完成后按下键盘的Enter键到下一步:
(6)接下来的选项是Choose a version of Vue.js that you want to start the project with,
   表示“选择你的工程中希望使用的Vue.js的版本”,
   推荐选择【2.x】这项,选择到目标项后按下键盘的Enter键到下一步
(7)接下来的选项是Use history mode for router,
   表示“是否在路由中选 择历史模式”,
   推荐选择“是”,输入Y后按下键盘的Enter键到下一步 (提示信息中,Y是大写的,表示它是默认选项,不输入Y而直接按下 Enter键是等效的):
(8)接下来的选项是Where do you prefer placing config for Babel, ESLint, etc.?,
   表示“你习惯把一些配置信息存放在哪里?”,
   推荐选择 【In package.json】,即存放在package.json文件中,
   选择到目标项后按下键盘的Enter键到下一步
(9)最后的选项是Save this as a preset for future projects?,
   表示“是否 保存以上配置信息,作为后续将创建的新工程的预设?”,
   推荐选择 “否”,输入N后按下键盘的Enter键(提示信息中,N是大写的,表示它是默认选项,不输入N而直接按下Enter键是等效的
(10)至此,配置过程已经结束,接下来会自动创建工程,耗时取决于网络速度,
    通常需要30秒至5分钟左右
(11)出现以下即为成功:
     Successfully created project vue-project-01.
     系统会提示这两句:
       $ cd vue-project-01
       $ npm run serve
5.启动服务
(0)从现在开始,你可以使用IDEA或类似的开发工具来打开你的项目(vue-project-01),
   并使用idea中内置的【Terminal   :在idea中最下边的窗口栏点开即可】执行下面的命令,
   使用方式与命令提示符窗口完全相同,为保证你有足够的权限执行命令,
   你应该以管理员的身份运行IDEA(一般在idea中是默认的),
   否则,后续仍可能出现某些命令无法成功执行的问题。
(1)进入工程目录:执行命令:【cd vue-project-01】,
(2)执行【npm run serve】命令即可启动此工程的服务(过程中会编译打包此工程):
(3)当提示以下信息时,表示工程已成功启动:
     DONE  Compiled successfully in 7728ms                                          11:41:39
       App running at:
       - Local:   http://localhost:8080/
       - Network: http://172.18.8.11:8080/

(4)在浏览器访问上边系统提示的localhost网址,若页面中出现【Vue绿色图标】即为成功!!!

6.停止服务
(1)如果需要停止服务,在控制台使用Ctrl + C即可
   ● 提示:当按下Ctrl + C后,会提示"终止批处理操作吗(Y/N)?",输入Y即可停止服务!!!
           其实,当按下Ctrl + C后,服务已经被停止,
                 在此提示中无论选择YN结果都已经不重要了

7.重启服务
(1)在idea控制台重新输入【npm run serve】;系统提示出网址在浏览器访问即可!

8.若启动后该网址端口被占用
(1)第一种:可以百度找到该占用的端口号停止服务!
(2)第二种:可修改指定服务端的端口号
           此服务默认占用8080端口,如果端口已经被占用,
           将自动修改端口为 8081或继续向后递增端口号
           ● 如需手动修改端口号,在【工程根目录下】的【package.json】中修改scripts属性下serve的值:
	     【vue-cli-service serve --port 端口号】
(3)例如上面的【package.json】改为使用9090端口:
   则下次启动时会使用新设定的端口号:
   DONE  Compiled successfully in 7728ms                                          11:41:39
       App running at:
       - Local:   http://localhost:9090/
       - Network: http://172.18.8.11:9090/
   提示:即使显式的指定了端口号,如果指定的端口仍被其它服务占用,
         仍会自动在指定端口号的值的基础上递增,以使用新的端口号!
         例如显式的指定了使 用9090端口,如果9090被占用且9091可用时,会自动改为使用9091端口。
————————————————————————————————————————————————————————————————————————————
5.了解Vue CLI工程【ppt:DAY01-VUE Cli基础-01:第47页——65页】
————————————————————————————————————————————————————————————————————————————
6.运行流程概要【ppt:DAY01-VUE Cli基础-01:第66页——80页】
————————————————————————————————————————————————————————————————————————————
7.复制出新的工程:【vue-project-preset】:【ppt:DAY01-VUE Cli基础-01:第81页——83页】
————————————————————————————————————————————————————————————————————————————
8.复制出新的工程:【vue-project-02:】:【ppt:DAY01-VUE Cli基础-01:第84页——83页】


————————————————————————————————————————————————————————————————————————————
—— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —
—— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —
————————————————————————————————————————————————————————————————————————————


——————————————————>       【ppt】      <—————————————————
——————————————————>DAY02-VUE Cli基础-02<—————————————————
一、使用Element UI————>vue-project-02
   【一、1、   vue-project-02 安装Element UI】
   【一、202 vue-project-03 设计登录视图   】
   【二、101 启动vue-project-server-0.0.1.jar】
   【二、103 vue-project-04           】
   【三、03.(1)vue-project-051、vue-project-02————>安装Element UI【ppt:DAY02-VUE Cli基础-02:第10页】
   (1)配置ElementUI: 【cd vue-project-02、npm i element-ui -S(2)在页面中显示Element UI组件【ppt:DAY02-VUE Cli基础-02:第12——22页】

2、设计登录视图
01.vue-project-preset中:—>配置ElementUI【ppt:DAY02-VUE Cli基础-02:第24页】
 ①cd vue-project-preset
 ②npm i element-ui -S  或  npm install --save element-ui
 ③打开src/main.js文件 在上面导入:
    // main.js
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);
 ④在《package.json》文件里查看是否导入了elementUI的依赖:
   "element-ui": "^2.15.8",
   若有上边这句代码即安装成功!

02.复制vue-project-preset,得到【vue-project-03】,并根据前序说 明修改必要的配置
   【ppt:DAY02-VUE Cli基础-02:第24:步骤3————43页】
————————————————————————————————————————————————————————————————————————
二、使用axios
(1)关于axios ● axios是一个易用、简洁且高效的http库,主要用于发起HTTP请求,并获取响应的结果。
   ● axios的官方网址是:http://www.axios-js.com/
(2)axios的主要特点有:【ppt:DAY02-VUE Cli基础-02:第46页】
   – 从浏览器中创建 XMLHttpRequests
   – 从 node.js 创建 http 请求
   – 支持 Promise API
   – 拦截请求和响应
   – 转换请求数据和响应数据
   – 取消请求
   – 自动转换 JSON 数据
   – 客户端支持防御 XSRF
(3)axios基本使用【ppt:DAY02-VUE Cli基础-02:第47/48/49页】

1、安装axios:
01.使用axios与后端服务器交互【ppt:DAY02-VUE Cli基础-0252页】
(1)前提:首先找到并启动vue-project-server-0.0.1.jar
(2)补充:此工程的服务默认占用18080端口,如端口被占用,将无法启动此服务
        ①如果端口被占用,你可以通过netstat -ano |findstr 18080找出占用端口的进程的id,
          并通过taskkill /f /t /im 进程id来终止进程
        ②你可以使用java -jar 工程jar包文件命令启动此服务
        ③例如,把工程jar包文件放在D盘,并且当前位置也在D盘,直接执行命令即可:
           【java -jar vue-project-server-0.0.1.jar】
(3)配置axios:
 ①cd vue-project-03
 ②npm i axios -S
 ③cd fiveJava
 ④启动该服务:java -jar doc/note/01VueCli/vue-project-server-0.0.1.jar
 ⑤控制台出现一个画面即为成功:
          .   ____          _            __ _ _
         /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
        ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
         \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
          '  |____| .__|_| |_|_| |_\__, | / / / /
         =========|_|==============|___/=/_/_/_/
         :: Spring Boot ::                (v2.5.4)
        ⑤启动后,访问:http://localhost:18080/doc.html

02.在【vue-project-preset】配置axios:【ppt:DAY02-VUE Cli基础-0259页】
   当准备好后端服务后,接下来将开发Vue CLI工程
   ● 步骤1:由于axios也是常用库,
     在vue-project-preset中安装axios:
   ①cd vue-project-preset
   ②安装axios:npm i axios -S  或   npm install --save axios
   ③安装完成后,在vue-project-preset的main.js中添加导入axios:
     import axios from 'axios'
     Vue.prototype.axios = axios
   ④在《package.json》文件里查看是否导入了axios的依赖:
     "axios": "^0.27.2",
     若有上边这句代码即安装成功!
03.基于【vue-project-preset】【复制出新的工程vue-project-04】:
   :进行【ppt:DAY02-VUE Cli基础-0261页——85页】
     注意:测试【04工程时,也要打开后端服务才可!
          :doc/note/java -jar doc/note/vue-project-server-0.0.1.jar】
————————————————————————————————————————————————————————————————————————
三、Vue CLI的嵌套路由【ppt:DAY02-VUE Cli基础-02:第86页开始————第121页结束】
   ————>03.(1)vue-project-05:【ppt:DAY02-VUE Cli基础-0299页】<————

01.关于嵌套路由     【ppt:DAY02-VUE Cli基础-0287页————91页】

02.嵌套路由的使用方式【ppt:DAY02-VUE Cli基础-0291页————95页】

03.设计带嵌套路由的多个视图【ppt:DAY02-VUE Cli基础-0296页开始】
(1)基于vue-project-preset【复制出新的工程vue-project-05】,
   并在配置文件中修改必要参数(工程名称等):
   :【ppt:DAY02-VUE Cli基础-0299页】
(2)进行【ppt:DAY02-VUE Cli基础-0286-[99]-121页】

01.前提:【关于Vue CLI】

Vue.js(读音:/vjuː/, 类似于:view)是一套构建用户界面的渐进式框架
● Vue.js的官方网址是:https://cn.vuejs.org/index.html

需要注意:在使用传统模式开发的前端工程中,各个页面是相对独立的,
          甚至你可以在本地硬盘上找到对应的html文件并双击直接打开它,
          【而Vue CLI工程与一个Web应用程序一样,是需要启动服务才可以访问的】,
          一定程度上,它的上手难度会更大一些,但是在中大型应用程序的开发中,
          它在开发效率、管理和维护成本上有更大的优势

单页面应用的工程中需要一定的配置,对配置文件的位置也有一定的要求,
整体工程结果并不像传统的前端应用工程那么自由,所以,手动创建这种应用的成本较大,
而【Vue CLI则可以自动生成vue.js + webpack的工程模板,
    是为现代前端工作流提供了Batteries-included(自含全套工具集)的构建设置,
    只需要几分钟的时间甚至更短的时间就可以运行起来。】

由Vue CLI创建的工程,默认即是热部署的,
当你启动了这个工程(非打包后 的工程)的服务后,
在绝大部分情况下,修改源代码后并不需要重新启动服务,即可体验到修改后的内容。

02.npm(使用命令即可获取框架文件)

关于npm
npm的全称是:Node Package Manager,可译为“Node包管理器”。
在早期没有npm时,如果需要在前端工程中使用某些框架(例如jQuery、 Bootstrap等),
需要找到相关的官网或资源站点,要么下载这些框架的文件,要么使用cdn在线引用这些文件,
其过程相对烦琐,
因为这些框架的具体表现可能不只是一个文件,
如果下载,只能下载打包后的文件并自行解压缩,
如果在线引用,则需要自行添加多条源代码进行引用,
同时,
是这些框架也是在不停维护和更新的,即使开发人员在本地已经下载过这些框架文件,
当需要新版本的框架时,又需要重新下载,或者上网搜索最新的cdn引用地址。

为了解决这个问题,IsaacZ.Schlueter使用运行在Node.js之上的
JavaScript语言开发了npm,它允许配置一个仓库,并将大量框架的文件放在了npm仓库中,
并提供了一系列的管理命令,最终,开发人员只需要使用npm的命令,
就可以在线获取到框架文件。

通常,框架的文件会自动的存储到工程目录下,
并在工程的配置文件中进行注册,开发人员只需要在主配置中进行导入及一些简单的配置就
可以开始使用这些框架。
● 一定程度上,npm与MavenGradle是比较相似的。

03.搭建npm开发环境:

搭建开发环境:

为了使用npm,首先需要下载Node.js安装包:
– https://nodejs.org/dist/v16.14.2/node-v16.14.2-x64.msi
– https://mirrors.nju.edu.cn/nodejs/latest-v16.x/node-v16.14.2-x64.msi
– https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/v16.14.2/node- v16.14.2-x64.msi
Node.js的安装过程没有特别的注意事项,
整个安装过程中都可以不必修改任何内容,
直至其自动安装完成
安装Node.js

1.此安装包会自动配置环境变量,安装完成后,可检查npm命令是否可用,
   在操作系统的命令提示符下执行以下命令即可:
(1)/1.右键windows图标,点击管理员,出现窗口后,输入【npm -v;】查看版本
   /2.或者【windows+R 输入cmd调出dos窗口】输入指令也可以!——————建议用这种!
   注意:你必须在新的命令提示符窗口(安装完Node.js后再打开的窗口) 中运行此命令。

2.配置npm源
  nmp源(即npm仓库,称之为:registry)默认是境外服务器,
  在国内使用时访问速度较慢,通常在初次使用时,应该将npm源更换为国内的服务器地址,
  例如使用https://registry.npm.taobao.org作为npm源
(1)设置淘宝的npm源:【npm config set registry https://registry.npm.taobao.org】

(2)当配置成功后,可通过get命令可查看npm源:
   查看npm源:【npm config get registry】 按回车若显示出上边那个网址即可成功

例:
在dos窗口输入以下指令:
D:\soft\01Java\00soft>cd D:\soft\01Java\00soft\ruanjian\nodejs\npm
_nodejs>  npm config set registry https://registry.npm.taobao.org  

D:\soft\01Java\00soft\ruanjian\nodejs\npm_nodejs> npm config get registry
https://registry.npm.taobao.org/

————————————————————————————————————————————————————————————————————————


3.安装Vue CLI脚手架:
(1)执行命令:【npm install -g @Vue/cli】
   注:-g表示全局安装,则当前操作系统中所有用户均可使用,且自动配置Vue环境变量。

(2)出现以下提示就是安装成功:
   added 897 packages in 1m
   npm notice
   npm notice New minor version of npm available! 8.5.0 -> 8.10.0
   npm notice Changelog: https://github.com/npm/cli/releases/tag/v8.10.0
   npm notice Run npm install -g npm@8.10.0 to update!
   npm notice
(3)若安装没有成功,则需要卸载后重新安装npm源:重复以上步骤!!!
(4)继续:查看vue版本号:【vue -V】   即可显示当前的Vue CLI版本号

04.创建第1个Vue CLI工程

4.创建第1Vue CLI工程————>vue-project-01【ppt:DAY01-VUE Cli基础-01:第29页——46页】
(1)创建工程:把项目创建在该文件夹:Vue-Workspace 中:
             注:[cd..]的意思:可以退回到想创建上面文件夹的路径位置,
             若想在[F:\danei\softone\guobinjava> ]处创建,
             输入:【mkdir Vue-Workspace】  此时该文件夹创建成功,
(2)进入该文件夹:【cd Vue-Workspace(3)当进入Vue Workspace后,就可以创建工程了
   ①使用vue create命令即可创建Vue CLI工程,命令格式是:[vue create 工程名称]
    例如执行:【vue create vue-project-01】
   ②输入以上指令后,按下Enter键将准备创建工程,创建过程可能耗时较长,
    注意:不可反复按Enter键,否则会视为选择各设置选项的默认项
(4)创建过程中会有一些选项:
   最先提示的选项是Please pick a preset,
   表示“请选择一个预设项”,推荐选择【Manually select features】,表示 “手动选择”,
   通过键盘的上下箭头进行选择,选择到目标项后按下键盘的Enter键到下一步:
(5)接下来的选项是Check the features needed for your project,
   表示 “选择你的工程中需要使用的功能”,
   推荐在列表中选择【BabelRouterVuex】这3项,
   使用键盘的上下箭头移动,使用【空格】选中或取消选中,
   选择完成后按下键盘的Enter键到下一步:
(6)接下来的选项是Choose a version of Vue.js that you want to start the project with,
   表示“选择你的工程中希望使用的Vue.js的版本”,
   推荐选择【2.x】这项,选择到目标项后按下键盘的Enter键到下一步
(7)接下来的选项是Use history mode for router,
   表示“是否在路由中选 择历史模式”,
   推荐选择“是”,输入Y后按下键盘的Enter键到下一步 (提示信息中,Y是大写的,表示它是默认选项,不输入Y而直接按下 Enter键是等效的):
(8)接下来的选项是Where do you prefer placing config for Babel, ESLint, etc.?,
   表示“你习惯把一些配置信息存放在哪里?”,
   推荐选择 【In package.json】,即存放在package.json文件中,
   选择到目标项后按下键盘的Enter键到下一步
(9)最后的选项是Save this as a preset for future projects?,
   表示“是否 保存以上配置信息,作为后续将创建的新工程的预设?”,
   推荐选择 “否”,输入N后按下键盘的Enter键(提示信息中,N是大写的,表示它是默认选项,不输入N而直接按下Enter键是等效的
(10)至此,配置过程已经结束,接下来会自动创建工程,耗时取决于网络速度,
    通常需要30秒至5分钟左右
(11)出现以下即为成功:
     Successfully created project vue-project-01.
     系统会提示这两句:
       $ cd vue-project-01
       $ npm run serve
       
       
5.启动服务
(0)从现在开始,你可以使用IDEA或类似的开发工具来打开你的项目(vue-project-01),
   并使用idea中内置的【Terminal   :在idea中最下边的窗口栏点开即可】执行下面的命令,
   使用方式与命令提示符窗口完全相同,为保证你有足够的权限执行命令,
   你应该以管理员的身份运行IDEA(一般在idea中是默认的),
   否则,后续仍可能出现某些命令无法成功执行的问题。
(1)进入工程目录:执行命令:【cd vue-project-01】,
(2)执行【npm run serve】命令即可启动此工程的服务(过程中会编译打包此工程):
(3)当提示以下信息时,表示工程已成功启动:
     DONE  Compiled successfully in 7728ms                                          11:41:39
       App running at:
       - Local:   http://localhost:8080/
       - Network: http://172.18.8.11:8080/
(4)在浏览器访问上边系统提示的localhost网址,若页面中出现【Vue绿色图标】即为成功!!!


6.停止服务
(1)如果需要停止服务,在控制台使用Ctrl + C即可
   ● 提示:当按下Ctrl + C后,会提示"终止批处理操作吗(Y/N)?",输入Y即可停止服务!!!
           其实,当按下Ctrl + C后,服务已经被停止,
                 在此提示中无论选择YN结果都已经不重要了


7.重启服务
(1)在idea控制台重新输入【npm run serve】;系统提示出网址在浏览器访问即可!

05.修改指定服务端的端口号

8.若启动后该网址端口被占用
(1)第一种:可以百度找到该占用的端口号停止服务!
(2)第二种:可修改指定服务端的端口号
           此服务默认占用8080端口,如果端口已经被占用,
           将自动修改端口为 8081或继续向后递增端口号
           ● 如需手动修改端口号,在【工程根目录下】的【package.json】中:
             修改scripts属性下serve的值:【vue-cli-service serve --port 端口号】
(3)例如上面的【package.json】改为使用9090端口:
   则下次启动时会使用新设定的端口号:
   DONE  Compiled successfully in 7728ms                                          11:41:39
       App running at:
       - Local:   http://localhost:9090/
       - Network: http://172.18.8.11:9090/
   提示:即使显式的指定了端口号,如果指定的端口仍被其它服务占用,
         仍会自动在指定端口号的值的基础上递增,以使用新的端口号!
         例如显式的指定了使 用9090端口,如果9090被占用且9091可用时,会自动改为使用9091端口。

06.Vue CLI 工程结构 详解:

工程结构:
[.idea]:使用IntelliJ IDEA打开工程时会产生此文件夹,
         其中的文件都是IntelliJ IDEA管理此工程时将使用到的,不手动管理,即使删除此文件夹,
         IntelliJ IDEA也会按需自动创建此文件夹及其下必要的文件
         
[node_modules]:工程中使用到的模块,也可以理解为工程的依赖项, 
                例如BabelVue等框架的文件,不手动管理 
                – 需要注意:如果是从Git或其它位置复制得到的工程,可能没有此文件夹,
                  需要运行 npm install,
                  否则将无此文件夹及其下的各依赖项,工程将无法正常启动 
                – 本课程提供的资源中,各练习案例也不包含此文件夹,
                  均需要在各工程目录下执行 npm install后才可以正常运行

[public]:工程被编译打包后仍会保留的内容(文件内容可能会在编译打 包过程中修改) 
          – 你可以自行在此文件夹下创建子级文件夹,用于存放静态资源,例如图片等 
public/index.html:默认的主页,通常不修改其内容。 

[src]:页面源代码,除工程配置以外的所有开发都在此文件夹下

[src/assets]:资源文件夹,通常存放图片等 
   		      – 注意:仅不需要被程序动态控制的图片放在此处
   		      
[src/components]:视图组件,通常是可以被其它各页面复用的,是各个 .vue文件
——————————————————————————————————————————————————————————————————————————
路由:
[src/router]:路由控制 
src/router/index.js:路由配置文件,除非工程中页面数量较大,或路由关系复杂,
					 否则使用这1个文件进行路由管理即可
● Vue CLI应用是单页面的,也就是说,
  在Vue CLI工程的设计思想中,页面只有1个,只不过页面中的内容是可以动态调整的,
  所以,无论是使用不同的URL进行访问,还是页面中触发某个事件后,虽然用户看到了不同的页面效果,
  但是,用户看到的其实永远只是1个页面,只是页同中的内容不同而已,
  这也就是视图组件源文件中使用<template>节点设计页面元素的原因(template释义为:模板)。
  
● Vue CLI工程使用路由控制 单页面中显示的元素,
● 在此文件中,需要重点关注import语句和routes常量。 
● routes常量是一个数组,数组元素是JSON对象,此JSON对象中主要配 置的属性有: 
   – path:路径,即URL路径,每个JSON对象的此属性值必须是唯一的,
           Vue将根据用 户访问的URL加载对应的视图组件 
   – name:名称,可忽略,如果配置此属性,则每个JSON对象的此属性值必须是唯一的
   – component:视图组件,当访问的URL匹配path时,此视图组件将被显示,
   			   在配置此属性的值时,需分析此视图组件是否为动态导入(懒加载)的 
   			   – 默认即导入:当客户端首次访问此服务时,就会导入此视图组件,
   			   			   需通过此文件顶部通过import语句导入,
   			   			   且component属性的值就是import时指定的视图组件名,
   			   			   每个工程中通过此方式导入的视图组件应该非常少 
   			   – 动态导入:取值是使用ES6中的import()返回的Promise对象,
   			   		     相比之下,import语句 是默认即导入的,而import()函数是动态导入的,
   			   		     以避免客户端首次访问此服务时就加 载了大量的视图组件,
   			   		     每个工程中的绝大部分视图组件都应该是这种方法导入的 
   			   – 注意:以上语法格式需要Babel支持,否则将无法正常编译打包

提示:此JSON对象还有更多属性配置,将在后续的应用中补充。
——————————————————————————————————————————————————————————————————————————
[src/store]:全局共享的内存中的数据
src/store/index.js:暂不关注
——————————————————————————————————————————————————————————————————————————
视图组件:
[src/views]:视图组件,通常,每个页面在此文件夹都应该有1个对应的文件,
			 可引用[src/components]下的组件 
			 
src/App.vue:默认的主页视图组件 ;  所有以 .vue 为后缀的都是视图组件,
             可以看到此文件中主要有2个节点: 
		  	 – <template>节点:用于设计页面元素,其内部代码可以是标准HTML代码,
                              也可以是Vue或其它库(例如Element UI)支持的代码 
			 – <style>节点:用于定义样式,语法规则与传统前端技术中完全相同
● 需要注意:在<template>必须有且仅有1个直接子节点(通常是<div>节 点),否则将无法通过编译!

(1)<HelloWorld>节点中配置了名为msg值为"Welcome to Your Vue.js App"的属性,
   而components/HelloWorld.vue中则使用 {{ msg }}应用了传入的值,
   这是在Vue CLI工程中封装视图组件并引用时传参的典型方式 
(2)在components和views下的.vue文件的设计方式是基本相同的,
   区别在于components下的视图组件应该是可以被引用的 
(3)所有视图组件的源代码被修改后,都不需要重启服务,在浏览器可以看到最新的内容,
   这是————>Vue CLI的热部署(热更新)实现的
——————————————————————————————————————————————————————————————————————————


src/main.js:工程的主js文件,通常用于导入工程中将使用的其它库 
			 – 此文件中所有import语句必须在最上方位置,各import语句不区分先后顺序

.gitigore:用于配置使用Git提交工程时将忽略的文件和文件夹

bable.config.js:Babel的配置文件,在不熟悉的情况下不要手动修改 此文件的配置

package.json:工程的管理配置文件,相当于Maven项目的pom.xml,
              在不熟悉的情况下不要手动修改此文件的配置,可能需要关注的主要有: 
              – scripts:支持的2个npm命令参数,例如npm run serve、npc run build 
              – dependencies:运行时依赖项 
              – devDependencies:开发依赖项,将不参与打包

package-lock.json:工程的管理配置文件,在不熟悉的情况下不要手 动修改此文件的配置 

README.md:工程的说明文档,用于开发人员编写如何使用、注意事项、更新日志等内容,
          使用IntelliJ IDEA首次打开工程时,会自动打开此文件,
          此文件的默认内容中提示了工程的运行命令

vue.config.js:Vue的配置文件,在不熟悉的情况下不要手动修改此文件的配置

07.运行流程概要

● 在启动服务的过程中,会加载相关配置,例如配置的端口号等 
● 此过程通常是无感的,不需要过多关注,只要最终能提示访问此服务的 URL即可视为成功,
  如果启动服务失败,则会提示错误信息
1.默认的主页是public/index.html(单页面应用中也只有这1个页面), 
  则加载此页面,注意此页面中的<div id="app"></div>
2.根据main.js中的代码,其导入了./App.vue,并在挂载id为app的元素时使用./App.vue进行渲染,
  可简单理解为<div id="app></div>"就是 App.vue组件的全部内容
3.App.vue的页面设计中,有<nav>节点和<router-view/>节点,
  其中, <nav>中的内容类似于(但不完全相同)HTML中的一系列<a>标签实现的超链接,
  所以,在页面中会显示Home | About字样,
  且HomeAbout都 是可点击的超链接,与传统超链接不同,
  如果当前访问路径与<router- link>配置的to属性匹配时,
  会根据路由配置,将匹配的视图组件显示在 <router-view/>位置。
4.由于以上第1<router-link>节点配置的to属性是/,
   匹配当前访问的 http://localhost:8080为根路径的请求,
   则在/router/index.js中根据/匹 配视图组件,也就是HomeView.vue:
5.所以,当访问http://localhost:8080时,
  在App.Vue<router-view/> 处实际显示的由HomeView渲染的效果,
  在HomeView.vue中,仅使用 <img>标签显示了Vue的logo图片,
  然后,
  就通过HelloWorld组件渲染剩余的部分,
  并且,在引用HelloWorld组件时,还传入了名为msg的参数:
6.最后,由HelloWorld.vue完成页面中剩余部分的渲染,
  并通过{{ msg }} 语法将HomeView.vue中传入的"Welcome to Your Vue.js App"显示在页面中:
7.当在浏览器中点击About时,由于配置的<router-link>节点的to属性是 /about,
  所以,在浏览器的地址栏中可以看到URL变成了 http://localhost:8080/about:
8.由于在router/index.js中配置了/about路径映射的组件是动态导入../view/AboutView.vue,
  则此时会由AboutView.vue完成在App.vue 中<router-view/>处的渲染

08.复制出新的工程

(1)由于创建工程时,每次都需要选择预设项,并且需要下载大量的依赖模块, 导致创建工程耗时较长,
   在入门级的学习阶段,可能需要频繁创建不同的工程,以进行不同的尝试,为了提高创建工程的效率,
   通常可以创建好新工程后,将它保留下来,当后续需要新的工程时,将此工程复制一份即可得到新的工具。
   
(2)提示:
   尽管每次创建工程时选择的预设项都相同,
   仍不建议在创建过程的最后一步:Save this as a preset for future 
   							projects?(是否保存以上 配置信息,作为后续将创建的新工程的预设?)
   这一项选择为“是”,随 着学习的深入,你的预设项可能会发生一些调整。


● 【需求】创建出新的工程,创建完成后不做任何修改,并将其复制,得到另一个新工程 
         – 你可能需要删除一些不必要的问题,甚至可能引发错误的文件 
         – 你可能需要了解某些配置值,在新项目中需修改这些值,否则可能引发错误
步骤1. 通过此前创建工程的步骤,创建新的工程,工程名可自定义,例如: vue-project-preset
步骤2:将vue-project-preset复制为vue-project-02:
步骤3:由于工程中的部分配置中保存了工程名称,所以还需要对其中的一 些文件内容进行调整: 
      – 删除.i – dea文件夹 如果你曾经使用IntelliJ IDEA打开过vue-project-preset才需要进行此操作 
      – 如其中的文件内部报错,可以无视 
      – 删除.git文件夹————>如果你认为不需要使用Git管理此工程,可以不进行此操作 
      – 将package.json中的原工程名称改成新工程名称
      – 将package-lock.json中的原工程名称改成新工程名称
      — 将README.md中的标题修改为新工程名称(非必须)

09.使用Element UI

Element UI:
是一套采用 Vue 2.0 作为基础框架实现的组件库,
一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库,
提供了配套设计资源,帮助网站快速成型。 
● Element UI的官方网址是:https://element.eleme.cn/

应该重点关注Element UI可以做到什么, 例如有哪些样式,各组件的核心属性等用法,
然后结合官方文档 (https://element.eleme.cn/#/zh-CN/component/)进行开发。


安装Element UI
1.首先进入工程文件夹(如果使用IDEA打开了此工程,直接点击IDEA下面的 Terminal 即可):
  cd vue-project-02       
  npm i element-ui -S
2.然后使用npm命令安装Element UI,
  以下2条命令是等效的(注意:以下命令区分大小写,例如最后的-S的字母是大写的)
  npm i element-ui -S
  npm install --save element-ui
3.安装完成后,在工程的src/main.js中导入并使用Element UI// main.js
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    Vue.use(ElementUI);
    至此,已经可以在工程中使用Element UI

10.使用axios:发起HTTP请求,并获取响应的结果

axios是一个易用、简洁且高效的http库,主要用于发起HTTP请求,并获取响应的结果。
● axios的官方网址是:http://www.axios-js.com/

axios的主要特点有: 
– 从浏览器中创建 XMLHttpRequests 
– 从 node.js 创建 http 请求 
– 支持 Promise API 
– 拦截请求和响应 
– 转换请求数据和响应数据 
– 取消请求
– 自动转换 JSON 数据 
– 客户端支持防御 XSRF

1、安装axios:
01.使用axios与后端服务器交互【ppt:DAY02-VUE Cli基础-0252页】
(1)前提:首先找到并启动vue-project-server-0.0.1.jar
(2)补充:此工程的服务默认占用18080端口,如端口被占用,将无法启动此服务
        ①如果端口被占用,你可以通过netstat -ano |findstr 18080找出占用端口的进程的id,
          并通过taskkill /f /t /im 进程id来终止进程
        ②你可以使用java -jar 工程jar包文件命令启动此服务
        ③例如,把工程jar包文件放在D盘,并且当前位置也在D盘,直接执行命令即可:
           【java -jar vue-project-server-0.0.1.jar】
(3)配置axios:
 ①cd vue-project-03
 ②npm i axios -S
 ③cd fiveJava
 ④启动该服务:java -jar doc/note/01VueCli/vue-project-server-0.0.1.jar
 ⑤控制台出现一个画面即为成功:
          .   ____          _            __ _ _
         /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
        ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
         \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
          '  |____| .__|_| |_|_| |_\__, | / / / /
         =========|_|==============|___/=/_/_/_/
         :: Spring Boot ::                (v2.5.4)
        ⑤启动后,访问:http://localhost:18080/doc.html

02.在【vue-project-preset】配置axios:【ppt:DAY02-VUE Cli基础-0259页】
   当准备好后端服务后,接下来将开发Vue CLI工程
   ● 步骤1:由于axios也是常用库,在vue-project-preset中安装axios:
           ①cd vue-project-preset
           ②安装axios:npm i axios -S  或   npm install --save axios
           ③安装完成后,在vue-project-preset的main.js中添加导入axios:
             import axios from 'axios'
             Vue.prototype.axios = axios
           ④在《package.json》文件里查看是否导入了axios的依赖:
             "axios": "^0.27.2",
             若有上边这句代码即安装成功!

11.Vue CLI的嵌套路由

由于Vue CLI工程是单页面的,
为了保证能显示各式各样的页面,
则需要将页面的整个区域都设计为<router-view/>,
然后根据URL不同,加载不同的视图组件(.vue文件)
但是,即使将整个页面的显示区域作为一个<router-view/>,多个页面仍可能存在共用的部分,
(1)如果要设计为以上风格,则页面中的右侧最大的区域也必须是一个 <router-view/>,
    就会形成最外部(页面所有部分)是<router-view/>的同时,
    内部还有一个<router-view/>(右侧的大区域),在配置路由时, 就需要使用到嵌套路由的做法了。 
(2)当然,如果以上设计风格中右侧区域中仍有各页面均显示的部分,还可以继续在其内部进行嵌套,
   通常,并不建议嵌套太多层次。


嵌套路由的使用方式:
嵌套路由则需要在原路由的某对象中添加children属性,此属性仍是一个数组,
其内部的配置方式与routes常量是相同的,例如:
const routes = [ {
	path: '/admin', 
	component: () => import('../views/AdminView.vue'), 
	children: [ 
	  {path:'user/list' component:()=>import('../views/admin/UserListView.vue')},
	  {path:'user/add-new' component:()=>import('../views/admin/UserAddNewView.vue')} 
	          ] 
} ];
提示:以上children下的配置中,各path没有使用/作为第1个字符,则表示在父级路径的基础上增加,
     例如配置为user/list,由于父级配置了 /admin,则完整路径为/admin/user/list

12.小结

– 理解Vue CLI的单页面设计思想 

– 掌握安装Node.js、配置npm源、安装Vue CLI、创建Vue CLI工程、在Vue CLI工程 中安装常用框架 
   – 你应该自已做一些笔记,把相关的命令及注意事项记下来,而不是死记硬背 
   
– 认识Vue CLI的工程的目录结构 

– 掌握.vue视图组件的基本开发
  – 认识此类文件的结构及特点
  
– 掌握Vue CLI中路由的配置 包括一般路由和嵌套路由 

– 能从Element UI官网或相关网站找到你所需的(或相似的)页面设计,并处理成你需要的样子
   – 永远不要死记硬背标签、属性等,而是应该尽可能多的使用各种标签,
     了解它们的特点,并结合官方文档去使用,从而积累经验 
     
– 掌握使用axios发送请求,并处理响应的结果

————————————————————————————

2、spring

——————————————————>       【ppt】      <—————————————————
——————————————————>DAY03-SPRING框架基础<—————————————————
————————————————————————————————————————————————————————————————————————
一、Spring框架基础

1、关于框架【ppt:DAY03-SPRING框架基础:4页——6页】
(1)框架(Framework)一个框架是一个可复用的设计构件,它规定了应用的体系结构,
   阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,
   表现为一组抽象类以及其实例之间协作的方法,它为构件复用提供了上下文(Context)关系。
(2)...
(3)...
(4)...
————————————————————————————————————————————————————————————————————————
2、关于Spring框架
01.关于Spring框架【ppt:DAY03-SPRING框架基础:7页——11页】
(1)Spring框架主要解决了创建对象、管理对象的问题。
(2)在开发实践中,Spring框架的核心价值在于:开发者可以通过Spring框架提供的机制,
   将创建对象、管理对象的任务交给Spring来完成,以至于开发者不必再关心这些过程,
   当需要某个对象时,只需要通过Spring 获取对象即可。
   – Spring框架也经常被称之为:Spring容器
(3)Spring框架还很好的支持了AOP,此部分将在后续课程中再介绍。
(4)创建对象、类与类之间是存在依赖关系的(UserController依赖UserMapper)
   .................................................
(5)在开发实践中,有许多类型的对象、配置值都需要常驻内存、需要有唯一性,或都需要多处使用,
   自行维护这些对象或值是非常繁琐的,通过 Spring框架可以极大的简化这些操作。
————————————————————————————————————————————————————————————————————————
02.Maven工程中使用Spring【ppt:DAY03-SPRING框架基础:12页】
   ● 当某个项目需要使用Spring框架时,推荐使用Maven工程。
   ● 使用Spring框架所需的依赖项是 spring-context,依赖代码为:
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.14</version>
   </dependency>
   – 以上代码中版本号可按需调整
————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————

二、【102.创建spring-01】【103.创建spring-020、前提:——————>通过Spring管理对象【ppt:DAY03-SPRING框架基础:13页——56页】
00.创建spring-01工程:
(0)创建一个空的maven,把以后创建的spring工程放进此maven:
(1)创建时:【选择分组Groupld:cn.tedu  工程Artifactld:spring-01(2)打开【spring-01】的【pom.xml】添加【spring依赖项】
      <!--添加Spring框架依赖项:-->
      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>5.3.14</version>
          </dependency>
      </dependencies>
(3)点击刷新maven按钮即可

01.————————>创建对象的方式【ppt:DAY03-SPRING框架基础:14页】
(1)如果需要Spring管理对象,就必须先创建对象,然后Spring获取到对象 才可以进行管理
(2)【被Spring管理的对象,通常也称之为Spring Bean(3)创建对象的方式有2种:
   – 通过@Bean方法
   – 通过组件扫描
————————————————————————————————————————————————————————————————————————
1、通过@Bean方法创建对象【ppt:DAY03-SPRING框架基础:15页——22页】
(1)在【spring-01】里【创建cn.tedu.spring.SpringBeanFactory类】
(2)在类中添加方法,方法的返回值类型就是你希望Spring创建并管理的对象的类型,
   并在此方法中自行编写返回有效对象的代码
(3)在此类上添加@Configuration注解
(4)在此方法上添加@Bean注解
    @Bean
    public Random random(){
        return new Random();
    }
(5)测试运行的编码步骤:
   – 创建任意类(SpringBeanFactory),
     在类中添加main()方法,将通过此方法测试运行,以观察运行效果
   – 如果你已经掌握JUnit的用法,且添加了JUnit依赖,也可以使用JUnit测试
   – 创建AnnotationConfigApplicationContext类型的对象,
     并使用添加了 @Configuration注解的类作为构造方法参数
   – 这是ApplicationContext接口的实现,通常称之为Spring的应用程序上下文
   – 调用AnnotationConfigApplicationContext对象的getBean()方法,
     以获取@Bean 注解方法返回的对象 – 自行测试获取的对象
   – 调用AnnotationConfigApplicationContext对象的close()方法,以关闭
(6)测试运行:
public class SpringRunner {
    public static void main(String[] args) {
        //1.让spring跑起来:
        /*
        AnnotationConfigApplicationContext:用于加载Spring的配置,是Spring的容器
        注意:需要在构造方法中添加上面的参数,否则就不会加载SpringBeanFactory类中内容
        */
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(SpringBeanFactory.class);

        //2.从Spring中获取对象
        //后边的参数是?——>默认的bean name就是@Bean方法的名称
        Random random = (Random) ac.getBean("random");

        //3.测试使用
        System.out.println(random);//java.util.Random@35fc6dc4

        //4.结束
        ac.close();

    }
}
(7)关于以上代码,你需要知道(1/3):【ppt:DAY03-SPRING框架基础:19页——21页】
   1)
   ①在AnnotationConfigApplicationContext的构造方法中,
     应该将 SpringBeanFactory.class作为参数传入,
     否则就不会加载SpringBeanFactory类 中内容
     – 实际上,在以上案例中,SpringBeanFactory类上的@Configuration注解
       并不是必须的, 但@Bean方法的使用规范是将其声明在@Configuration类中
   ②在getBean()时,传入的字符串参数"random"SpringBeanFactory类中的@Bean 方法的名称
   ③在SpringBeanFactory类中的方法必须添加@Bean注解,其作用是使得Spring框架自动调用此方法,
     并管理此方法返回的结果
   2)..................
   3)..................
————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————
2、通过组件扫描创建对象【ppt:DAY03-SPRING框架基础:23页——30页】
(1)创建spring-02————>导入spring依赖:......................
(2)打开【spring-01】的【pom.xml】添加【spring依赖项】
(3)编码步骤:
   – 自行创建某个类,例如创建cn.tedu.spring.UserMapper类,
     并在类的声明之前添 加@Component注解
   – 自行创建某个类,例如创建cn.tedu.spring.SpringConfig类,
     并在类的声明之前添 加2个注解:
     – @Configuration@ComponentScan,且在注解参数指定包名为cn.tedu.spring
(4)测试运行的编码步骤:
   – 与前序编写测试运行代码的方式相同
   – 调用getBean()获取对象时,传入的beanName是:
     将UserMapper的类名首字母改为小写,即userMap
(5)测试【SpringRunner类】运行:...................
(6)1)关于以上代码,你需要知道(1/3):
     – 使用@ComponentScan配置的是执行组件扫描的根包,
       当创建 AnnotationConfigApplicationContext对象时,
       由于传的SpringConfig添加了此 注解,则Spring框架会扫描所配置的包,
       如果包中有组件类,Spring框架就会创建 组件类的对象并管理
     – UserMapper类必须在@ComponentScan注解配置的包中,否则Spring框架不会知道此类的存在
     – 在UserMapper上的@Component表示此类是个“组件”,如果无此注解,
       Spring 框架不会创建此类的对象
   2)关于以上代码,你需要知道(2/3):
     – 在@ComponentScan中配置的包是执行组件扫描的“根包(basePackage)”,
       在执行时,会扫描此包及其下所有子孙包,例如配置为cn.tedu时,
       如果存在cn.tedu.spring、cn.tedu.mybatis、cn.tedu.boot、cn.tedu.boot.mapper、
       cn.tedu.boot.controller等包,则这些包都会被扫描
       – 你甚至可以把根包配置为cn,也可以完全扫描到以上列举的包,但并不推荐这么做,
         毕竟你的开发环境中的其它库中的类也是项目的一部分,例如依赖的第三方框架或工具等,
         如果这些框架或工具的包名的第1级也是cn,也会被扫描到,尽管不确实是否会导致意外的问题,
         但这种做法肯定是不对的
   3)关于以上代码,你需要知道(3/3):
     – 当getBean()时,由Spring创建的组件类的对象,默认的beanName都是将首字母改为小写
        – 以上规则仅适用于:类名中的第1个字母是大写,且第2个字母是小写的情况,
          如果类名不符合这种情况,则getBean()时传入的名称就是类名(与类名完全相同的字符串)
     – 你可以配置@Component注解的参数以指定beanName
     – 在创建对象的过程中,Spring会自动调用构造方法来创建对象,其中用到了反射机制,
       即使构造方法是私有的,也不影响调用
       – Spring在许多实现上都使用了反射机制,基本上不会受到访问权限修饰符的影响
       – 如果类中有多个构造方法,Spring的选取构造方法的规则请参见《课后阅读》
————————————————————————————————————————————————————————————————————————
04.配置注解属性【ppt:DAY03-SPRING框架基础:31页——35页】
(1)关于@ComponentScan,其源代码片段有:【ppt:31页】
(2)关于注解的配置语法:[........................]
————————————————————————————————————————————————————————————————————————
05.组件注解【ppt:DAY03-SPRING框架基础:36页——39页】
(1)除了@Component以外,在Spring框架中还可以使用:
   @Repository@Service@Controller表示某个类是组件类
   这4个注解选择其中1个使用即可
(2)@Repository@Service@Controller使用@Component作为元注解(Meta Annotation),
   并且,这3个注解的value属性都等效于 @Component的value属性,通过源代码可以看出这些特点,
(3)Spring框架的解释范围内,组件注解的用法及作用表现是完全相同的,
   但字面语义不同,在开发实践中,应该根据类的定位进行选取:
   – @Repository:用于实现数据访问的类
   – @Service:用于处理业务逻辑的类
   – @Controller:用于控制器类
   – @Component:通用组件注解,即:不适合使用以上注解的组件类则添加此注解
————————————————————————————————————————————————————————————————————————
06.选择创建对象的方式【ppt:DAY03-SPRING框架基础:40页】
(1)以上已经介绍了2种创建对象的方式,相比之下:
   – 通过@Bean方法:需要在配置类中添加@Bean方法,需要Spring管理的对象越多,
     则需要添加的@Bean方法就越多,虽然每个方法的代码并不复杂,但是当方法的数量到一定程度
     后也比较繁琐,不易于管理,这种做法的优点是可以完全自定义对象的创建过程,
     在@Bean方法内部仍是传统的创建对象的语句
   – 通过组件扫描:只需要配置1次组件扫描,然后各组件类添加组件即可,且各组件类 添加组件注解
     后也可增强语义,所以,无论编码成本还是代码的可读性都更好,
     这种做法的不足在于[“只能适用于自定义的类”],毕竟你不可以在[引用的库中的类上添加组件注解]
————————————————————————————————————————————————————————————————————————
07.Spring Bean的作用域【ppt:DAY03-SPRING框架基础:41页——51页】
(1)在默认情况下,由Spring Bean的作用域是单例的
(2)单例的表现为:实例唯一,即在任意时刻每个类的对象最多只有1个,并且,
   当对象创建出来之后,将常驻内存,直至Spring将其销毁(通常是 ApplicationContext调用了销
   毁方法,或程序运行结束)
(3)注意:这与设计模式中的单例模式无关,只是作用域的表现完全相同
(4)创建 [————————>创建spring-03<———————]
————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————
07.Spring Bean的作用域【ppt:DAY03-SPRING框架基础:41页——51页】
(1)在默认情况下,由Spring Bean的作用域是单例的
(2)单例的_表现为:实例唯一,即在任意时刻每个类的对象最多只有1个,并且,
               当对象创建出来之后,将常驻内存,直至Spring将其销毁(通
               常是 ApplicationContext调用了销毁方法,或程序运行结束)。
(3)注意:这与设计模式中的单例模式无关,只是作用域的表现完全相同
(4)【创建spring-03】:.......................................
(5)可以通过@Scope注解修改作用域:【ppt:41-46页】
   ①默认Spring Bean的作用域是单例的,所以不需要声明为:@Scope("singleton")
   ②要想声明为非单例的,需要添加:@Scope("prototype") 来声明。
   ③补充:单例的:是先加载Spring配置,
                表现为:实例唯一,即在任意时刻每个类的对象最多只有1个,并且,
                      当对象创建出来之后,将常驻内存,直至Spring将其销毁(通
                      常是 ApplicationContext调用了销毁方法,或程序运行结束))
(6)默认是预加载的,不是懒加载的:【ppt:47-51页】
   1)默认情况下,由Spring Bean的作用域是单例的(是条件,不是单例的不能讨论懒加载),是预加载的;
   2可以通过@Lazy注解来配置懒加载,由于Lazy默认是懒加载的,所以不用加true即可:
      – 当通过@Bean方法创建对象时,在方法的声明之前添加@Lazy注解
      – 当通过组件扫描创建对象时,在组件类的声明之前添加@Lazy注解
   ● 在默认情况下,单例的Spring Bean是预加载的,必要的话,也可以将其配置为懒加载的
     – 如果某个对象本身不是单例的,则不在此讨论范围之内
   ● 预加载的表现为:加载Spring环境时就会创建对象,即加载Spring配置的环节,会创建对象
                  优点:事先创建好对象,无论何时需要获取对象,都可以直 接获取,
                  缺点:相当于启动程序时就会创建对象,这样的对象越多,启动过程就越慢,
                       并且,如果某个对象创建出来以后,在接下来的很长一段时间都不需要使用,
                       而此对象却一直存在于内存中,则是一种浪费
   ● 懒加载的表现为:加载Spring环境时并不会创建对象,而是在第1次获取对象的那一刻再创建对象
                  优点:仅当需要对象时才会创建对象,不会形成浪费,
                  缺点:如果当前系统已经负荷较重,需要的对象仍未加载,则会增加系统负担
   ● 相比而言,在开发实践中,——————>通常认为 预加载 是更合理的配置
————————————————————————————————————————————————————————————————————————
08.创建对象的小结【ppt:DAY03-SPRING框架基础:52页——56页】
[★★★★★] Spring可以将创建出来的对象管理起来,对于开发者而言,当需要某个类的对象时,
           只需要从Spring容器中获取即可
[★★★★★] 创建对象的方式有2种:
            – 通过@Bean方法:在配置类中自定义方法,返回需要Spring管理的对象,
              此方法必须添加@Bean注解
            – 通过组件扫描:在配置类中使用@ComponentScan指定需要扫描的包,并确保需要
              Spring管理对象的类都在此包或其子孙包下,且这些类必须添加@Component@Repository@Service@Controller中的其中某1个注解
            – 如果需要Spring管理的是自定义的类的对象,应该使用组件扫描的做法,如果需要
              Spring管理的对象的类型不是自定义的,只能使用@Bean方法的做法
[★★★★★] 使用组件扫描时,在@ComponentScan中指定的包是扫描的根包,其子孙包中的类都会被扫描,
           通常,指定的包不需要特别精准,但也不宜过于粗糙,你应该事先规划出项目的根包并配置在组
           件扫描中, 且保证自定义的每个组件类都在此包或其子孙包中
[★★★★★]Spring框架的解释范围内,@Component@Repository@Service@Controller的
           作用是完全相同的,但语义 不同,应该根据类的定位进行选取
[★★★★★] @Configuration是特殊的组件注解,Spring会通过代理模 式来处理,此注解应该仅用于配置类
[★★★☆☆] 使用@Bean方法时,beanName默认是方法名,也可以在 @Bean注解中配置参数来指定beanName
[★★★☆☆] 使用组件扫描时,beanName默认是将类名首字母改为小 写的名称(除非类名不符合首字母大写、
           第2字母小写的规律),也可以 在@Component或其它组件注解中配置参数来指定beanName
[★★★☆☆] Spring Bean的作用域默认是预加载的单例的,可以通过 @Scope("prototype")配置为
           “非单例的”,在单例的前提下,可以通过 @Lazy配置为“懒加载的”,通常,保持为默认即可
[★★★★★] 关于配置注解参数:
           – 如果你需要配置的只是注解的value这1个属性,不需要显式的写出属性名称
           – 如果你需要配置注解中的多个属性,每个属性都必须显式写出属性名称,包括value 属性
           – 如果你需要配置的注解属性的值是数组类型,当只指定1个值时,可以不使用大括号将值框住,
             当指定多个值时,多个值必须使用大括号框住,且各值之间使用逗号分隔
           – 你可以通过查看注解的源代码来了解注解可以配置哪些属性、属性的值类型、默认值
           – 在注解的源代码中,@AliasFor可理解为“等效于“
————————————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————————————
4、自动装配【ppt:DAY03-SPRING框架基础:57页开始】
—————————————————————————————————————————————————————————————————————————————
01.自动装配机制【ppt:57页——65页】
(1)Spring的自动装配机制表现为:当某个量需要被赋值时,可以使用特定的 语法,使得Spring尝试从容器找到合适的值,并自动完成赋值
(2)最典型的表现就是在类的属性上添加@Autowired注解,Spring就会尝试 从容器中找到合适的值为这个属性赋值
(3)【创建:spring-04(4)代码解析:
   ①在main()方法中,由于加载了SpringConfig类,根据SpringConfig上配置的 @ComponentScan,
     将执行组件扫描 – 由于UserMapperUserController都在组件扫描的包范围内,所以Spring框架会
     自动调用它们的构造方法以创建对象,并把对象保管在Spring容器中
   ②由于UserController中的userMapper属性添加了@Autowired注解,所以Spring框架会尝试为此属性
     注入()值,且由于在Spring容器中存在UserMapper对象,则可成功注入,使userMapper属性是有值的
   ③最终userController调用reg()方法时,实现过程中还通过userMapper调用了 insert()方法,整个
     执行过程中不会出错,在控制台可以看到对应的输出文本
(5)①除了对属性装配以外,Spring的自动装配机制还可以表现为:如果某个方 法是由Spring框架自动调用
     的(通常是构造方法,或@Bean方法),当 这个方法被声明了参数时,Spring框架也会自动的尝试从容器
     找到匹配的 对象,用于调用此方法
    ②对方法的参数自动装配时,如果方法有多个参数,各参数的先后顺序是不重要的
—————————————————————————————————————————————————————————————————————————————
02.@Autowired的装配机制【ppt:66页——65页】
(1)①关于@Autowired的装配机制,首先,会根据需要装配的数据的类型在 Spring容器中统计匹配的
     Bean(对象)的数量
   ②当匹配的Bean数量为0个时,判断@Autowired注解的required属性值
     - true(默认):装配失败,启动项目时即抛出NoSuchBeanDefinitionException
     - false:放弃自动装配,不会报告异常,后续直接使用此属性时,会出现NPE
   ③当匹配的Bean数量为1个时,将直接装配,且装配成功
   ④当匹配的Bean数量为多个时:自动尝试按照名称实现装配(即:要求属性名称与beanName相同)
     – 存在与属性名称匹配的Spring Bean:装配成功
     – 不存在与属性名称匹配的Spring Bean:装配失败。启动项目时即抛出NoUniqueBeanDefinitionException
—————————————————————————————————————————————————————————————————————————————
03.自动装配的小结:【ppt:68页——69页】
[★★★★★] 当某个属性需要被注入值,且你肯定此值存在于Spring容器中,你可以在属性上添
           加@Autowired注解,则Spring框架会自动为此属 性注入值
[★★★★★] 如果某个方法是由Spring调用的,当方法体中需要某个值, 且你肯定此值存在于Spring容器
           中,你可以将其声明为方法的参数,则 Spring框架会自动从容器中找到此值并且于调用此方法,
           如果声明了多个 这样的参数,各参数的先后顺序是不重要的
[★★★★★] 自动装配的前提是Spring会自动创建此类的对象,否则, Spring不可能为属性赋值,也不可
           能调用类中的方法
[★★★★★] @Autowired的装配机制的表现是可以根据类型实现装配,并且,当匹配类型的Bean有多个时,
           还可以根据名称进行匹配,从而实现装配,你需要熟记具体装配机制
—————————————————————————————————————————————————————————————————————————————
04.其它
(1)仍有一些与Spring框架相关的内容并未在本课提及,主要包括:
   – Spring Bean的生命周期 – 读取properties配置文件
   – @Value注解 – Spring AOP
(2)以上未提及的内容将在后续需要使用时再补充讲解
—————————————————————————————————————————————————————————————————————————————
05.课后阅读【ppt:72页———90页】

00.关于注解

 @Configuration        | 添加在类的声明之前,表示此类是配置类,会自动执行配置类中的@Bean方法,
 						 并解读配置类上的其它注解 
 				
 						 
 @Bean                 | 添加在配置类中用于创建对象的方法之前,使得Spring框架自动调用此方法,
 						 并管理此方法返回的结果,@Bean方法必须存在于@Configuration类中
 	
 四个组件注解:						 
 @Component            | 组件注解,添加在类的声明之前,表示此类是组件类,是通用注解,
 						 应该添加在不是控制器类,也不是业务逻辑类,也不是数据访问类的类上

 @Controller           | 组件注解,添加在类的声明之前,表示此类是组件类,应该添加在控制器类上
 
 @Service              | 组件注解,添加在类的声明之前,表示此类是组件类,应该添加在业务逻辑类上 
 
 @Repository           | 组件注解,添加在类的声明之前,表示此类是组件类,应该添加在数据访问类上 
 

 @ComponentScan        | 组件扫描,当加载到此注解时,Spring会扫描此注解配置的根包下是否存在组件类,
   						 如果存在,会自动创建组件类的对象,如果某些类不是组件,会被跳过(无视) 


 @Scope                | 配置Spring Bean的作用域,当配置为`@Scope("prototype")`时,
  					     Spring Bean将是“非单例的”,如果使用@Bean方法创建对象,
  					     则此注解添加在@Bean方法之前,如果使用组件扫描创建对象,
  					     则此注解添加在组件类之前

 @Lazy                 | 配置单例的Spring Bean是否是“懒加载”的,当在@Bean方法之前,
  						 或在组件类的声明之前添加此注解,则会使得此Spring Bean是懒加载的,
  						 如果使用@Bean方法创建对象,则此注解添加在@Bean方法之前,
  						 如果使用组件扫描创建对象,则此注解添加在组件类之前 

 @Autowired            | 自动装配的注解,当某个属性需要被Spring装配值时,在属性之前添加此注解,
 						 另外,此注解可以添加在Setter方法、构造方法之前,通常不需要这样处理 

01.关于框架

(1)框架(Framework)一个框架是一个可复用的设计构件,它规定了应用的体系结构,
   阐明了整个设计、协作构件之间的依赖关系、责任分配和控 制流程,表现为一组抽象类
   以及其实例之间协作的方法,它为构件复用提 供了上下文(Context)关系。
   
(2)应用框架指的是实现了某应用领域通用完备功能(除去特殊应用的部分)的底层服务。
   使用这种框架的编程人员可以在一个通用功能已经实现的基础上开始具体的系统开发。
   框架提供了所有应用期望的默认行为的类集合。 
   具体的应用通过重写子类(该子类属于框架的默认行为)或组装对象来支持应用专用的行为。

(3)你可以将框架理解为现实生活中的“毛胚房”,它已经完成了住房最基础部分的设计,
   例如打地基、设计住房的基本格局、预留电路、水路的线路 接入等……
   当你使用一个框架时,就相当于得到了一间毛胚房,
   如果你想住进去,你需要做的事情主要是“装修”,把这个毛胚房加工成你希望的样子。 

(4)所以,在软件开发中,使用框架,可以不必关注基础的、通用的功能开发, 
   因为这些部分在框架中已经处理好了,
   而且,框架已经实现的部分,通常比你自行开发的代码更加高效、更加安全、更加健壮

(5)虽然使用框架对你的开发有很大的帮助,但与此同时,你也会受到框架的约束,
   例如:假设你的“毛胚房”是东西朝向的,你是没有办法通过正常的手段把它变成一个南北朝向
        的房子的。

(6)所以,在学习一款框架时,应该更多的关注框架的正确使用方式,从而实现你预期的目标

02.关于Spring框架(需要添加依赖)

(1)Spring框架主要解决了创建对象、管理对象的问题。

(2)在开发实践中,Spring框架的核心价值在于:
   开发者可以通过Spring框 架提供的机制,将创建对象、管理对象的任务交给Spring来完成,
   以至于开发者不必再关心这些过程,当需要某个对象时,只需要通过Spring获取对象即可。 
   – Spring框架也经常被称之为:Spring容器

(3)在开发实践中,有许多类型的对象、配置值都需要常驻内存、需要有唯一性,或都需要多处使用,
   自行维护这些对象或值是非常繁琐的,通过Spring框架可以极大的简化这些操作。

(4)当某个项目需要使用Spring框架时,推荐使用Maven工程。

(5)使用Spring框架所需的依赖项是 spring-context,依赖代码为:
    <!--添加Spring框架依赖项:-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.14</version>
        </dependency>
    </dependencies>

03.Spring管理对象(@Bean方法、组件扫描)

如果需要Spring管理对象,就必须先创建对象,
然后Spring获取到对象才可以进行管理被Spring管理的对象,
通常也称之为Spring Bean

[★★★★★] Spring可以将创建出来的对象管理起来,对于开发者而言,当需要某个类的对象时,
            只需要从Spring容器中获取即可

[★★★★★] 创建对象的方式有2种: 
– 通过@Bean方法:在配置类中自定义方法,返回需要Spring管理的对象,此方法必须添加@Bean注解 
– 通过组件扫描:在配置类中使用@ComponentScan指定需要扫描的包,并确保需要Spring管理
               对象的类都在此包或其子孙包下,且这些类必须添加
               @Component@Repository@Service@Controller中的其中某1个注解 
– 如果需要Spring管理的是自定义的类的对象,应该使用组件扫描的做法,
  如果需要Spring管理的对象的类型不是自定义的,只能使用@Bean方法的做法

[★★★★★] 使用组件扫描时,在@ComponentScan中指定的包是扫描的根包,其子孙包中的类都会被扫描,
            通常,指定的包不需要特别精准, 但也不宜过于粗糙,你应该事先规划出项目的根包并配置在
            组件扫描中, 且保证自定义的每个组件类都在此包或其子孙包中 

[★★★★★]Spring框架的解释范围内,
            @Component@Repository@Service@Controller的作用是完全相同的,
            但语义不同,
            应该根据类的定位进行选取

[★★★★★] @Configuration是特殊的组件注解,Spring会通过代理模式来处理,此注解应该仅用于配置类

[★★★☆☆] 使用@Bean方法时,beanName默认是方法名,也可以在 @Bean注解中配置参数来指定beanName 

[★★★☆☆] 使用组件扫描时,
	          beanName默认是将类名首字母改为小写的名称
	            (除非类名不符合首字母大写、第2字母小写的规律),
	          也可以在 @Component 或 其它组件注解中 配置参数来指定beanName

[★★★☆☆] Spring Bean的作用域默认是预加载的单例的,
            可以通过 @Scope("prototype")配置为“非单例的”,
            在单例的前提下,可以通过@Lazy配置为“懒加载的”,通常,保持为默认即可

[★★★★★] 关于配置注解参数: 
            – 如果你需要配置的只是注解的value这1个属性,不需要显式的写出属性名称 
            – 如果你需要配置注解中的多个属性,每个属性都必须显式写出属性名称,包括value 属性 
            – 如果你需要配置的注解属性的值是数组类型,当只指定1个值时,可以不使用大括号将值框住,
              当指定多个值时,多个值必须使用大括号框住,且各值之间使用逗号分隔 
            – 你可以通过查看注解的源代码来了解注解可以配置哪些属性、属性的值类型、默认值 
            – 在注解的源代码中,@AliasFor可理解为“等效于”

04.自动装配(@Autowired注解)

Spring的自动装配机制表现为:当某个量需要被赋值时,可以使用特定的语法,
使得Spring尝试从容器找到合适的值,并自动完成赋值

最典型的表现就是在类的属性上添加@Autowired注解,Spring就会尝试 从容器中找到合适的值为这个属性赋值

代码解析: 
– 在main()方法中,由于加载了SpringConfig类,根据SpringConfig上配置的 @ComponentScan,
  将执行组件扫描 

– 由于UserMapperUserController都在组件扫描的包范围内,
  所以Spring框架会自动调用它们的构造方法以创建对象,并把对象保管在Spring容器中 

– 由于UserController中的userMapper属性添加了@Autowired注解, 
  所以Spring框架会尝试为此属性注入值,且由于在Spring容器中存在UserMapper对象,
  则可以成功注入,使得userMapper属性是有值的 
  
– 最终userController调用reg()方法时,实现过程中还通过userMapper调用了 insert()方法,
  整个执行过程中不会出错,在控制台可以看到对应的输出文本


除了对属性装配以外,Spring的自动装配机制还可以表现为:
如果某个方法是由Spring框架自动调用的(通常是构造方法,或@Bean方法),
当这个方法被声明了参数时,Spring框架也会自动的尝试从容器找到匹配的对象,用于调用此方法 

对方法的参数自动装配时,如果方法有多个参数,各参数的先后顺序是不重要的


[★★★★★] 当某个属性需要被注入值,且你肯定此值存在于Spring容器中,
        你可以在属性上添加@Autowired注解,则Spring框架会自动为此属性注入值

[★★★★★] 如果某个方法是由Spring调用的,当方法体中需要某个值,且你肯定此值存在于Spring容器中,
        你可以将其声明为方法的参数,则 Spring框架会自动从容器中找到此值并且于调用此方法,
        如果声明了多个这样的参数,各参数的先后顺序是不重要的 

[★★★★★] 自动装配的前提是Spring会自动创建此类的对象,
        否则, Spring不可能为属性赋值,也不可能调用类中的方法

[★★★★★] @Autowired的装配机制的表现是可以根据类型实现装配, 
        并且,当匹配类型的Bean有多个时,还可以根据名称进行匹配,从而实现装配,
        你需要熟记具体装配机制

05.课后阅读:

IoCDI
1.IoC(Inversion of Control:控制反转)是Spring框架的核心,
  在传统的开发模式下,是由开发者创建对象、为对象的属性赋值、管理对象的作用域和生命周期等,
  所以,是开发者拥有“控制权”,当使用了Spring之 后,这些都交给Spring框架去完成了,
  开发者不必关心这些操作的具体实 现,所以,  称之为“控制反转” 
  无论是创建对象,还是自动装配等等,只要是Spring创建并管理对象的操作,
  都可以称之为Spring IoC的过程


2.DI(Dependency Injection:依赖注入)是Spring框架实现IoC的核心实现,
  当某个类中声明了另一个类的属性(例如在UserController类中声明了UserMapper类型的属性),
  则称之为依赖(即UserController依赖了 UserMapper),
  Spring框架会帮你完成依赖项的赋值,
  只是你在你的代码中看不到赋值过程或赋值符号,所以称之为 注入

3.关于IoCDI的关系,
  可以描述为:
  Spring通过DI实现了IoC,
  所以,
  IoC 是一种目标,
  而DI是实现此目标的重要手段
——————————————————————————————————————————————————————————————————————————————

关于@Qualifier
(1)@Qualifier注解是在自动装配机制中,用于指定beanName的注解 
(2)通常是因为存在多个类型匹配的Bean,
   但是所有Bean的beanName与被装配的属性名称/参数名称都不匹配,
   且不想修改beanName也不想修改属性名称/参数名称,
   则可以通过@Qualifier指定beanName 
   – 这种现象在开发实践中并不多见
(3)在自动装配属性时指定beanName
   例:
   @Controller 
   public class UserController { 
   	@Autowired 
   	@Qualifier("userMapper") 
   	private UserMapper xxx; 
   	public void reg() { 
   		System.out.println("UserController.reg() >> 控制器即将执行用户注册……");
   		xxx.insert(); 
   	} 
   }
   在匹配类型的对象在Spring容器中有且仅有1个时,一定会装配成功,
   为得到更好的演示效果,
   你应该使得Spring容器中有2个或以上UserMapper类型的对象   
(4)在自动装配方法参数时指定beanName   
   例:
   @Controller 
   public class UserController { 
   	@Autowired 
   	@Qualifier("userMapper") 
   	private UserMapper xxx; 
   	public void reg() { 
   		System.out.println("UserController.reg() >> 控制器即将执行用户注册……"); 
   		xxx.insert(); 
   	} 
   }
在匹配类型的对象在Spring容器中有且仅有1个时,一定会装配成功,
为得到更好的演示效果,
你应该使得Spring容器中有2个或以上UserMapper类型的对象
——————————————————————————————————————————————————————————————————————————————

关于构造方法
(1)当通过组件扫描创建对象时,Spring会自动调用组件类的构造方法,
   此过程中会使用到反射,所以可以无视构造方法的访问权限
(2)如果类中仅有1个构造方法,Spring会自动调用这个构造方法 
(3)如果类中没有显式的添加构造方法,根据Java的机制,会由编译器添加默认构造方法,相当于有1个构造方法 
(4)如果类中有多个构造方法,默认情况下,Spring会自动调用无参数构造方法(如果存在的话),
   如果某个构造方法添加了@Autowired,则Spring 会自动调用添加了此注解的构造方法
(5)Spring调用的构造方法是允许有参数的,
   作为开发者,你需要保证: 
   Spring可以利用自动装配机制为参数注入值(你需要保证Spring容器中存在匹配的值),
   否则会导致NoSuchBeanDefinitionExcep
——————————————————————————————————————————————————————————————————————————————

关于@Resource注解
(1)@Resource注解是javax.annotation包中的
(2)如果某属性添加了@Resource注解,Spring也可以实现自动装配
(3)@Resource注解的装配机制是:
   先尝试根据名称进行装配(即:要求属 性名称与beanName相同),
   如果失败,则尝试根据类型装配,
   如果不存在类型的Bean,则抛出NoSuchBeanDefinitionException,
   如果只有1个匹配类型的Bean,则装配成功,
   如果匹配类型的Bean超过1个,则抛出NoUniqueBeanDefinitionException
(4)在开发实践中,绝大部分类型的Bean都只有1个,
   无论是@Autowired还 是@Resource,当匹配类型的Bean有且仅有1个时,都可以成功装配, 
   所以,在绝大部分情况下,这2个注解的装配机制的差异对于开发人员来说是无感的
(5)当需要讨论@Autowired@Resource的区别时,
   除了这2个注解所在的包不同、装配机制不同以外,还存在以下区别: 
   – @Autowired可以添加在构造方法的声明之前,@Resource不可以 
   – @以Resource可以添加在类的声明之前(但不会装配属性的值),@Autowired不可 
   – 当存在多个同类型的Bean时 
   – 当装配属性时,@Autowired需要通过@Qualifier指定beanName,
                 而@Resource可以直接配置name属性以指定beanName 
   – 当装配方法的参数时(例如添加了@Autowired的构造方法的参数),
     @Autowired仍可以使用@Qualifier指定beanName,
     而@Resource无法解决此问题
(6)综合来看,由于@AutowiredSpring框架专门定制的注解,
   且 @Autowired可以添加在构造方法上,
   相比@Resource有更多的应用场景 (虽然不一定真的需要这样用),
   所以,在开发实践中,当需要  使用注解显式的表示自动装配时,推荐优先使用@Autowired
——————————————————————————————————————————————————————————————————————————————

关于@Autowired自动装配注解的 警告
(1)当你在属性上添加@Autowired时,在IntelliJ IDEA中会有浅灰色的下划线提示警告
(2)假设某开发人员不是通过Spring容器获取UserController的对象,而是通 过传统方式,
   使用new关键字自行创建对象,
   且忽略了为userMapper属性赋值(此属性是private的,对外不可见,忽略了很常见),
   则会导致 userMapper一直是默认的null值,
   后续调用userMapper的方法时就会出现NPE(空指针异常),这被视为字段注入的不足之处 
   – 暂不关心为什么不通过Spring容器获取对象
(3)Spring有非常完善的调用构造方法的机制,并且也可以对构造方法中的参数注入值,
   甚至存在多个匹配类型对象时还可以使用@Qualifier指定 beanName,
   所以,只要构造方法中的代码能够对属性赋值,就可以实现和属性上自动装配完全相同的效果 
(4)只要将必要的值声明为构造方法的参数,无论是Spring创建对象,还是人为创建对象,
   都需要提供此参数值,除非恶意的传入null值,
   否则,不会出现NPE问题,所以,此做法是被推荐的
(5)使用构造方法虽然安全,不会出现NPE,
   但是,当需要被赋值的属性的数量出现增或减时,
   都需要调整构造方法,当属性数量较多时,构造方法的参数也会变多,
   这些都是在开发实践中非常现实的问题 
(6)由于字段注入的NPE问题只是开发阶段人为错误使用导致的,
   出现概率非 常低、可控、容易解决,且字段注入的语法简洁、直观,
   所以,在开发实践中,字段注入仍是最常见的用法

————————————————————————————

3、springMvc

【ppt:Spring MVC框架基础-011、关于Spring MVC框架
2、接收客户端的请求
3@RequestMapping注解
4、响应正文
5、接收请求参数
6、课后阅读
————————————————————————————————————————————————————————————————————————————
1、关于Spring MVC框架【ppt:1页——5页】
01.关于Spring MVC框架
(1)Spring MVC是基于Spring框架基础之上的,主要解决了后端服务器接收客户端提交的请求,
   并给予响应的相关问题
(2)MVC = Model + View + Controller,它们分别是:
   –Model:数据模型,通常由业务逻辑层(Service Layer)和数据访问层(Data Access Object Layer)共同构成
   –View:视图
   –Controller:控制器
   –MVC为项目中代码的职责划分提供了参考
   需要注意:Spring MVC框架只关心 V-C 之间的交互,与 M 其实没有任何关系。
(3)Spring MVC的核心执行流程图如下所示:
————————————————————————————————————————————————————————————————————————————
2、接收客户端的请求【ppt:6页——18页】
(1)创建Spring MVC工程:
   请参考 http://doc.canglaoshi.org/doc/idea_tomcat/index.html
   来创建项目,首次练习的项目名称请使用————>【springmvc01】
(2)接收客户端的请求
   ● 【案例目标】开发使用Spring MVC框架的项目,将其部署到Tomcat,最终,
               部署此项目启动Tomcat,用户在浏览器中输入指定的URL提交请求后,
               项目可以对此进行简单的响应.
(3)接收客户端的请求:
   在《tomact-webapp》中:pom.xml中找到【<dependencies>.....</dependencies>:
   在其中添加两个依赖项
   <!--【添加spring-webmvc依赖项:】-->
     <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.14</version>
     </dependency>
   <!--添加(后续运行时提示不可识别Servlet相关类)依赖项:-->
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>javax.servlet-api</artifactId>
         <version>3.1.0</version>
         <scope>provided</scope>
     </dependency>
     <!--创建项目时自动添加的依赖:-->

(4)以上代码中的<scope>provided</scope>表示此依赖不会参与测试或部署,因为当Web项目部署
   到Tomcat中后,Tomcat环境会包含此依赖项

(5)准备2个配置类,一个是Spring框架的配置类:...................

(6)启动配置好的Tomcat,此时会自动跳转页面,并在页面显示“HelloWorld”内容!
   ①全部完成后,启动项目,会自动打开浏览器并显示主页,
     在主页的地址栏 URL上补充/hello.do即可实现访问,并看到结果
   ②在启动过程中,你还可以在IDEA的控制台中看到控制器类的构造方法中输出的内容
(7)关于以上案例:
   – 当启动Tomcat时,会自动将项目打包并部署到Tomcat,通过【自动打开的浏览器中的URL】即可
     访问主页,在URL中有很长一段是例如 springmvc01_war_explored 这一段是不可以删除的,
     其它的各路径必须补充在其之后,例如 /hello.do 就必须在此之后
   – 当启动Tomcat,【项目一旦部署成功,就会自动创建并加载AbstractAnnotationConfigDispatcherServletInitializer
     的子类】,【即当前项目中自定义的 SpringMvcInitialier】,【无论这个类放在哪个包中,
     都会自动创建并加载】
   – 由于会自动调用这个类中所有方法,所以会将Spring MVC框架处理的请求路径设置为 *.do,
     并执行对 cn.tedu.springmvc 的组件扫描,进而会创建 UserController 的对象
   – 由于在 UserController 中配置的方法使用了 @RequestMapping("/hello.do"),
     则此时还将此方法与/hello.do进行了绑定,以至于后续随时访问/hello
(8)Userontroller中再写:login.do和reg.do两个ip后缀和对应方法进行绑定,
   启动Tomcat后,
   自动跳转到(http://localhost:8080/springmvc01_war_exploded/)显示Hello World!
   当添加ip后缀:[/login.do][/reg.do]时,会在跳转到对应的页面,并显示对应网页的内容!

(9)关于以上案例的注意事项:
   –注意:组件扫描必须配置在Spring MVC的配置类中
   –注意:控制器类上的注解必须是@Controller,不可以是@Component@Service@Repository
   –各注解的作用并不是注解自身决定的,而是运行环境或框架决定的,在Spring框架中,
    这4个注解是完全等效的,但是,在部分版本的Spring MVC框架中并不相同
   –注意:方法返回的值应该是ASCII码字符,不推荐使用中文、中文标点符号等
         非ASCII码字符,否则可能显示为乱码
   –某些版本的Spring MVC默认的字符编码是ISO-8859-1,只支持ASCII字符
   –乱码的问题暂不解决
————————————————————————————————————————————————————————————————————————————
3@RequestMapping注解【ppt:19页——30页】
01.@RequestMapping注解:
(1)@RequestMapping注解的主要作用是:
     配置请求路径与处理请求的方法的 映射关系,例如将此注解添加在控制器中某个方法之前:
       // http://localhost:8080/springmvc01_war_exploded/login.do
       @RequestMapping("/login.do")
       @ResponseBody
       public String login() {
            return "UserController.login()";
       }
!!!②注意:配置的请求路径值——必须是初始化类(SpringMvcInitializer)中的
          getServletMapping()方法返回值可以匹配的,
          例如方法返回值是 *.do,则配置的请求路径必须以 .do 作为后缀!!!!!!!!!!!!!!!
(2)@RequestMapping注解 ● 除了方法之前,此注解还可以添加在控制器类之前,例如:
   1)在类上添加了此注解并配置路径后,每个方法实际映射到的请求路径都是:
      “类上的配置的路径 + 方法上的配置的路径”,
      例如 /user/login.do
   2)通常,在开发实践中,推荐为每一个控制器类都配置此注解,以指定某个URL前缀
(3)在使用@RequestMapping配置路径时,无意义的 / 会被忽略,例如在类 上配置为 /user/,
   在方法上配置为 /login.do,则拼接的结果会是 /user//login.do,实际有效值为 /user/login.do
(4)必要的 / 会被补充,例如在类上配置为user,在方法上配置为login.do,实际有效值仍是 /user/login.do
(5)在开发实践中,应该保持统一风格,例如:无论在类上,还是在方法上,配置的请求路径均以 / 作为第1个字符(某些特殊的URL配置例外)
(6)@RequestMapping还可以配置:
   – method:请求方式
   – headers:请求头
   – params:请求参数
   – consumes:请求文档类型
   – produces:响应文档类型
(6)例如,在"UserController"中:
①@RequestMapping注解中,增加配置method属性,可以限制客户端的请求方式:
  【@RequestMapping(value = "/login.do",method = RequestMethod.POST)】
②如果按照以上代码,则/login.do路径只能通过POST方式发起请求才可以被正确的处理,
  如果使用其它请求方式(例如GET),则会导致HTTP405错误。
③【如果没有配置method属性,则表示可以使用任何请求方式】,包括:
  GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
  可以写成数组的形式:
  【@RequestMapping(value = "/login.do",method = {RequestMethod.POST,RequestMethod.GET})】
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
02.解决中文乱码问题:[ppt:25]
在部分版本的Spring MVC框架中,响应结果不会自动添加响应头的文档类型,
可通过配置"UserController"类中:
@RequestMapping的produces属性显式的添加,以解决中文乱码问题:
①问题:在"UserController"类中:
      若【方法中有中文】,则启动Tomcat后,自己在浏览器网址添加后缀访问登录和注册后,
      会显示乱码问题!
②解决1:可以通过在方法上的"@RequestMapping"后添加【produces】属性来解决:
      【@RequestMapping(value = "/login.do", produces="text/html; charset=utf-8")】
       但此时每个方法都要添加才可!
       访问【http:/.......oded/user/login.do】发现是中文!成功!
 解决2:当然!
       ——————>!!!!我们可以直接[在类的上面添加该属性]进行配置,!!!!!!
       ——————>!!!!则类中每个处理请求的方法均应用此配置!!!!!
       例:———>@RequestMapping(value = "/user",produces = "text/html;charset=utf-8")
       测试网址,添加"/user/login.do"后缀访问,发现是中文显示了!!成功!!!
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
(8)@XxxMapping@RequestMapping的区别?
   另外,Spring MVC框架还提供了@RequestMapping的相关注解,例如:
   – @GetMapping@PostMapping@PutMapping@DeleteMapping – 等等
   ①@GetMapping等注解是已经限制了请求方式的注解
     – 以@GetMapping为例,限制了请求方式必须是GET,除此以外,使用方式
       与@RequestMapping完全相同
   ②在开发实践中,
     【在类的上方肯定使用@RequestMapping(其它的@XxxMapping不可以加在类上)】,
     方法上一般都使用@GetMapping@PostMapping等注解,除非在极特殊的情况下,
     某些请求同时允许多种请求方式,才会在方法上使用@RequestMapping
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
(9)总结1:类上用@RequestMapping(好处:可以添加多种访问方式,例如get、post等)
          方法上用@XxxMapping()
   总结2[★★★☆☆] 使用@RequestMapping的主要作用是配置请求路径
   [★★★☆☆] @RequestMapping可以添加在类上,也可以添加在方法上,同时配置时,
              最终的URL是由类上的与方法上的配置值组合的
   [★★★☆☆] 当需要限制请求方式为某1种时,应该在处理请求的方法上使用@GetMapping@PostMapping等注解
   [★★★★★] 在开发实践中,通常:
               – 在类上使用@RequestMapping,配置请求路径,并配置produces以指定响
                 应的文档类型
               – 在方法上使用@GetMapping@PostMapping等限制了请求类型的注解,
                 配置请求路径
——————————————————————————————————————————————————————————————————————————
4、响应正文【ppt:19页——43页】

01.响应正文【ppt:31页——35页】
(1)@ResponseBody注解表示:响应正文
(2)一旦配置为“响应正文”,则处理请求的方法的返回值就会直接响应到客户端去
(3)如果没有配置为“响应正文”,则处理请求的方法返回的字符中值表示“视图组件的名称”,
   当方法返回后,服务器端并不会直接响应,而是根据“视图组件的名称”在服务器端找到对应的视图组件,
   并处理,最后,将处理后的视图响应到客户端去,这不是前后端分离的做法
(4)@ResponseBody注解还可以添加在类上,作用域当前类的所有方法
   ②在Spring MVC框架中,还提供了@RestController注解:
     它同时具有 @Controller@ResponseBody注解的效果,所以,在响应正文的控制器上,
     只需要使用@RestController即可,不必再添加@Controller@ResponseBody注解。
   ③在"UserController"类中:把方法前的"@ResponseBody"注解去掉,
     类上添加"@RestController"启动服务,测试网址。
(5)Spring MVC内置了一系列的转换器(Converter:
   ①!用于将方法的返回值转换为响应到客户端的数据(并补充其它必要数据);
   ②!并且,Spring MVC会根据方法的返回值不同,自动选取某个转换器;
      例如,当方法的返回值是String时,会自动使用StringHttpMessageConverter转换器,
      这个转换器的特点就是:【直接将方法返回的字符串作为响应的正文】,
      并且:在许多版本的Spring MVC框架中,其默认的响应文档的字符集是ISO-8859-1,
          所以在许多版本的Spring MVC中响应String正文时默认不支持非ASCII字符(例如中文)
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
02.响应JSON格式的正文【ppt:36页——40页】
(1)①在开发实践中,不会使用String作为处理请求的方法的返回值类型,主要是因为普通的字符串不足
     以清楚的表现多项数据,通常建议使用XMLJSON语法来组织数据
   ②主流的做法是向客户端响应JSON格式的字符串,需要在项目中添加 jackson-databind的依赖项:
     在【springmvc01】的【pom.xml】文件中:添加jackson-databind的依赖:
        <!--添加jackson-databind的依赖:-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>
(2)①以上jackson-databind依赖项中也有一个转换器,当Spring MVC调用的处理请求的方法的
     返回值是Spring MVC没有匹配的默认转换器时,会自动使用jackson-databind的转换器,
     而jackson-databind转换器就会解析方法的返回值,并将其处理为JSON格式的字符串,
     在响应头中将 Content-Type设置为——————>application/json
   ②注意:在Spring MVC项目中,还需要在Spring MVC的配置类上添加 @EnableWebMvc注解,
          否则响应时将导致出现HTTP406错误

   ③在spring-mvc的配置类SpringMvcConfig上面添加【@EnableWebMvc】注解,
     原因:当响应正文且响应的是JSON格式的结果时,必须添加此注解的话,
          否则在响应时将导致出现HTTP406错误。
   ④在项目cn.tedu.springmvc下新建包vo,并创建UserVo类 ,
     声明三个类:
     private String username;
     private String password;
     private String email;
     并自行补充以上3个属性的Setter & Getter
     注意:jackson-databind转换器会自动调用属性的[Setter/Getter]方法
   ⑤在"UserController"类中添加【UserVo类型的info方法】:
           @GetMapping("/info.do")
           public UserVo info(){
               UserVo user = new UserVo();
               user.setUsername("chengheng");
               user.setPassword("1234567890");
               user.setEmail("chengheng@qq.com");
               return user;
           }
      注意:若想启动服务后成功,需要注意:上边的【(2)①】的文本内容!!!!
           即:把"UserController"中最上边的"RequestMapping"注解的produces属性改为:
              【@RequestMapping(value = "/user",produces = "application/json;charset=utf-8")】
      启动Tomcat后,页面地质栏添加后缀【/user/info.do】
      显示:{"username":"chengheng","password":"1234567890","email":"chengheng@qq.com"}
   ⑥为了便于下边的练习,
     我们把"UserController"类中:方法里的返回都写成英文
     我们把String作为返回值会乱码的"UserController"类的最上边@RequestMapping"的属性:
     由:@RequestMapping(value = "/user",produces = "application/json;charset=utf-8")
     改为:@RequestMapping(value = "/user")
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
03.响应正文的小结【ppt:41页——43页】
[★★★★★] 响应正文的定义:将处理请求的方法的返回值作为响应到客 户端的正文数据
[★★★★★] 响应正文的做法:
           – 在方法上添加@ResponseBody
           – 或,在类上添加@ResponseBody
           – 或,在类上使用@RestController取代@Controller
[★★★★★] 响应正文的格式:响应的结果通常需要包含多项数据,响应1个字符串并不便于表示这些数据,
           通常响应JSON格式的字符串
[★★★☆☆] 响应正文的原理:Spring MVC内置了一系列的转换器 (Converter),根据处理请求
           的方法的返回值不同,自动选取某个转换 器,将方法的返回值转换为响应到客户端的数据,
           当方法的返回值没有匹 配的默认转换器时,会自动使用jackson-databind的转换器
[★★★★★] 当需要响应JSON格式的正文时,你需要:
           – 添加jackson-databind依赖
           – 在Spring MVC配置类上添加@EnableWebMvc注解
           - 自定义类,作为处理请求的方法的返回值类型
           – 类的属性必须添加Setter & Getter
           – 使得处理请求的方法是响应正文的
——————————————————————————————————————————————————————————————————————————
5、接收请求参数【ppt:44页——52页】

01.接收请求参数【ppt:44页——51页】
(1)Spring MVC中,当需要接收客户端的请求参数时,只需要将各参数直接声明为处理请求的方法
   的参数即可,例如:在Spring MVC中,当需要接收客户端的请求参数时,只需要将各参数直接声明
   为处理请求的方法的参数即可,例如:
   在"UserController":
       @RequestMapping("/reg.do")
       // /user/reg.do?username=root&password=123456&age=25
       public String reg(String username,String password,Integer age){
           System.out.println("username = " + username + ", password = "
                   + password + ", age = " + age);
           return "reg";
       }

(2)在声明参数时,你可以将参数声明为你期望的数据类型,Spring会自动的执行类型转换
   – 【经过网络传输得到的数据,最原始的类型都是String】
   – 【Spring会自动尝试转换类型】,通常,
       类型必须是【基础数据类型及其包装类】、【常用类型(例如String等)、Spring定义的类型】等
   – 如果自动转换类型失败,会抛出相应的异常

(3)由于在上边"UserController":给reg后边定义了参数,下面我们启动Tomcat后,
   在浏览器地址栏输入下边的三个网址进行测试,观察idea中控制台的输出:
   ①如果客户端提交的请求中根本没有匹配名称的参数,则以上获取到的值将是null
     – 例如:http://localhost/user/reg.do
   ②如果客户端仅提交了参数名称,却没有值,
     则以上String类型获取到的值将是""(长度为0的字符串)
     则以上Integer类型获取到的值将是""(长度为0的字符串)
     – 例如:http://localhost/user/reg.do?username=&password=&age=
   ③如果客户端提交了匹配名称的参数,并且值是有效的,则可以获取到值
     – 例如:http://localhost/user/reg.do?username=admin&password=1234&age=27
   ④以上名称应该是由服务器端(我们自己)决定的,客户端需要根据以上名称来提交请求参数

(4)当有必要的情况下,可以在以上各参数的声明之前添加@RequestParam注解,其作用主要有:
1)配置name属性:客户端将按照此配置的值提交请求参数,而不再是根据方法的参数名称来提交请求参数
2)配置required属性:是否要求客户端必须提交此请求参数,默认为true,如果不提交,则出现400错误,
                  当设置为false时,如果不提交,则服务器端将此参数值视为 null
3)配置defaultValue属性:配置此请求参数的默认值,当客户端没有提交此请求参数时,视为此值
————>对应"1)配置name属性"的练习:<————————
①在"UserController"中注册的方法里边:参数【String username】的前边
  添加@RequestParam注解:public String reg(@RequestParam("name") String username,S.......】
  此时启动服务,在浏览器地址栏后缀输入:
  【http://localhost/user/reg.do?username=&password=&age=】
  去在页面显示对应的值,此时该页面会报错404,
  而当我们输入name时:【http://localhost/user/reg.do?name=&password=&age=】页面会显示正常!
②————>对应"2)配置required"的练习:<————————
  若添加成:
 [public String reg(@RequestParam(value = "name",required = true) String username,S]
 此时启动服务,在浏览器地址栏后缀不提交:输入【user/reg.do】
 去在页面显示对应的值,此时该页面会报错400;
  若添加成:
 [public String reg(@RequestParam(value = "name",required = false) String username,S]
 此时启动服务,在浏览器地址栏后缀不提交:输入【user/reg.do】
 去在页面显示对应的值,此时该页面会显示reg方法里的返回值"reg",idea控制台出现则将此参数值视为null;
③————>对应"3)配置defaultValue属性"的练习:<————————
  若添加成:
 [public String reg(@RequestParam(value="name",required=true,defaultValue="789") String username,S.....]
 此时启动服务,在浏览器地址栏后缀提交:输入【user/reg.do?name=&password=&age=】
 去在页面显示对应的值,此时该页面会显示reg方法里的返回值:reg;
 因为defaultValue属性是配置此请求参数Value的默认属性,当客户端没有提交此请求参数时,视为此值:
 当提交上边的name=空值时,idea控制台显示username = 789,————>即默认为上边配置的定值"789"
 当提交上边的name=111时,idea控制台显示username = 111,————>即默认为111

(5)如果需要客户端提交的请求参数较多,可以定义一个类,将其参数写在里面,传入的参数为这个类名即可!:
   如果需要客户端提交的请求参数较多,可以将这些参数封装为自定义的数据类型,
   并将自定义的数据类型作为处理方法的参数即可,例如:
   ①在springmvc 包下:创建包:dto  下创建类:UserRegDTO
   ②在UserController控制器中:将reg方法的参数改为:[UserRegDTO userRegDTO]
       public String reg(UserRegDTO userRegDTO) {
           System.out.println("userRegDTO = " + userRegDTO);
           return "reg";
       }
    你也可以将多个请求参数区分开来,一部分直接声明为处理请求的方法的参数,另一部分封装起来。
   ③启动服务,
     /浏览器地址栏后缀输入:[user/reg.do?userRegDTO]
      会跳转到对应的页面并显示reg方法里返回的参数:"reg"
      idea的控制台会显示:
      【userRegDTO = UserRegDTO{username='null', password='null', age=null}/浏览器地址栏后缀输入:[user/reg.do?username=55&password=123456&age=22]
      会跳转到对应的页面并显示reg方法里返回的参数:"reg"
      idea的控制台会显示:
      【userRegDTO = UserRegDTO{username='55', password='123456', age=22}(6)理论上来说,由于一个个的声明请求参数更加简单并且直观,所以,当请求参数数量非常少时,
   应该使用这种做法,当参数较多,或参数数量可能调整(例如需求变化引起的调整),则应该使用封装的数据类型
(7)在开发实践中,考虑到需要使用到的其它框架的特性,使用封装的做法更为常见

02.接收请求参数的小结【ppt:52页】
[★★★★★] 你可以将请求参数一个个的声明为处理请求方法的参数,也可以将多个参数封装到一个自定义类中,
           使用自定义类作为处理请求的方 法的参数,Spring MVC框架会自动接收客户端提交的请求参数,
           并用于调用你编写的处理请求的方法
[★★★★★] 在大部分情况下,推荐使用将参数封装到自定义类的做法
[★★★★★] 你需要保证非String类型的参数是Spring框架可以成功自动转换类型的,或者,
           对转换失败有进一步的处理,或可以接受转换失败带来的后果
————————————————————————————————————————————————————————————————————————————
6、课后阅读【ppt:53页——60页】
01.关于POJO
(1)POJOPlain Ordinary Java Object,即:普通Java对象
(2)所有用于封装属性的类型都可以统称为POJO
(3)常见的POJO的类名后缀有:BODOVODTO等,不同的后缀表示不同的意义,
   例如: – VO = Value Object / View ObjectDTO = Data Transfer Object
(4)所有POJO类都应该遵循以下规范:
   – 实现Serializable接口
   – 所有属性都是私有的
   – 所有属性都有规范的Setter & Getter
   – 由开发工具生成的,即是规范的
   – 规范的重写了hashCode()equals()2个类型相同、所有属性值都相同的对象,equals()应该返回true,否则,返回false2个类型相同、所有属性值都相同的对象,hashCode()返回值应该相同,否则,不同
   – 由开发工具生成的,即是规范的,不同开发工具生成的方法源码可能不同,这不重要
   – 通常,应该重写toString()输出每个属性的值
(5)所有POJO类的类名后缀:
   – 在一个项目中,哪些情景下使用哪种后缀并没有统一的规定,通常是各项目内部决定
   – 注意:在为封装属性的类进行命名时,以上BODOVODTO等这些后缀的每一 个字母都应该是大写的!
(6)参考资料:在《阿里巴巴Java开发手册》中要求POJO类名后缀的每个字母都是大写的:
   ......................................
(7)参考资料:在《阿里巴巴Java开发手册》中要求不允许使用POJO作为类名后缀:
   .....................................
(8)参考资料:在《阿里巴巴Java开发手册》中要求所有POJO类的属性都不要设置默认值:
   .....................................
(9)参考资料:在《阿里巴巴Java开发手册》中提供了使用类名后缀的参考,但不是强制约定:
   .....................................







【ppt:Spring MVC框架基础-021RESTful基础
2、响应正文的结果类型
3、统一处理异常
4、拦截器
5、小结
————————————————————————————————————————————————————————————————————————————
1RESTful基础【ppt:3页——15页】
01.RESTful基础
(1)RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。
   RESTFUL适用于移动互联网厂商作 为业务接口的场景,实现第三方OTT调用移动网络资源的功能,
   动作类型为新增、变更、删除所调用资源。
(2)RESTful的设计风格的典型表现就是:
   将某些唯一的请求参数的值放在URL中,使之成为URL的一部分,例如:
   – https://www.zhihu.com/question/28557115
   – 这个URL的最后一部分28557115应该就是这篇贴子的id值,
     而不是使用例如?id=28557115这样的方式放在URL参数中。
(3)注意:RESTful只是一种设计风格,并不是一种规定,也没有明确的或统一的执行方式
(4)在开发实践中,如果没有明确的要求,以处理用户数据为例,可以将URL设计为:
   /users:查看用户列表
   /users/9527:查询id=9527的用户的数据
   /users/9527/delete:删除id=9527的用户的数据
(5)RESTful建议根据希望实现的数据操作不同而使用不同的请求方式:
   – 通过之前学习,你应该知道HTTP请求方式包括:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
   – 查询:使用GET请求
   – 新增:使用POST请求
   – 修改:使用PUT请求
   – 删除:使用DELETE请求
(6)在开发实践中,仅使用GETPOST2种请求方式的做法更为常见,主要因为:
   – 某些业务可能非常复杂,可能同时需要执行增、删、改、查的多种数据操作,
     不便于界定使用POSTDELETEPUTGET请求方式中的哪一种
   – 大部分开发者更习惯于只使用GETPOST2种请求方式
(7)通常,以查询为主要目标的请求使用GET请求方式,否则,使用POST请求方式

(8)RESTful风格的URL中,大多是包含了某些请求参数的值,在使用 Spring MVC框架时,
   当需要设计这类URL时,可以使用{名称}进行占位,并在处理请求的方法的参数列表中,
   使用@PathVariable注解请求参数,即可将占位符的实际值注入到请求参数中
   !!!!!!!!!!!!!!!![变为了:users]!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
——>①在UserController中:最上边的:添加"s"@RequestMapping(value = "/users")】
   ②    //http://localhost:8080/sprinxxxxx/user/9527/info.do
        //http://localhost:8080/sprinxxxxx/user/9999/info.do?username=root
        @GetMapping("/{id}/info.do")
        public String getInfoById(@PathVariable Long id){
            return "get user info,id = "+id;
        }
   ③启动服务,后缀加【users/9999/info.do】测试:页面返回:【get user info,id = 9999(9)提示:在以上代码中,URL中使用的占位符是{id},则方法的参数名称也 应该是id,就可以直接匹配上!
   如果无法保证这2处的名称一致,则需要 在@PathVariable注解中配置占位符中的名称,例如:
   @GetMapping("/{userId}/info.do")
   public UserVO info(@PathVariable("userId") Long id) { // ... }
(10)在使用{}格式的占位符时,还可以结合正则表达式进行匹配,其基本语法是:{占位符名称:正则表达式}
    例如:  @GetMapping("/{id:[0-9]+}/info.do")
(11)当设计成以上URL时,仅当占位符位置的是纯数字的URL才会被匹配上,
    如果不是纯数字的刚出现404错误页面
    ①在"UserController"中:getInfoById上面的注解,改为:
      @GetMapping("/{id:[0-9]+}/info.do")
    ②启动服务,
      在浏览器地址栏后缀添加【/users/9999/info.do】时,跳转页面:【get user info,id = 9999】
      当后缀添加【/users/9xx9/info.do】时,跳转页面:【HTTP状态 404 - 未找到】
(12)【以上模式的多种不冲突的正则表达式是可以同时存在的】,例如:
    在"UserController"中:添加:
        @GetMapping("/{username:[a-zA-Z]+}/info.do")
        public String getInfoById(@PathVariable String username){
            return "get user info,username = "+username;
        }
    ①启动服务,
      在浏览器地址栏后缀添加【/users/9999/info.do】时,跳转页面:【get user info,id = 9999】
      当后缀添加【/users/xxxx/info.do】时,跳转页面:【get user info,id = xxxx】
      即:客户端发起请求时,服务端会寻找相应的【@GetMapping后配置的正则参数】来匹配客户端的请求并返回
(13)还可以存在不使用正则表达式,但是URL格式几乎一样的配置,例如
    在"UserController"中:添加:
        /* 还可以存在不使用正则表达式,但是URL格式几乎一样的配置,例如: */
        //http://localhost:8080/springmvc01_war_exploded/users/list/info.do
        @GetMapping("/list/info.do")
        public String getListInfo(){
            return "get user-list info";
        }
    ①启动服务,
      在浏览器地址栏后缀添加【/users/list/info.do】时,跳转页面:【get user-list info】
    ②执行时,如果使用/user/list/info.do,则会匹配到以上代码中的【(13)】中的方法,
      并不会因为这个URL还能匹配第2个方法配置的{username:[a-zA-Z]+}而产生冲突,
      所以,使用了占位符的做法并不影响精准匹配的路径

02.RESTful基础小结:【ppt:16页——17页】
[★★★☆☆] RESTful是一种风格,并不是规范或标准
[★★★☆☆] RESTful的典型表现有:
           – 将某些唯一的请求参数的值放在URL中,使之成为URL的一部分
           – 根据希望实现的数据操作不同而使用不同的请求方式
[★★★★★] 将请求参数设计到URL中时,可以在@RequestMapping或相关注解(@GetMapping)
           配置URL时使用{名称}进行占位
[★★★★★] 在处理请求的方法的参数列表中,为占位符对应的参数添加 @PathVariableSpring MVC会自动获取URL中的值注入到参数中
[★★★★★] 当占位符中的名称与方法参数的名称不一致时,需要配置 @PathVariable注解的参数
[★★★★★]URL的占位符中,可以使用正则表达式限制占位符的文本格式,不冲突的多个使用了
           正则表达式的相同格式URL可以共存
[★★★★★] 使用了正则表达式的占位符的,与不使用占位符的,相同格式的URL可以共存,
           且优先匹配到不使用占位符的
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
2、响应正文的结果类型【ppt:19页——22页】
01.响应正文的结果类型
(1)当响应正文时,只要方法的返回值是自定义的数据类型,
   则Spring MVC 框架就一定会调用jackson-databind中的转换器,
   就可以将结果转换为JSON格式的字符串
(2)通常,在项目开发中,【会定义一个“通用”的数据类型,
   无论是哪个控制器的哪个处理请求的方法,最终都将返回此类型】

(3)显示的通用返回类型如下:
   ①首先将控制器类:UserController中的方法的类型都改为:JsonResult
     为了避免语法出错,方法中都返回null: 【return null;】
   ②在springmvc类下:创建包web:下创建JsonResult类,
     写一个静态方法带参数的ok
   ③在UserController中:info里写一句:return JsonResult.ok(user);
   ④在JsonResult类再定义一个ok不传参的方法和一个失败fail的方法
   ⑤在web包里新建枚举类型的state类,
   ⑥在UserController中:login方法添加参数:
           //假设root/1234是正确的账号
           if ("root".equals(username)) {
               if ("1234".equals(password)) {
                   return JsonResult.ok();
               } else {
                   return JsonResult.fail(State.ERR_USERNAME, "登录失败,密码错误!");
               }
           } else {
               return JsonResult.fail(State.ERR_PASSWORD, "登录失败,用户名错误!");
           }
   ⑦
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
3、统一处理异常【ppt:23页——33页】
01.统一处理异常
(1)Spring MVC框架提供了统一处理异常的机制,使得特定种类的异常对应一段特定的代码,
   后续,当编写代码时,无论在任何位置,都可以将异常 直接抛出,由统一处理异常的代码进行处理即可
   – 无论哪种异常(包括RuntimeException及其子孙类异常),
     只要没有显式的使用try...catch语法进行捕获并处理,均视为抛出
(2)关于统一处理异常,需要自定义方法对异常进行处理,关于此方法:
   – 注解:必须【添加@ExceptionHandler注解】
   – 访问权限:应该是公有的
   – 返回值类型:可参考处理请求的方法的返回值类型
   – 方法名称:自定义
   – 参数列表:必须包含1个异常类型的参数,并且可按需添加HttpServletRequestHttpServletResponse等少量特定的类型的参数,不可以随意添加参数
     ①在UserController中:添加:
         @ExceptionHandler
         public String handleException(NullPointerException e) {
            return "Error, NullPointerException!";
         }
         注意:以上处理异常的代码,只能作用于当前控制器类中各个处理请求的方法,
              对其它控制器类的中代码并不产生任何影响,也就无法处理其它控制类中处理请求时出现的异常!

(3)①controller中:创建BlogController类
   ②controller中:创建包handler 下创建GlobalExceptionHandler类
     为保证更合理的处理异常,应该:
      – 将处理异常的代码放在专门的类(GlobalExceptionHandler)中
      – 在此类上添加@ControllerAdvice注解
      – 由于目前主流的响应方式都是“响应正文”的,
        则可以将@ControllerAdvice替换为 @RestControllerAdvice

(4)①处理的方法的参数中的异常类型,就是Spring MVC框架将统一处理的异常类型,
     例如将参数声明为Throwable类型时,所有异常都可被此方法进行处理!但是,
     在处理过程中,应该判断当前异常对象所归属的类型,以针对不同类型的异常进行不同的处理
        在GlobalExceptionHandler@ExceptionHandler
        public String handleException(Throwable e) {
            return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
        }Spring MVC允许存在多个统一处理异常的方法,这些方法可以在不同的类中,
     只要处理的异常的类型不冲突即可(允许继承)
     – 例如:如果有2个或多个方法都处理NullPointerException,是错误的
     – 例如:如果同时存在2个方法,分别处理NullPointerExceptionRuntimeException,是允许的
         在GlobalExceptionHandler//范围越小越优先匹配到
         @ExceptionHandler
         public String handleException(NullPointerException e){
             return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
         }

         @ExceptionHandler
         public String handleException(NumberFormatException e){
             return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
         }

         @ExceptionHandler
         public String handleException(RuntimeException e){
             return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
         }

         @ExceptionHandler
         public String handleException(Throwable e) {
             e.printStackTrace();//在控制台输出异常信息,但页面不会提示
             return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
         }
(5)在开发实践中,通常都会有handleThrowable()方法(方法名是自定义的),
   以避免某个异常没有被处理而导致500错误!
   – 此方法中应该输出异常的相关信息,甚至跟踪信息,否则,当程序运行此至处时,可能不便于
     观察、分析、记录出现异常
(6)@ExceptionHandler注解还可用于配置被注解的方法能够处理的异常的类型,
   其效力的优先级高于在方法的参数上指定异常类型
(7)在开发实践中,建议为每一个@ExceptionHandler配置注解参数,在注解参数中指定需要处理异常的类型,
   而处理异常的方法的参数类型只需要 包含@ExceptionHandler配置的类型即可,甚至可以是Throwable
       @ExceptionHandler({NullPointerException.class, ClassCastException.class})
       public String handleException(Throwable e){
           return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
       }

       @ExceptionHandler(NumberFormatException.class)
       public String handleException(RuntimeException e){
           return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
       }

       @ExceptionHandler(Throwable.class)
       public String handleThrowable(Throwable e) {
           e.printStackTrace();//在控制台输出异常信息,便于分析问题
           return "Error, " + e.getClass().getName() + ", message : " + e.getMessage();
       }
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
4、 拦截器【ppt:34页——41页】
01.拦截器(Interceptor(1)Spring MVC框架中,拦截器是可以运行在所有控制器处理请求之前和之后的一种组件,并且,
   如果拦截器运行在控制器处理请求之前,还可以选择对当前请求进行阻止或放行。
(2)注意:拦截器的目的并不是“拦截下来后阻止运行”,更多的是“拦截下来后执行某些代码”,
   其优势在于:可作用于若干种不同请求的处理过程;即写一个拦截器,就可以在很多种请求的处理过程中被执行。
(3)只要是若干种不同的请求过程中都需要执行同样的或高度相似的代码,都可以使用拦截器解决,
   典型的例如验证用户是否已经登录等等。

(4)需要使用拦截器时,需要自定义类,实现HandlerInterceptor接口,例如:
   ①在springmvc包中:创建interceptor包:创建LoginInterceptor类 并实现HandlerInterceptor接口
   ②alt+insert调用实现的方法:按implements Methods 全选自动生成方法
     方法中的语句会报错,删掉即可! 发现有一个还会报错,添加【return false;】即可
   ③每个拦截器都必须注册才会被启用,注册过程通过重写 WebMvcConfigure接口中的
     addInterceptors()方法即可,例如:
     在springmvc中添加:
         @Autowired
         LoginInterceptor loginInterceptor;

         /*
         每个拦截器都必须注册才会被启用,注册过程通过重写 WebMvcConfigure接口中的
         addInterceptors()方法即可,例如:
          */
         public void addInterceptors(InterceptorRegistry registry){
             //拦截器指定要作用于哪些请求路径:例如:"/users/hello.do"
             registry.addInterceptor(loginInterceptor)
                     .addPathPatterns("/users/hello.do");

         }
     ④启动服务,后缀加【/users/hello.do】会发现页面时空白,因为被拦截器拦截了
(5)①当进行访问时,在浏览器窗口中将看到一片空白,在Tomcat控制台可以看到preHandle()方法已经执行
   ②当把拦截器中preHandle()方法的返回值改为true时,在Tomcat控制台可以看到依次执行了:
     preHandle() -> 控制器中处理请求的方法 -> postHandle() -> afterCompletion()preHandle()方法的返回值为true时,表示“放行”,为false时,表示 “阻止”
   ⑤此时在【LoginInterceptor、】
(6)关于注册拦截器时的配置,使用链式语法可以先调用addInterceptor()方法添加拦截器,
   然后调用addPathPatter()方法添加哪些路径需要被拦截,此方法的参数可以是String...,
   也可以是List<String>,在编写路径值时,可以使用*作为通配符,例如配置为/user/*,
   则可以匹配/user/login.do、 /user/reg.do等所有直接在/user下的路径,
   但不能匹配/user/1/info.do,
   如果需要匹配若干层级,必须使用2个连续的星号,例如配置为/user/**
   在SpringMvcConfig:
       public void addInterceptors(InterceptorRegistry registry){
           //拦截器指定要作用于哪些请求路径:例如:"/users/hello.do"
           List<String> pathPatterns = new ArrayList<>();
           pathPatterns.add("/users/**");
           registry.addInterceptor(loginInterceptor)
                   .addPathPatterns(pathPatterns);
       }
(7)一旦使用通配符,就有可能导致匹配的范围过大,例如配置为/user/**时,还可以匹配
   到/user/reg.do(注册)和/user/login.do(登录),如果此拦截器是用于“验证用户是否登录”的,
   则不应该对这2个路径进行处理,那么,配置拦截器时,还可以在链式语法中
   调用excludePathPatterns()方法,以添加“排除路径”(例外)
   在SpringMvcConfig:addInterceptors方法中添加:
     .excludePathPatterns("/user/login.do", "/user/reg.do");
——  ——  ——  ——  ——  ——  ——  ——  ——  —— ——  ——  ——  ——  ——  ——  ——  ——  ——  ——
5、Spring MVC小结【ppt:42页——48页】
(1)关于Spring MVC框架,你应该(1/5):
   – 理解Spring MVC框架的作用————>接收请求(配置mapping),响应结果(响应正文),处理异常
   – 掌握创建基于Maven的运行在Tomcat的Webapp
   – 认识基础的依赖项
      – spring-webmvc、 javax.servlet-api、 jackson-databind
   – 掌握配置Spring MVC的运行环境(使得控制器能接收到请求)

(2)关于Spring MVC框架,你应该(2/5):
   – 掌握以下注解的使用:
   – @Controller(控制器) / @RestController(基于控制器的响应正文)
   – @ResponseBody(响应正文)
   – @RequestMapping(配置请求路径) / @GetMapping / @PostMapping ...
   – @RequestParam(参数的注解,为请求的参数另外取个名字;或者必须是该命名的名字)
     / @PathVariable
   – @ExceptionHandler(处理异常的方法需要添加的注解;配置参数来表示接下来需要处理哪些异常)
     / @ControllerAdvice(不是响应正文的) / @RestControllerAdvice(响应正文的)
   – @EnableWebMvc(需要响应json字符串结果的时候,必须加在springmvc配置类上的注解)

(3)关于Spring MVC框架,你应该(3/5):
   – 掌握接收请求参数的方式:
   – 将请求参数直接声明在处理请求的方法的参数列表中
   – 将若干个请求参数进行封装,并将封装的类型声明在处理请求的方法的参数列表中
   – 如果是URL中的路径,则需要使用@PathVariable

(4)关于Spring MVC框架,你应该(4/5):
   – 掌握响应JSON格式的正文的做法:
   – 处理请求的方法必须添加@ResponseBody,或当前控制器类添加@ResponseBody,
     或当前控制器类添加@RestController
   – 在Spring MVC配置类上添加@EnableWebMvc,不加会出现406
   – 在项目的pom.xml中添加了jackson-databind
   – 处理请求的方法返回【自定义】的数据类型:JsonResult类

(5)Spring MVC小结 ● 关于Spring MVC框架,你应该(5/5):
   – 掌握 响应JSON格式的正文时,统一的响应类型的类的设计
   – 了解 RESTful风格
   – 掌握 统一处理异常
   – 掌握 拦截器的创建与配置

00.关于注解:

 @EnableWebMvc         | 添加在Spring MVC配置类上的注解,
 						 当响应正文,且响应的是JSON格式的结果时,
 						 必须添加此注解,否则将导致406错误,
 						 另外,在Spring Boot项目中不需要手动添加 
 						 
 @ResponseBody         | 响应正文,可添加在处理请求/处理异常的方法之前,
 						 将作用于对应的方法,或添加在类之前,将作用于类中所有处理请求/处理异常的方法
 					
 @RestController       | 等效于@Controller + @ResponseBody    

                        
 @RequestMapping       | 通常添加在类上,用于配置请求路径的前缀部分,
 						 也使用produces属性配置此控制器类中所有处理请求的方法响应时的文档类型,
 						 例如:在类上配置为:		
 						 @RequestMapping(value="user",produces="application/json; 
 						 							  charset=utf-8")
 						 							  
 @GetMapping           | 是将请求类型限制为GET@RequestMapping,通常添加在处理请求的方法上,
 						 用于配置此方法映射的请求路径  						 
 @PostMapping          | 参考@GetMapping         
                                      |
 @RequestParam         | 添加在处理请求的方法的参数之前,可用于:
 						 指定请求参数名称、配置是否必须提交此请求参数、配置请求参数的默认值 
 						 
 @PathVariable         | 添加在处理请求的方法的参数之前,
 						 仅当在请求路径中使用占位符匹配变量值时使用,
 						 如果占位符中的名称与方法参数名称不同,还可以配置此注解的参数,
 						 以指定占位符中的名称
 						 
 @ExceptionHandler     | 添加在处理异常的方法之前,可通过注解参数配置需要处理的异常类型 
 
 @ControllerAdvice     | 添加在类之前,此类中的代码在处理任何请求时都会生效           
 
 @RestControllerAdvice | 等效于@ControllerAdvice + @ResponseBody     

01.关于Spring MVC框架

(1)Spring MVC是基于Spring框架基础之上的,主要解决了后端服务器接收客户端提交的请求,
   并给予响应的相关问题
   
(2)MVC = Model + View + Controller,它们分别是:
   –Model:数据模型,通常由业务逻辑层(Service Layer)和数据访问层(Data Access Object Layer)共同构成
   –View:视图
   –Controller:控制器
   –MVC为项目中代码的职责划分提供了参考
   需要注意:Spring MVC框架只关心 V-C 之间的交互,与 M 其实没有任何关系。

(3)案例目标:
   开发使用Spring MVC框架的项目,将其部署到Tomcat,
   最终,部署此项目启动Tomcat,
   用户在浏览器中输入指定的URL提交请求后,项目可以对此进行简单的响应.
   
(4)Spring MVC的核心执行流程图如下所示:

在这里插入图片描述

02.创建工程前首先配置Tomcat:

请参考:
http://doc.canglaoshi.org/doc/idea_tomcat/index.html 
创建名为:springmvc01 的项目

1.苍老师网站——>找到:Tomcat模块——>Tomcat 9.0.37 Windows官网下载
在这里插入图片描述

2.点击创建新工程maven,此处建了一个"SpringMvc"的maven,以后的springmvc工程都放到此工程下,按图打钩和选择对应的webapp依赖,点击Next
在这里插入图片描述
3.上一步骤点击下一步后,跳到此页面:输入分组和工程名;点击Next
在这里插入图片描述
4.此处注意下载依赖的"settings.xml"文件建议是阿里云的,没有设置的话在苍老师网站找到即可
在这里插入图片描述
5.由于main包内没有java和resources两个文件夹,需要我们手动添加,右键main时选择目录选项,双击"Java"即可
在这里插入图片描述
6.
在这里插入图片描述
7.打开File 选择——>Project Structure按步骤开始:
在这里插入图片描述
8.选择idea上边窗口栏,启动项目的位置,按如图步骤:
在这里插入图片描述
9.选择该"Tomcat…"文件夹,注意是有效路径才可
在这里插入图片描述
10.
在这里插入图片描述
11.
在这里插入图片描述
12.第一次配置时建议在最上边name处输入和该版本一样的名字,便于启动项目好找,点击右下角:“Fix”
在这里插入图片描述
13.选择下边文本长的那一项:
在这里插入图片描述
14.
在这里插入图片描述
15.按从左往右的步骤:
在这里插入图片描述
16.
在这里插入图片描述
17.
在这里插入图片描述
18.若出现此情况,需要将第12步的————>"浏览器图标"指定才可。关闭Tomcat后重启即可!
在这里插入图片描述

03.添加依赖:

   在《springmvc01》中的:pom.xml中找到【<dependencies>.....</dependencies>:
   在其中添加两个依赖项
   <!--【添加spring-webmvc依赖项:】-->
     <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.14</version>
     </dependency>
   <!--添加(后续运行时提示不可识别Servlet相关类)依赖项:-->
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>javax.servlet-api</artifactId>
         <version>3.1.0</version>
         <scope>provided</scope>
     </dependency>
     <!--创建项目时自动添加的依赖:-->

注意:以上pom.xml代码中的:
  <scope>provided</scope>
  ————>表示此依赖不会参与测试或部署,因为当Web项目部署到Tomcat中后,
       Tomcat环境会包含此依赖项

04.案例的注意事项:

– 当启动Tomcat时,会自动将项目打包并部署到Tomcat,通过自动打开的浏览器中的URL即可访问主页,
  在URL中有很长一段是例如 springmvc01_war_explored 这一段是不可以删除的,
  其它的各路径必须补充在其之后,例如 /login.do 就必须在此之后 

– 当启动Tomcat时,项目一旦部署成功,就会自动创建并加载
  AbstractAnnotationConfigDispatcherServletInitializer的子类,
  即当前项目中自定义的 SpringMvcInitialier,
  无论这个类放在哪个包中,都会自动创建并加载 

– 由于会自动调用这个类中所有方法,所以会将Spring MVC框架处理的请求路径设置为 *.do, 
  并执行对 cn.tedu.springmvc 的组件扫描,
  进而会创建 UserController 的对象 

– 由于在 UserController 中配置的方法使用了 @RequestMapping("/login.do"),
  则此时还将此方法与/login.do进行了绑定,以至于后续随时访问/login.do时都会执行此方法

关于以上案例的注意事项: 
– 注意:组件扫描必须配置在Spring MVC的配置类中 
– 注意:控制器类上的注解必须是@Controller,不可以是@Component@Service@Repository 
– 各注解的作用并不是注解自身决定的,而是运行环境或框架决定的,
  在Spring框架中, 这4个注解是完全等效的,但是,在部分版本的Spring MVC框架中并不相同 
– 注意:方法返回的值应该是ASCII码字符,
       不推荐使用中文、中文标点符号等非 ASCII码字符,否则可能显示为乱码 
– 某些版本的Spring MVC默认的字符编码是ISO-8859-1,只支持ASCII字符 
– 乱码的问题暂不解决

05.@RequestMapping注解

@RequestMapping注解属性:
@RequestMapping还可以配置: 
– method:请求方式 
– headers:请求头 
– params:请求参数 
– consumes:请求文档类型 
– produces:响应文档类型

如果没有配置method属性,则表示可以使用任何请求方式,包括:
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE 

另外,Spring MVC框架还提供了@RequestMapping的相关注解,
例如: 
– @GetMapping@PostMapping@PutMapping@DeleteMapping 
– 等等


[★★★☆☆] 使用@RequestMapping的主要作用是配置请求路径 

[★★★☆☆] @RequestMapping可以添加在类上,也可以添加在方法上, 同时配置时,
        最终的URL是由类上的与方法上的配置值组合的 

[★★★☆☆] 当需要限制请求方式为某1种时,应该在处理请求的方法上 使用@GetMapping@PostMapping等注解

[★★★★★] 在开发实践中,通常: 
        – 在类上使用@RequestMapping,配置请求路径,并配置produces以指定响应的文档类型 
        – 在方法上使用@GetMapping@PostMapping等限制了请求类型的注解,配置请求路径

04.响应正文(@ResponseBody/@RestController)

[★★★★★] 响应正文的定义:将处理请求的方法的返回值作为响应到客户端的正文数据

[★★★★★] 响应正文的做法:
           – 在方法上添加@ResponseBody
           – 或,在类上添加@ResponseBody
           – 或,在类上使用@RestController取代@Controller
           
[★★★★★] 响应正文的格式:响应的结果通常需要包含多项数据,响应1个字符串并不便于表示这些数据,
           			  通常响应JSON格式的字符串
           
[★★★☆☆] 响应正文的原理:Spring MVC内置了一系列的转换器 (Converter),
   					  根据处理请求的方法的返回值不同,自动选取某个转换器,
   					  将方法的返回值转换为响应到客户端的数据,
           			  当方法的返回值没有匹配的默认转换器时,会自动使用jackson-databind的转换器
           			  
[★★★★★] 当需要响应JSON格式的正文时,你需要:
           – 添加jackson-databind依赖
           – 在Spring MVC配置类上添加@EnableWebMvc注解
           - 自定义类,作为处理请求的方法的返回值类型
           – 类的属性必须添加Setter & Getter
           – 使得处理请求的方法是响应正文的

05.接收请求参数

[★★★★★] 你可以将请求参数一个个的声明为处理请求方法的参数,也可以将多个参数封装到一个自定义类中,
        使用自定义类作为处理请求的方法的参数,Spring MVC框架会自动接收客户端提交的请求参数,
        并用于调用你编写的处理请求的方法
        
[★★★★★] 在大部分情况下,推荐使用将参数封装到自定义类的做法

[★★★★★] 你需要保证非String类型的参数是Spring框架可以成功自动转换类型的,
        或者,对转换失败有进一步的处理,或可以接受转换失败带来的后果

06.课后阅读——关于POJO

POJOPlain Ordinary Java Object,即:普通Java对象 

所有用于封装属性的类型都可以统称为POJO 

常见的POJO的类名后缀有:BODOVODTO等,不同的后缀表示不同的意义,
例如: 
– VO = Value Object / View ObjectDTO = Data Transfer Object

所有POJO类都应该遵循以下规范: 
– 实现Serializable接口 
– 所有属性都是私有的 
– 所有属性都有规范的Setter & Getter 
– 由开发工具生成的,即是规范的 
– 规范的重写了hashCode()equals()2个类型相同、所有属性值都相同的对象,equals()应该返回true,否则,返回false2个类型相同、所有属性值都相同的对象,hashCode()返回值应该相同,否则,不同 
– 由开发工具生成的,即是规范的,不同开发工具生成的方法源码可能不同,这不重要 
– 通常,应该重写toString()

所有POJO类的类名后缀: 
– 在一个项目中,哪些情景下使用哪种后缀并没有统一的规定,通常是各项目内部决定 
– 注意:在为封装属性的类进行命名时,以上BODOVODTO等这些后缀的每一个字母都应该是大写的!

参考资料:
在《阿里巴巴Java开发手册》中:
要求POJO类名后缀的每个字母都是大写的
要求不允许使用POJO作为 类名后缀
要求所有POJO类的属性都不要设置默认值
提供了使用类名后缀的参考, 但不是强制约定

————————————————————————————

4、MyBatis

01.关于Mybatis框架

Mybatis的主要作用是: ——————> 快速实现对关系型数据库中的数据进行访问的框架 
– 在原生的Java技术中,需要使用JDBC实现对数据库中的数据访问,执行过程繁琐且相对固定,
  使用框架可以有效的提高开发效率。

(1)设置: AdminMapper.xml 配置sql文件:中的sql语句背景颜色深问题:

在这里插入图片描述
点击File————>settings: 选中其中一个即可
在这里插入图片描述
可以发现,sql语句的背景颜色变浅了!:
在这里插入图片描述

02.创建Mybatis-Spring工程并添加依赖

(1)Mybatis可以不依赖于Spring等框架直接使用的,
   但是,就需要进行大量的配置,前期配置工作量较大,
   基于Spring框架目前是业内使用的标准之一,
   所以,通常会整合SpringMybatis,以减少配置 

(2)在创建工程时,创建普通的Maven工程即可(不需要选择特定的骨架)


(3)添加7个依赖项:
   其中《添加JUnit测试的依赖项:junit-jupiter-api》的目的:
   之前我们通过一个自定义的运行类添加main方法进行测试,
   测试一个就需要写一个方法,太麻烦;
   解决办法:
   添加测试的依赖项,在测试类中分别对想测试的类进行测试!更便捷响应更快!
   
    <!--创建【<dependencies>...</dependencies>】并在里边添加依赖:-->
    <dependencies>
        <!--添加Mybatis的依赖项:mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        
        <!--添加Mybatis整合Spring的依赖项:mybatis-spring-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        
        <!--添加Spring的依赖项:spring-context-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.14</version>
        </dependency>
        
        <!--添加Spring JDBC的依赖项:spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.14</version>
        </dependency>
        
        <!--添加MySQL连接的依赖项:mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        
        <!--添加数据库连接池的依赖项:commons-dbcp2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.8.0</version>
        </dependency>
        
        <!--
        添加JUnit测试的依赖项:junit-jupiter-api
        scope:作用域 指定test后整个test文件夹不参与编译
        -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

03.配置Mybatis开发环境

(1)IDEA中配置可视化数据库视图:

  • 可以参考链接:http://doc.canglaoshi.org/doc/idea_database/index.html

1.在创建工程时,创建普通的Maven工程即可(不需要选择特定的骨架)
在这里插入图片描述
2.
在这里插入图片描述
3.若右侧没有"Database"则点击左侧"View",按步骤点击,以后则可在界面显示"Database" 了!
在这里插入图片描述

4.点击 加号 ,选择数据源,来增加连接数据库的配置,选择MariaDB或者ysql都可以
在这里插入图片描述
5.下载丢失的驱动文件,点击后可能下载不成功,
在这里插入图片描述
6.先演示不成功怎么做:
若没有下载成功的话,
解决方案:
点击mysql,会发现右侧是红色,证明没有导入该文件,那点击加号,自定义添加文件的位置
在这里插入图片描述
6.1.这个需要添加的文件,在".m2"中:路径:C:\Users\TR.m2\repository\mysql\mysql-connector-java\8.0.28
在这里插入图片描述
6.2.记住这个路径,点击加号,选择文件位置(我的是在上面那个路径中),会发现上边【Class】处由红色变为黑色了。成功导入了!
在这里插入图片描述
7.若是在第5步点击"下载"成功了:
(1)点击User,并输入用户名root(此处是自己mysql数据库的用户名,若不知道,按以下步骤查询:)
①mysql查看用户名的方法:首先进入mysql并启动数据库;然后连接到数据库;最后输入“select host,user from mysql.user;”命令查看用户名以及信息即可。
(2)输入自己的password ——>root
(3)Database:表示你要连接哪个数据库
①可以在mysql客户端,使用查询数据库命令show、 databases; 命令来查询有哪些数据库
(4)在【Database】出输入想要连接的数据库,点击ok即可
在这里插入图片描述
8.此时跳转到了成功连接数据库的页面,若想连接多个数据库,
再次点击加号,选择数据源 ; ————按刚才的步骤来一遍即可
在这里插入图片描述
9.点击右侧下箭头展开数据库,若没有数据库
点击"1 of 13",选中想打开的数据库并打钩,可以发现再次点击下箭头已经有数据库的信息了:
在这里插入图片描述
在这里插入图片描述
10.可以发现在idea中,我们连接数据库查看信息和在客户端输入指令后查看的内容是一样的:
在这里插入图片描述
11.可以看到,在idea中双击某个表格出现的内容,和在数据库中输入指令查询到的内容是一样的,但是idea中的显示更美观
在这里插入图片描述
12.我们可以点击加号来添加行,也可以右键:"Delete Row"来删除行
在这里插入图片描述
13.当然我们也可以直接修改数据,但要点击提交按钮来保存否则刚才修改是不生效的
在这里插入图片描述
14.点击右侧【QL】按钮,选择控制台,输入查询语句,即可在下面显示查询出来的结果
下面有两个按钮,Output表示我们执行过程中的信息,
cs.banner则表示我们查询到的表的数据
在这里插入图片描述
15.我们可以通过输入 left join来获得这个工具的提示来知道表和表之间的关联在这里插入图片描述

16.我们可以点击【QL】来选择【new Query Console 】在开一个执行sql语句的控制台,
当然我们也可以【修改此窗口的名字】来更明确自己的多窗口想要进行工作的分类
在这里插入图片描述
在这里插入图片描述
16.我们修改的名字在右侧也可以看到被保存了下来,方便以后我们来直接使用
在这里插入图片描述

(2)配置Mybatis的开发环境

(0)添加测试类:
   在src/test下的绿色java包下,创建和上边同级的包:cn.tedu.mybatis
   再在创建 MybatisTests 类
   添加测试代码:
    @Test
    public void contextLoads() {
        System.out.println("MybatisTests.contextLoads()");
    }
    运行此类,可以在控制台看到【MybatisTests.contextLoads()】,
             和绿色的勾,
             成功!
——————————————————————————————————————————————————————————————
(1)登录MySQL客户端控制台,创建名为mall_ams的数据库:
   CREATE DATABASE mall_ams;
——————————————————————————————————————————————————————————————
(2)通过数据库视图的Console面板创建数据表:Idea中,点击右侧的【Database】,
   点击【QL】图标打开可以输入sql语句的控制台,注意不要开成其他的数据库!
   执行下面的指令:
   create table ams_admin ( 
	 id bigint unsigned auto_increment, 
     username varchar(50) default null unique comment '用户名', 
     password char(64) default null comment '密码(密文)', 
     nickname varchar(50) default null comment '昵称', 
     avatar varchar(255) default null comment '头像URL', 
     phone varchar(50) default null unique comment '手机号码', 
     email varchar(50) default null unique comment '电子邮箱', 
     description varchar(255) default null comment '描述', 
     is_enable tinyint unsigned default 0 comment '是否启用,1=启用,0=未启用', 
     last_login_ip varchar(50) default null comment '最后登录IP地址(冗余)', 
     login_count int unsigned default 0 comment '累计登录次数(冗余)', 
     gmt_last_login datetime default null comment '最后登录时间(冗余)', 
     gmt_create datetime default null comment '数据创建时间', 
     gmt_modified datetime default null comment '数据最后修改时间', 
     primary key (id) 
   ) comment '管理员' charset utf8mb4;
——————————————————————————————————————————————————————————————
(3)在src/main/resources下创建【 datasource.properties 】配置文件,
   用于配置连接数据库的参数,
   注:后缀为【properties】的文件在编译后仍然不会改变,不像Java文件,方便以后改动!
   例如:
   
   datasource.url=jdbc:mysql://localhost:3306/mall_ams?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai 
   datasource.driver=com.mysql.cj.jdbc.Driver
   datasource.username=root
   datasource.password=root
   注意:以上代码【句后不能有空格!!】否则会报【类不能发现异常】!!
   提示:以上配置中的属性名称【应该添加前缀:什么都可以】,避免与系统环境变量冲突, 
        例如在Windows操作系统中,username则表示登录系统的用户名
——————————————————————————————————————————————————————————————
(4)在src/main下:cn.tedu.mybatis包下(不存在,则创建)创建SpringConfig类,
   读取以上配置文件:
   @Configuration //配置
   //读取properties类型(即创建的datasource.properties文件)的配置文件,
   // -读取到的值将存入到Spring容器的Environment对象中
   @PropertySource("classpath:datasource.properties") //
   public class SpringConfig {
   }
——————————————————————————————————————————————————————————————
(5)在测试方法中补充测试代码:
   用来测试能否读取SpringConfig类中 @PropertySource注解读取的配置文件
    @Test
    public void contextLoads() {
        System.out.println("MybatisTests.contextLoads()");
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        ConfigurableEnvironment environment = ac.getEnvironment();
        System.out.println(environment.getProperty("datasource.url"));
        System.out.println(environment.getProperty("datasource.driver"));
        System.out.println(environment.getProperty("datasource.username"));
        System.out.println(environment.getProperty("datasource.password"));
        ac.close();
    }
    运行此类,我们可以在控制台显示上边配置的信息:
    控制台显示:
    MybatisTests.contextLoads()
    jdbc:mysql://localhost:3306/mall_ams?    useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    com.mysql.cj.jdbc.Driver 
    root 
    root 
——————————————————————————————————————————————————————————————
(6)SpringConfig中配置一个DataSource对象用于返回datasource的值:
   注意导入的包:
   import org.springframework.context.annotation.Bean;
   import org.apache.commons.dbcp2.BasicDataSource;
   import org.springframework.core.env.Environment;
   
    @Bean
    public DataSource dataSource(Environment env) {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUrl(env.getProperty("datasource.url"));
        dataSource.setDriverClassName(env.getProperty("datasource.driver"));
        dataSource.setUsername(env.getProperty("datasource.username"));
        dataSource.setPassword(env.getProperty("datasource.password"));
        return dataSource;
    }
——————————————————————————————————————————————————————————————
(7)并在测试类中添加新的测试方法,
   以尝试获取数据库的连接对象,检测是否可以正确连接到数据库
    @Test
    public void testConnection() throws Exception {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = ac.getBean("dataSource", DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        ac.close();
    }
    运行此类,通过控制台的输出可以发现已经正确连接到数据库:
    控制台显示:
    MybatisTests.contextLoads()
    jdbc:mysql://localhost:3306/mall_ams?useUnicode=true&characterEncoding=UTF-   8&serverTimezone=Asia/Shanghai
    com.mysql.cj.jdbc.Driver 
    root 
    root

04.Mybatis的基本使用

(1)当使用Mybatis实现数据访问时,需要干两件事: 
   – 编写数据访问的抽象方法 
   – 配置抽象方法对应的SQL语句

(2)/1.关于抽象方法: 
   – 必须定义在某个接口中,这样的接口通常使用Mapper作为名称的后缀,例如 AdminMapperMybatis框架底层将通过接口代理模式来实现 
   – 方法的返回值类型: 
      – 如果要执行的数据操作是增、删、改类型的,
        统一使用int作为返回值类型,表示“受影响的行数”,
        也可以使用void,但是不推荐 
      – 如果要执行的是查询操作,返回值类型只需要能够装载所需的数据即可
    
   /2.关于抽象方法: 
   – 方法的名称:自定义,不要重载,建议风格如下: 
      – 插入数据使用`insert`作为方法名称中的前缀或关键字 
      – 删除数据使用`delete`作为方法名称中的前缀或关键字 
      – 更新数据使用`update`作为方法名称中的前缀或关键字 
      – 查询数据时: 
         如果是统计,使用`count`作为方法名称中的前缀或关键字 
         如果是单个数据,使用`get`或`find`作为方法名称中的前缀或关键字 
         如果是列表,使用`list`作为方法名称中的前缀或关键字 
         如果操作数据时有条件,可在以上前缀或关键字右侧添加`by字段名`,
          例如 `deleteById` 

   /3.关于抽象方法: 
   – 方法的参数列表:取决于需要执行的SQL语句中有哪些参数,
                   如果有多个参数,可将这些参数封装到同一个类型中,
                   使用封装的类型作为方法的参数类
————————————————————————————————————————————————————————————
(3)假设当需要实现“插入一条管理员数据”,则需要执行的SQL语句大致是:
   insert into ams_admin (username, password, nickname, avatar, phone, email, description, is_enable, last_login_ip, login_count, gmt_last_login, gmt_create, gmt_modified) values (?,?,? ... ?);
————————————————————————————————————————————————————————————
(4)由于以上SQL语句中的参数数量较多,则应该将它们封装起来,
   则在src/main/java/cn/tedu/mybatis下:创建包:entity 下创建实体类:Admin,并实现序列化接口
   实体类:Admin————>实现序列化接口Serializable(标识接口) 
   声明一系列的属性,对应以上各参数值:
    private String username;
    private String password;
    private String nickname;
    private String avatar;
    private String phone;
    private String email;
    private String description;
    private Integer isEnable;
    private String lastLoginIp;
    private Integer loginCount;
    private LocalDateTime gmtLastLogin;
    private LocalDateTime gmtCreate;
    private LocalDateTime gmtModified;
    并调用Setters & Getters方法 ; 
    并重写toString()  ; equals和hashcode(添加hashcode时  Next——>Next——>Finish)
————————————————————————————————————————————————————————————   
(5)在src/main/java/cn/tedu/mybatis下:创建包:mapper 下创建接口:AdminMapper
   并在接口中添加“插入1条管理员数据”的抽象方法: 
    /** 添加“插入1条管理员数据”的抽象方法: */
    int insert(Admin admin);
————————————————————————————————————————————————————————————
(6)所有用于Mybatis处理数据的接口都必须被Mybatis识别,有2种做法: 
   – 【不推荐】在每个接口上添加@Mapper注解 
   – 【推荐】在配置类SpringConfig上添加@MapperScan注解,指定接口所在的根包
      @Configuration 
      @PropertySource("classpath:datasource.properties") 
      @MapperScan("cn.tedu.mybatis.mapper") 
      public class SpringConfig { // ... ... }
   注意:因为Mybatis会扫描以上配置的包,并自动生成包中各接口中的代理对象,
        所以,千万不要放其它接口文件
————————————————————————————————————————————————————————————
(7)接下来,需要配置抽象方法对应的SQL语句,
   之前我们在mapper包内的方法上添加对应的注解,后面跟sql语句,
     若是有很多属性我们需要写很长的sql语句,但是我们现在不推荐这样;
   【我们推荐————>把这些SQL语句配置在 XML 文件中】
   ● 可以从苍老师网站:
     点击配置文件下载
     点击MyBatis Mapper映射文件 Mapper.xml.zip 进行下载,
或直接点击 http://doc.canglaoshi.org/config/Mapper.xml.zip 下载 XML文件。
     解压后得到:名为:SomeMapper.xml 的文件
     在项目的src/main/resources下创建mapper文件夹,
     并将下载得到的XML文件复制到此文件夹中,内容是:
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    <mapper namespace="">
    </mapper>
————————————————————————————————————————————————————————————     
(8)打开XML文件夹,进行配置(1/2):   
   <?xml version="1.0" encoding="UTF-8" ?>
   <!DOCTYPE mapper
     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

   <!--此文件必须使用mapper作为根级节点-->
   <!--namespace属性:必须的。用于指定此 XML 文件对应的接口,取值为接口的全限定名-->
   <mapper namespace="cn.tedu.mybatis.mapper.AdminMapper">

    <!--根据要执行的数据操作的类型来选择insert/delete/update/select节点-->
    <!--节点的id内容是抽象方法的名称(仅名称)-->
    <!--节点的内部编写SQL语句-->
    <!--SQL语句中的参数值使用#{}格式的占位符-->
    <insert id="insert">

    </insert>

</mapper> 
————————————————————————————————————————————————————————————     
(9)打开XML文件夹,进行配置(2/2):
   在上边的【<insert id="insert">中添加】插入“ams_admin”表格 的数据
   insert into ams_admin (
    username, password, nickname, avatar,
    phone, email, description, is_enable, last_login_ip,
    login_count, gmt_last_login, gmt_create, gmt_modified
    )
    values ( #{username}, #{password}, #{nickname}, #{avatar},
             #{phone}, #{email}, #{description}, #{isEnable},
             #{lastLoginIp}, #{loginCount}, #{gmtLastLogin},
             #{gmtCreate}, #{gmtModified}
    )
—————————————————————————————————————————————————————————————————      
(10)最后,还需要将DataSource配置给Mybatis框架,
    并且,为Mybatis配置这些XML文件的路径,
    这2项配置都将通过配置 SqlSessionFactoryBean 来完成
——————————————————————————————————————————————————————————————————  
(11)先在 datasource.properties 中补充一条配置:    
    mybatis.mapper-locations=classpath:mapper/AdminMapper.xml
——————————————————————————————————————————————————————————————————
(12)将src/main/resources/mapper下的 【SomeMapper.xml】 改为 【AdminMapper.xml】
——————————————————————————————————————————————————————————————————
(13)然后【在SpringConfig配置类中】【创建SqlSessionFactoryBean类型的对象】:
    ————>基于spring的自动装配机制:Mybatis规定:需要配置SqlSessionFactoryBean(Sql会话工厂Bean)
    /**
     * Mybatis规定:需要配置SqlSessionFactoryBean(Sql会话工厂Bean)
     *
     * 注://根据spring自动装配机制,添加该方法的参数(前提是存在于spring中)
     *
     */
    @Bean //该注解的作用:让spring创建对象
    public SqlSessionFactoryBean sqlSessionFactoryBean(
            DataSource dataSource, @Value("${mybatis.mapper-locations}")Resource mapperLocations){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(mapperLocations);
        return sqlSessionFactoryBean;
    }
——————————————————————————————————————————————————————————————————
(14)在绿色的java包内
    测试类中补充测试方法,
    以检验是否可以通过调用AdminMapperinsert()方法插入数据:
    
    /**
     * 检验是否可以通过调用AdminMapper的 insert()方法插入数据:
     */
    @Test
    public void testInsert() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        Admin admin = new Admin();
        //注意此处插入username时,当插入的用户名相同时,
        // 因为在数据库的表ams_admin表格查询表结构(desc desc ams_admin;)
        // 发现username设置了唯一约束
        admin.setUsername("admin0021");
        admin.setPassword("12345678");
        System.out.println("插入前" + admin);
        adminMapper.insert(admin);
        System.out.println("插入后" + admin);
        ac.close();
    }
    
    运行此类,发现运行成功,
    控制台显示:
    
    插入前Admin{id=null, username='admin002', password='12345678', nickname='null', avatar='null', phone='null', email='null', description='null', isEnable=null, lastLoginIp='null', loginCount=null, gmtLastLogin=null, gmtCreate=null, gmtModified=null}
    插入后Admin{id=null, username='admin002', password='12345678', nickname='null', avatar='null', phone='null', email='null', description='null', isEnable=null, lastLoginIp='null', loginCount=null, gmtLastLogin=null, gmtCreate=null, gmtModified=null}
    
    发现插入前后 id 都为null,
    在mysql中查看表格,发现上边添加的用户名密码已经被成功添加! 
——————————————————————————————————————————————————————————————————   

05.获取新增的数据的自动编号的id

AdminMapper.xml:用来配置sql语句的文件



(1)获取新增的数据的自动编号的id
    在Admin实体类中:
    如果某数据的id是自动编号,当需要获取新增的数据的id时,
    需要先使得插入的数据类型中有id对应的属性,则在Admin类中添加id属性:
    private Long id;
    并把之前的所有方法删掉重新添加
——————————————————————————————————————————————————————————————————  
(2)接下来,在配置sql的文件【AdminMapper.xml】中:
   在<insert>节点中配置2个属性,分别是useGeneratedKeys和 keyProperty:
   
   <insert id="insert" useGeneratedKeys="true" keyProperty="id">
   原有代码
   </insert>
   
   当配置完成后,Mybatis执行此插入数据的操作后,
   会将自动编号的id赋值到参数Admin admin的id属性中,
   以上keyProperty指的就是将自动编号的值放回到参数对象的哪个属性中
——————————————————————————————————————————————————————————————————  
(3)在测试类中进行测试:
    /**
     * 检验是否可以通过调用AdminMapper的 insert()方法插入数据:
     */
    @Test
    public void testInsert() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        Admin admin = new Admin();
        //注意此处插入username时,当插入的用户名相同时,
        // 因为在数据库的表ams_admin表格查询表结构(desc desc ams_admin;)
        // 发现username设置了唯一约束
        admin.setUsername("admin0111");
        admin.setPassword("12345678");
        System.out.println("插入前" + admin);
        adminMapper.insert(admin);
        System.out.println("插入后" + admin);
        ac.close();
    }
   
    运行此类的测试方法,
    控制台显示:
    
    插入前Admin{id=null, username='admin0111', password='12345678', nickname='null', 
    		   avatar='null', phone='null', email='null', description='null', 
    		   isEnable=null, lastLoginIp='null', loginCount=null, gmtLastLogin=null, 
    		   gmtCreate=null, gmtModified=null}
    插入后Admin{id=5, username='admin0111', password='12345678', nickname='null',
    		   avatar='null', phone='null', email='null', description='null', 
    		   isEnable=null, lastLoginIp='null', loginCount=null, gmtLastLogin=null, 
    		   gmtCreate=null, gmtModified=null}
    
    发现 id 插入前后由于上边(2)中的配置,
    由null 变为 5 了,
    所以完成目标:获取新增的数据的自动编号的id  
    成功!

06.删除数据

(1)目标:根据id删除某一条数据 
   要实现此目标,需要执行的SQL语句大致是:delete from ams_admin where id=?
   所以在AdminMapper接口中添加抽象方法:
   /** 添加删除的抽象方法 */
   int deleteById(Long id);
—————————————————————————————————————————————————————————————————— 
(2)继续在【AdminMapper.xml】中配置以上抽象方法映射的SQL语句:
    <!--删除的sql语句-->
    <delete id="deleteById">
        delete from ams_admin where id=#{id}
    </delete> 
—————————————————————————————————————————————————————————————————— 
(3)在测试类中写一个【根据id删除ams_admin表格的行数】方法:
    @Test
    public void testDeleteById() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        //删除id编号为2的行数:
        Long id = 2L;
        int rows = adminMapper.deleteById(id);
        System.out.println("删除完成,受影响的行数 = " + rows);
//        if (rows == 1) {
//            System.out.println("删除成功");
//        } else {
//            System.out.println("删除失败,尝试删除的数据(id=" + id + ")不存在!");
//        }
        ac.close();
    }
    运行此类,发现可成功删除指定的id行数!!

06.修改数据

(1)目标:根据id修改某一条数据的密码 
   ● 要实现此目标,需要执行的SQL语句大致是:update ams_admin set password=? where id=?AdminMapper接口中添加抽象方法:
    /** 根据id来更新密码 */
    int updatePasswordById(Long id,String password);

    提示:在默认情况下,当Java程序源代码(.java文件)经过编译后,
         所有局部的量的名称都会丢失,
         为使得配置SQL语句时可根据指定的名称使用方法中的参数值,
         需要在方法的【各参数前添加@Param以指定名称】 
         – 【如果方法的参数只有1个,则可以不使用@Param指定名称】,
           因为Mybatis可以直接找到此参数的值
           
—————————————————————————————————————————————————————————————————— 
(2)AdminMapper.xml 中配置以上抽象方法映射的SQL语句:  
  
    <!--修改密码的sql语句-->
    <update id="updatePasswordById">
        update ams_admin set password=#{password} where id=#{id}
    </update>
    
   提示:以上占位符中的名称是通过@Param注解指定的名称,而不是抽象 方法的参数名称
—————————————————————————————————————————————————————————————————— 
(3)在测试类添加【根据id修改ams_admin表格中的username的密码】方法:
    /**
     * 根据id修改ams_admin表格中的username的密码
     */
    @Test
    public void testUpdatePasswordById() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        //注意
        Long id = 3L;
        String password = "12345678";
        int rows = adminMapper.updatePasswordById(id, password);
        System.out.println("修改密码完成,受影响的行数 = "+rows);
        ac.close();
    }

    运行后,发现控制台报错!
- - - - - - - - - - -  - - - -- - - - - - - - - - - - - - - - - - - -  
报错原因:参考上边(1)中的【提示】:
         在默认情况下,当Java程序源代码(.java文件)经过编译后,
         所有局部的量的名称都会丢失,
         为使得配置SQL语句时可根据指定的名称使用方法中的参数值,
         需要在方法的【各参数前添加@Param以指定名称】 
         – 【如果方法的参数只有1个,则可以不使用@Param指定名称】,
           因为Mybatis可以直接找到此参数的值



第一种解决方案:   
在【AdminMapper.xml】中将:<!--修改密码的sql语句-->中的大括号内的属性改为 param2 、parm1:
     具体代码:
    <!--修改密码的sql语句-->
    <update id="updatePasswordById">
        update ams_admin set password=#{param2} where id=#{param1}
    </update>
    此时运行,查看表格内对应id的密码,已经被修改成功!!



第二种解决方案: ————————>推荐推荐!!!          
如果方法的参数有多个,则需要使用@Param指定名称:
在AdminMapper中: 添加@Param注解!
    /** 改:根据id来更新密码 */
    int updatePasswordById(
            @Param("id") Long id, @Param("password") String password
    );
     
在【AdminMapper.xml】中将:<!--修改密码的sql语句-->中的大括号内的属性为 password 和 id:
     具体代码:
    <!--修改密码的sql语句-->
    <update id="updatePasswordById">
        update ams_admin set password=#{password} where id=#{id}
    </update>
    此时运行,查看表格内对应id的密码,已经被修改成功!!
——————————————————————————————————————————————————————————————————      

07.查询数据:

(1)之–统计

(1)目标:统计当前表中有多少条数据 
   ● 要实现此目标,需要执行的SQL语句大致是:select count(*) from ams_admin
——————————————————————————————————————————————————————————————————  
(2)/1.AdminMapper接口中添加抽象方法:
    /** 查: */
    int count();
    
   /2.AdminMapper.xml中配置以上抽象方法映射的SQL语句:      
    <!--查询所使用的必须是select节点-->
    <!--select节点必须配置resultType 或 resultMap中的其中1-->
    <!--resultType的值就是抽象方法的返回值类型的全限定名  
        此处也可以写成:resultType="java.lang.Integer"-->
    <select id="count" resultType="int">
        select count(*) from ams_admin
    </select>

注意:所有select节点必须配置resultType或resultMap这2个属性中的其中1个
—————————————————————————————————————————————————————————————————— 
(3)在测试类添加【查询ams_admin表格有多少条数据】:
    /**
     * 查询ams_admin表格有多少条数据
     */
    @Test
    public void testCount() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        int count = adminMapper.count();
        System.out.println("当前表中有" + count + "条记录");
        ac.close();
    }
    运行测试,控制台输出:当前表中有6条记录  对应数据库表格查询,发现确实有6条数据!

(2)之–查询某一条记录(表格中指定id的详细信息)

(1)目标:根据id查询管理员信息 
   ● 要实现此目标,需要执行的SQL语句大致是:select * from ams_admin where id=?
————————————————————————————————————————————————————————————————
(2)AdminMapper接口中添加抽象方法:
    /** 查询某一条数据 */
    Admin getById(Long id); 
————————————————————————————————————————————————————————————————
(3)AdminMapper.xml 中配置以上抽象方法映射的SQL语句: 

	<!--根据条件id查询ams_admin表格中对应的id信息-->
    <select id="getById" resultType="cn.tedu.mybatis.entity.Admin">
        select * from ams_admin where id=#{id}
    </select>
————————————————————————————————————————————————————————————————
(4)在测试类添加【根据id获得表格中对应id的信息】方法:
    /**
     * 查询某一条记录(表格中指定id的详细信息)
     */
    @Test
    public void testGetById() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        Long id = 2L;
        Admin admin = adminMapper.getById(id);
        System.out.println("查询结果:" + admin);
        ac.close();
    }
	运行测试类该方法,可以成功查询到指定方法中 id 的信息:
	
	控制台显示:
	查询结果:Admin{id=2, username='admin012', password='000000', 
				   nickname='null', avatar='null', phone='null', email='null', 
				   description='null', isEnable=null, lastLoginIp='null', 
				   loginCount=null, gmtLastLogin=null, gmtCreate=null,
				    gmtModified=null}

    通过测试可以发现:
    当存在匹配的数据时,将可以查询到数据,当不存在匹配的数据(指定查询的id)时,将返回null
————————————————————————————————————————————————————————————————
————————————————————————————————————————————————————————————————

(5)需要注意:如果【查询结果集中的: 列名column 与 类的属性名(isEnable) 不匹配时】,
            默认将放弃处理这些结果数据,则返回的对象中对应的属性值为null 
   ————>
   为了解决此问题,
   可以【在查询时使用自定义的别名】,使得名称保持一致,
   不过,更推荐配置————><resultMap>】以指导 Mybatis封装 查询结果
   :
    在【AdminMapper.xml】文件中:【配置 <resultMap> 以指导 Mybatis封装 查询结果】:
    
    <!--resultMap节点的作用:指导mybatis将查询到的结果集封装到对象中-->
    <!--id:自定义名称-->
    <!--type:封装查询结果的类型的全限定名-->
    <!--column:列名  property=属性名-->
    <resultMap id="BaseResultMap" type="cn.tedu.mybatis.entity.Admin">
        <result column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="nickname" property="nickname" />
        <result column="avatar" property="avatar" />
        <result column="phone" property="phone" />
        <result column="email" property="email" />
        <result column="description" property="description" />
        <result column="is_enable" property="isEnable" />
        <result column="last_login_ip" property="lastLoginIp" />
        <result column="login_count" property="loginCount" />
        <result column="gmt_last_login" property="gmtLastLogin" />
        <result column="gmt_create" property="gmtCreate" />
        <result column="gmt_modified" property="gmtModified" />
    </resultMap>
    
	    并改动此 <resultMap>:上边的:<select id="getById" resultMap="BaseResultMap">语句:
	    因为此处写了resultMap,要和上边的对应起来:
	    将【resultType="cn.tedu.mybatis.entity.Admin"】
	    改为:
	    <select id="getById" resultMap="BaseResultMap">
	    
         注: 
         resultMap的好处:复用,统一性 ; 可以解决名称不一致的问题!       

(3)之–查询列表

(1)● 目标:查询所有管理员的信息 
   ● 要实现此目标,需要执行的SQL语句大致是:select * from ams_admin order by id
   
————————————————————————————————————————————————————————————————
(2)AdminMapper接口中添加抽象方法:
   /** 查询列表 */ 
   List<Admin> list();
————————————————————————————————————————————————————————————————
(3)AdminMapper.xml 中配置以上抽象方法映射的SQL语句: 
   <!-- List<Admin> list(); --> 
   <select id="list" resultMap="BaseResultMap"> 
   	select * from ams_admin order by id 
   </select>
   
   注意:
   (1)查询时,结果集中可能超过1条数据时,必须显式的使用 ORDER BY子句对结果集进行排序;
   (2)查询时,结果集中可能超过1条数据时,应该考虑是否需要分页
————————————————————————————————————————————————————————————————
(4)在测试类添加【查询所有管理员的信息并返回在结果集中在控制台中显示】方法:
    /**
     * 查询所有管理员的信息并返回在结果集中在控制台中显示
     */
    public void testList() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        List<Admin> list = adminMapper.list();
        for (Admin admin : list) {
            System.out.println(admin);
        }
        ac.close();
    }
    
	运行测试类该方法,可以成功查询到ams_admin表格内的管理员信息:
	控制台显示:
	Admin{id=1, username='admin0021', password='12345678', nickname='null', 
	      avatar='null', phone='null', email='null', description='null', 
	      isEnable=null, lastLoginIp='null', loginCount=null, gmtLastLogin=null, 
	      gmtCreate=null, gmtModified=null}
	Admin{id=3, username='admin23', password='123', nickname='null', 
	      avatar='null', phone='null', email='null', description='null', isEnable=null, 
	      lastLoginIp='null', loginCount=null, gmtLastLogin=null, gmtCreate=null,
	      gmtModified=null}
	Admin{id=5, username='admin8888', password='12345678', nickname='null', 
		  avatar='null', phone='null', email='null', description='null', isEnable=null,
		  lastLoginIp='null', loginCount=null, gmtLastLogin=null, gmtCreate=null, 
		  gmtModified=null}

08.动态SQL

(0)Mybatis中的动态SQL表现为:根据参数不同,生成不同的SQL语句 

   例如,
   你可以对某些参数值进行判断,
   根据判断结果走向不同分支,来决定SQL语句的某个片段,
   如果参数值是可遍历的,你还可以遍历此参数来 生成部分SQL片段.

(1)动态SQL–foreach

(1) ● 目标:根据若干个id一次性删除若干条管理数据 

    ● 要实现此目标,需要执行的SQL语句大致是:delete from ams_admin where id in (?,?)
    
注意:以上SQL语句中,id值的数量(以上?的数量)对于开发人员而言是未知的,通常是软件的使用者决定的
————————————————————————————————————————————————————————————————
注意:
(2.2)中的属性collection的值 和 (2.1)中抽象方法删掉参数是相对应匹配的

(2.1)AdminMapper接口中添加抽象方法:
   int deleteByIds(Long... ids);    此处使用第一个
   或:int deleteByIds(Long[] ids);
   或:int deleteByIds(List<Long> ids);

(2.2)AdminMapper.xml中配置以上抽象方法映射的SQL语句:

    <!-- foreach节点是用于对参数值进行遍历的 -->
    <!-- collection属性:被遍历对象 -->
    <!-- 如果抽象方法的参数只有1个,当参数是数组时,collection="array",当参数是List时,collection="list" -->
    <!-- 如果抽象方法的参数只有多个,则collection="参数名",例如通过@Param注解配置的名称 -->
    <!-- item属性:自定义名称,是被遍历的对象的名称 -->
    <!-- separator属性:遍历过程中各值之间的分隔符号 -->
    <delete id="deleteByIds">
        delete from ams_admin where id in (
        <foreach collection="array" item="id" separator=",">
            #{id}
        </foreach>
        )
    </delete>   
————————————————————————————————————————————————————————————————
(3)● 以上代码中: 
     – <foreach>标签:用于遍历集合或数组类型的参数对象 
     
     – collection属性:被遍历的参数对象;
     				  当抽象方法的参数只有1个且没有添加 @Param注解时,
     				    如果参数是List类型则此属性值为list,
     				    如果参数是数组类型(包 括可变参数)则此属性值为array;
     				  当抽象方法的参数有多个或添加了@Param注解时,
     				    则此属性值为@Param注解中配置的值
     – item属性:自定义的名称,
     			表示遍历过程中每个元素的变量名,可在<foreach>子级使用#{变量名}表示数据 

	 – separator属性:分隔符号,会自动添加在遍历到的各元素之间
————————————————————————————————————————————————————————————————
(4)在测试类添加【根据若干个id一次性删除若干条管理数据】方法:
   编写并执行测试:
   
    /**
     * 批量删除:根据若干个id一次性删除若干条管理数据
     */
    @Test
    public void testDeleteByIds() {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        AdminMapper adminMapper = ac.getBean(AdminMapper.class);
        int rows = adminMapper.deleteByIds(3L,5L);
        System.out.println("受影响的行数 = " + rows);
        ac.close();
    }
	运行此类,发现控制台输出:【受影响的行数 = 2】
	此时查看ams_admin表格内的管理员信息,指定删除的id3和5已经被删除!
————————————————————————————————————————————————————————————————
(5)/1.Mybatis中动态SQL还有其它节点,例如: 
    – <if><choose> / <when> / <otherwise> 
    – 其它 
    
   /2.将在项目中必要时补充

09.关联查询

(1)首先,请准备一些数据表,及必要的测试数据,例如:
   存在若干条用户数 据,存在若干条角色数据,某个用户存在与角色的关联,
   最好有些用户有多个关联,又有些用户只有1个关联,还有些用户没有关联

(1)RBAC(基于角色的访问控制)

(1)RBAC = Role Based Access Control(基于角色的访问控制)
——————————————————————————————————————————————————————————————————
(2)RBAC是经典的用户权限管理的设计思路。
   在这样的设计中,会存在3种类型:用户、角色、权限,
   权限将分配到各种角色上,用户可以关联某种角色,进而实现用户与权限相关。
   使用这样的设计,更加利于统一管理若干个用户的权限。
——————————————————————————————————————————————————————————————————
(3)RBAC的设计思路中:用户与角色一般是多对多的关系,
   而在数据库中,仅仅只是使用“用户”和“角色”这2张表是不利于维护多对多关系的,
   通常会增加一张中间表,专门记录对应关系,
   同理,角色和权限也是多对多的关系,也需要使用中间表来记录对应关系!
——————————————————————————————————————————————————————————————————
(4)典型的RBAC设计:
(4.1)ams_admin:管理员表
(4.2)ams_role:角色表
(4.3)ams_admin_role:管理员与角色的关联表
(4.4)ams_permission:权限表
(4.5)ams_role_permission:角色与权限的关联表

(4.1)ams_admin:管理员表
     -- 管理员表:创建数据表
        因为此表我们之前创建过就不用再次创建了:
     drop table if exists ams_admin;
     create table ams_admin (
          id bigint unsigned auto_increment,
          username varchar(50) default null unique comment '用户名',
          password char(64) default null comment '密码(密文)',
          nickname varchar(50) default null comment '昵称',
          avatar varchar(255) default null comment '头像URL',
          phone varchar(50) default null unique comment '手机号码',
          email varchar(50) default null unique comment '电子邮箱',
          description varchar(255) default null comment '描述',
          is_enable tinyint unsigned default 0 comment '是否启用,1=启用,0=未启用',
          last_login_ip varchar(50) default null comment '最后登录IP地址(冗余)',
          login_count int unsigned default 0 comment '累计登录次数(冗余)',
          gmt_last_login datetime default null comment '最后登录时间(冗余)',
          gmt_create datetime default null comment '数据创建时间',
          gmt_modified datetime default null comment '数据最后修改时间',
          primary key (id)
      ) comment '管理员' charset utf8mb4;

(4.2)ams_role:角色表
     -- 角色表:创建数据表
     drop table if exists ams_role;
     create table ams_role
     (
         id           bigint unsigned auto_increment,
         name         varchar(50)      default null comment '名称',
         description  varchar(255)     default null comment '描述',
         sort         tinyint unsigned default 0 comment '自定义排序序号',
         gmt_create   datetime         default null comment '数据创建时间',
         gmt_modified datetime         default null comment '数据最后修改时间',
         primary key (id)
     ) comment '角色' charset utf8mb4;

(4.3)ams_admin_role:管理员与角色的关联表
     -- 管理员角色关联表:创建数据表
     drop table if exists ams_admin_role;
     create table ams_admin_role
     (
         id           bigint unsigned auto_increment,
         admin_id     bigint unsigned default null comment '管理员id',
         role_id      bigint unsigned default null comment '角色id',
         gmt_create   datetime        default null comment '数据创建时间',
         gmt_modified datetime        default null comment '数据最后修改时间',
         primary key (id)
     ) comment '管理员角色关联' charset utf8mb4;

(4.4)ams_permission:权限表
     -- 权限表:创建数据表
     drop table if exists ams_permission;
     create table ams_permission
     (
         id           bigint unsigned auto_increment,
         name         varchar(50)      default null comment '名称',
         value        varchar(255)     default null comment '值',
         description  varchar(255)     default null comment '描述',
         sort         tinyint unsigned default 0 comment '自定义排序序号',
         gmt_create   datetime         default null comment '数据创建时间',
         gmt_modified datetime         default null comment '数据最后修改时间',
         primary key (id)
     ) comment '权限' charset utf8mb4;

(4.5)ams_role_permission:角色与权限的关联表
     -- 角色权限关联表:创建数据表
     drop table if exists ams_role_permission;
     create table ams_role_permission
     (
         id            bigint unsigned auto_increment,
         role_id       bigint unsigned default null comment '角色id',
         permission_id bigint unsigned default null comment '权限id',
         gmt_create    datetime        default null comment '数据创建时间',
         gmt_modified  datetime        default null comment '数据最后修改时间',
         primary key (id)
     ) comment '角色权限关联' charset utf8mb4;

复制粘贴以上创建四个表的sql语句,
在idea右侧打开Database,
点击【QL】,
打开一个新的控制台,将上面的sql语句复制过来,并点击上边栏的绿色箭头执行创建成功!
此时再次点击idea的右侧打开Database,发现mall_ams下共有5张表!

总结:
utf8mb4:支持比较偏的符号,
①default null:声明该字段的默认值 
②comment '角色id':comment 后边跟的是注释,代表该字段要表达的意思
————————————————————————————————————————————————————————————————————
(5.1)两种删除sql语句的区别:
     insert into ams_admin (username, password) values ('admin001', '123456');
     insert into ams_admin (username, password) values ('admin002', '123456');
     insert into ams_admin (username, password) values ('admin003', '123456');
     insert into ams_admin (username, password) values ('admin004', '123456');
     insert into ams_admin (username, password) values ('admin005', '123456');

     delete from ams_admin where id>0;
     —————>删除ams_admin表中id>0的数据,下次在表中创建的数据的 id 会在上次删除的数值继续增加

     truncate ams_admin;
     —————>彻底清除ams_admin表格的数据,id会被重置:下次在表中创建的数据的 id 会从"1开始"

总结:
delete:在事务中,因为属于DML语句,所以可以进行回滚和提交操作(由操作者)
truncate和drop:属于DDL语句,在事务中,执行后会自动commit,所以不可以进行回滚。
          
(5.2)在idea中的页面:数据库中插入测试数据:
     /1.首先清空这5个表:
     truncate ams_admin;
     truncate ams_admin_role;
     truncate ams_role;
     truncate ams_permission;
     truncate ams_role_permission;

     /2.在ams_admin表中插入数据:
     insert into ams_admin (username, password) values ('admin001', '123456');
     insert into ams_admin (username, password) values ('admin002', '123456');
     insert into ams_admin (username, password) values ('admin003', '123456');
     insert into ams_admin (username, password) values ('admin004', '123456');
     insert into ams_admin (username, password) values ('admin005', '123456');

     /3.在ams_permission表中插入数据:
     insert into ams_permission (name, value, description)
     values ('商品-商品管理-读取', '/pms/product/read', '读取商品数据,含列表、详情、查询等'),
            ('商品-商品管理-编辑', '/pms/product/update', '修改商品数据'),
            ('商品-商品管理-删除', '/pms/product/delete', '删除商品数据'),
            ('后台管理-管理员-读取', '/ams/admin/read', '读取管理员数据,含列表、详情、查询等'),
            ('后台管理-管理员-编辑', '/ams/admin/update', '编辑管理员数据'),
            ('后台管理-管理员-删除', '/ams/admin/delete', '删除管理员数据');

     insert into ams_role (name) values ('超级管理员'),('系统管理员'),('商品管理员'),('订单管理员');

     insert into ams_admin_role (admin_id, role_id) values (1, 1),
            (1, 2),
            (1, 3),
            (1, 4),
            (2, 1),
            (2, 2),
            (2, 3),
            (3, 1),
            (3, 2),
            (4, 1);

————————————————————————————————————————————————————————————————————
(6)● 当有了新的数据表后,你应该在课后使用这些表继续练习常规数据管理,例如:
     – 向 权限表(ams_permission) 中插入新的数据
     – 根据id删除某个权限
     – 查询权限表中的所有权限

   ● 提示:
     – 需要新的数据类型,例如Permission类
     – 需要新的接口文件,用于定义以上2个数据访问功能的抽象方法
     – 需要新的XML文件,用于配置抽象方法对应的SQL语句
     – 需要修改配置信息,将此前指定的XML文件由AdminMapper.xml改为*.xml,
       并把SpringConfig类中sqlSessionFactoryBean()方法的
       第2个参数由Resource类型改为Resource...类型
     – 当需要测试时,使用新的测试类

(2)关联查询

(1)本次查询需要执行的SQL语句大致是:
   select * 
   from ams_admin 
   left join ams_admin_role on ams_admin.id=ams_admin_role.admin_id 
   left join ams_role on ams_admin_role.role_id=ams_role.id 
   where ams_admin.id=?
   
   在idea的database面板进行下面的两个查询:  
   /1. 查询 所有
       来自 ams_admin表中的
       将ams_admin表格的字段(id) = ams_admin_role表格的字段(admin_id)
   	   条件是:ams_admin的字段(id) = 1   
   select *
   from ams_admin
   left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
   where ams_admin.id = 1;
   
   /2.
   select *
   from ams_admin
   left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
   left join ams_role on ams_role.id = ams_admin_role.role_id
   where ams_admin.id = 1;
	
   经过上面这两个查询我们可以获得对应的信息。
————————————————————————————————————————————————————————————————————
(2)在阿里巴巴的《Java开发手册》中指出:
   – 【强制】:
      在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明
        — 若不知道有哪些字段,下列有一个操作的小技巧:
          先写:select * from ams_admin;
          再把 * 删掉,替换为id时,会自动补全所有的字段
          select id,
       			 username,
       			 password,
       			 nickname,
       			 avatar,
       			 phone,
       			 email,
       			 description,
       			 is_enable,
       			 last_login_ip,
       			 login_count,
       			 gmt_last_login,
       			 gmt_create,
       			 gmt_modified
		   from ams_admin;


所以在idea的database面板进行下面的查询:
把上面【(1)中的/2.】 中的 * 删掉,替换为:

select
    ams_admin.id, ams_admin.username, ams_admin.password, ams_admin.nickname,
    ams_admin.avatar, ams_admin.phone, ams_admin.email, ams_admin.description,
    ams_admin.is_enable, ams_admin.last_login_ip, ams_admin.login_count,
    ams_admin.gmt_last_login, ams_admin.gmt_create, ams_admin.gmt_modified,
    ams_role.id,
    ams_role.name
from ams_admin
left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
left join ams_role on ams_role.id = ams_admin_role.role_id
where ams_admin.id = 1;

我们可以查询到对应的信息。
————————————————————————————————————————————————————————————————————
(3)由于不建议使用星号表示字段列表,
   而关联查询时,由于涉及多张表,则字段列表可能较长,
   ————>可以使用<sql>节点封装字段列表,并在编写SQL语句时,使用<include>节点进行引用
   
(3.1)提示:在IntelliJ IDEA中,如果在<sql>节点中直接写字段列表,会提示错误,
          这是IntelliJ IDEA的误判,可以无视,并不影响运行,
          或者,————>使用其它方式避免误判,例如添加<if>恒等式(见下面的示例)
          
(3.2)注意:————>使用【<sql>节点】可以【封装SQL语句的任何部分】,而【封装字段列表】是最常见的做法

   在AdminMapper.xml中:
   将【<!--根据条件id查询ams_admin表格中对应的id信息-->】的【* 替换】:
   将【<!--查询所有管理员的信息-->】的【* 替换】:
   添加【sql】:
       <!--根据条件id查询ams_admin表格中对应的id信息-->
       <!--————————>resultType的值就是抽象方法的返回值类型的全限定名<———————— -->
       <!-- <select id="getById" resultType="cn.tedu.mybatis.entity.Admin"> -->
       <select id="getById" resultMap="BaseResultMap">
           select
           	<include refid="xxx"/>
           from ams_admin
           where id = #{id}
       </select>

       <!--查询所有管理员的信息-->
       <select id="list" resultMap="BaseResultMap">
           select
           	<include refid="xxx"/>
           from ams_admin order by id
       </select>
       
       <!--"if test="true"这句恒等式代码,只是为了在此处不报错的,没有任何意义-->
       <sql id="xxx">
           <if test="true">
               id, username, password
           </if>
       </sql>

       注意:上面的<include>里的refid的值 要和 <sql>中的id的值相同!
       
——————>总结: 要查哪些字段, 就在sql节点里写哪些节点
             sql节点起名字, include节点按照这个名字去引用
————————————————————————————————————————————————————————————————————
(4)1)如果使用<sql>封装了查询的字段列表,与<resultMap>的相性更好,
     所以,在开发实践中,通常结合一起使用
   2)另外,在开发实践中,许多不同条件的查询的字段列表是相同的,使用<sql>可以很好的实现代码片段的复用
————————————————————————————————————————————————————————————————————
(5)通过测试运行,可以发现(必须基于以上测试数据):
   – 当使用的id值为1时,共查询到4条记录,并且用户的基本信息是相同的,只是与角色关联的数据不同
   – 当使用的id值为2时,共查询到3条记录
   – 当使用的id值为3时,共查询到2条记录
   – 当使用其它有效用户的id时,共查询到1条记录
————————————————————————————————————————————————————————————————————
(6)此类查询期望的结果应该是:
   public class xxx {
    // 用户基本信息的若干个属性,例如用户名、密码等
    // 此用户的若干个角色数据,可以使用 List<?>
   }
————————————————————————————————————————————————————————————————————
(7)则先创建“角色”对应的数据类型:即放在上边(6)中的List————><?>里
   在entity包里创建:Role类  并实现implements 序列化Serializable 接口:
     /**
      * ppt:41页:关联查询:创建“角色”对应的数据类型:
      */
     public class Role implements Serializable {
         private Long id;
         private String name;
         private String description;
         private Integer sort;
         private LocalDateTime gmtCreate;
         private LocalDateTime gmtModified;
         // Setters & Getterss 、 toString() 、equals和hashCode
         ................
     }
————————————————————————————————————————————————————————————————————
(8)再 创建用于封装此次查询结果的类型:
   在src/main/java/cn.tedu/mybatis包下:创建vo包:创建 AdminDetailsVO 类
   重点声明:private List<Role> roles;  //在List集合里放若干个角色
   /**
    * 创建用于封装此次查询结果的类型:
    */
   public class AdminDetailsVO {
       private Long id;
       private String username;
       private String password;
       private String nickname;
       private String avatar;
       private String phone;
       private String email;
       private String description;
       private Integer isEnable;
       private String lastLoginIp;
       private Integer loginCount;
       private LocalDateTime gmtLastLogin;
       private LocalDateTime gmtCreate;
       private LocalDateTime gmtModified;
       //在List集合里放若干个角色:
       private List<Role> roles;
       //调用 Setters & Getterss  、 toString() 、equals和hashCode
————————————————————————————————————————————————————————————————————
(9)①在AdminMapper接口中添加抽象方法:

    /** 按 ID 获取详细信息 */
    AdminDetailsVO getDetailsById(Long id);
    
   ②在AdminMapper.xml里边准备好:
    <!-- AdminDetailsVO getDetailsById(Long id); -->
    <select id="getDetailsById" resultMap="DetailsResultMap">

    </select>
    <resultMap id="DetailsResultMap" type="cn.tedu.mybatis.vo.AdminDetailsVO">

    </resultMap?
————————————————————————————————————————————————————————————————————
(10)需要注意,
    由于此次关联了3张表一起查询,结果集中 必然出现某些列的名称是完全相同的,
    所以,在查询时,不可以使用星号表示字段列表(因为这样的结果集中的列名就是字段名,会出现相同的列名),
    而是应该至少为其中的一部分相同名称的列定义别名
————————————————————————————————————————————————————————————————————
(11)例如:
    在idea的Database面板中修改之前的关联查询指令,改为:
    
    select
    ams_admin.id, ams_admin.username, ams_admin.password, ams_admin.nickname,
    ams_admin.avatar, ams_admin.phone, ams_admin.email, ams_admin.description,
    ams_admin.is_enable, ams_admin.last_login_ip, ams_admin.login_count,
    ams_admin.gmt_last_login, ams_admin.gmt_create, ams_admin.gmt_modified,
       
    ams_role.id,
    ams_role.name,
    ams_role.description,
    ams_role.sort,
    ams_role.gmt_create,
    ams_role.gmt_modified
    from ams_admin
    left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
    left join ams_role on ams_role.id = ams_admin_role.role_id
    where ams_admin.id = 1;
    
    并将上面的语句复制到:配置sql语句的 AdminMapper.xml 中:
    并添加相对应的关联查询语句:
    
    <!-- AdminDetailsVO getDetailsById(Long id); -->
    <select id="getDetailsById" resultMap="DetailsResultMap">
        select
        <include refid="DetailsQueryFields"/>
        from ams_admin
        left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
        left join ams_role on ams_role.id = ams_admin_role.role_id
        where ams_admin.id=#{id}
    </select>
    <!--再写一个sql节点,id为查询的字段列表,将需要查询的字段放在sql节点-->
    <sql id="DetailsQueryFields">
        <if test="true">
            ams_admin.id,
            ams_admin.username,
            ams_admin.password,
            ams_admin.nickname,
            ams_admin.avatar,
            ams_admin.phone,
            ams_admin.email,
            ams_admin.description,
            ams_admin.is_enable,
            ams_admin.last_login_ip,
            ams_admin.login_count,
            ams_admin.gmt_last_login,
            ams_admin.gmt_create,
            ams_admin.gmt_modified,

            ams_role.id AS role_id,
            ams_role.name AS role_name,
            ams_role.description AS role_description,
            ams_role.sort AS role_sort,
            ams_role.gmt_create AS role_gmt_create,
            ams_role.gmt_modified AS role_gmt_create
        </if>
    </sql>

    <!-- resultMap节点的作用:指导mybatis将查询到的结果集封装到对象中 -->
    <!-- resultMap节点的id属性:自定义名称 -->
    <!-- resultMap节点的type属性:封装查询结果的类型的全限定名 -->
    <!-- 在关联查询中,即便结果集中的列名(column)与类的属性名(property)完全相同,也必须配置 -->
    <!-- column=结果集中的列名,property=属性名 -->
    <resultMap id="DetailsResultMap" type="cn.tedu.mybatis.vo.AdminDetailsVO">
        <!-- 主键应该使用id节点进行配置,非主键、非集合的使用result节点进行配置 -->
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="nickname" property="nickname" />
        <result column="avatar" property="avatar" />
        <result column="phone" property="phone" />
        <result column="email" property="email" />
        <result column="description" property="description" />
        <result column="is_enable" property="isEnable" />
        <result column="last_login_ip" property="lastLoginIp" />
        <result column="login_count" property="loginCount" />
        <result column="gmt_last_login" property="gmtLastLogin" />
        <result column="gmt_create" property="gmtCreate" />
        <result column="gmt_modified" property="gmtModified" />
        <!--
            (1)collection子节点:
                用于配置(1对多)关系的数据部分,
                通常在实体类(AdminDetailsVO)中是声明的List类型的属性roles
            (2)collection子节点的ofType:
                List集合中的元素的类型RoleList<Role>-->
        <collection property="roles" ofType="cn.tedu.mybatis.entity.Role">
            <!--当mybatis在处理结果的时候,若结果集里有一模一样的列名,
                只会取左边的那个,导致查询结果相同。
                ————>为了解决这个问题,我们在sql节点里给相同的取别名:
                     给ams_role表格取别名:直接(在后面加 AS 别名)即可!-->
            <id column="role_id" property="id" />
            <result column="role_name" property="name" />
            <result column="role_description" property="description" />
            <result column="role_sort" property="sort" />
            <result column="role_gmt_create" property="gmtCreate" />
            <result column="role_gmt_modified" property="gmtModified" />
        </collection>
    </resultMap>
   
————————————————————————————————————————————————————————————————————
(12)依然在idea的Dtabase面板里关联查询语句用条件为1 的进行测试
    在测试类中启动【testGetDetailsById】方法:
    并将id改为1:
    控制台输出:
    
    admin details >>>
	AdminDetailsVO{
		id=1, 
		username='admin001', 
		password='123456', 
		nickname='null', 
		avatar='null', phone='null', email='null', description='null', 
		isEnable=0, lastLoginIp='null', loginCount=0, 
		gmtLastLogin=null, gmtCreate=null, gmtModified=null, 
		roles=[
			Role{id=1, name='超级管理员', description='null', sort=0, gmtCreate=null, gmtModified=null}, 
			Role{id=2, name='系统管理员', description='null', sort=0, gmtCreate=null, gmtModified=null}, 
			Role{id=3, name='商品管理员', description='null', sort=0, gmtCreate=null, gmtModified=null}, 
			Role{id=4, name='订单管理员', description='null', sort=0, gmtCreate=null, gmtModified=null}
			]
	}
	可以看到查询的结果没有问题,
	我查询的管理员id=1,显示的就是1的数据,有对应的4个角色
	
	再分别将上边测试类中的方法的id改为4测试,发现也没有问题
————————————————————————————————————————————————————————————————————

(3)总结:重要!!!

整体流程:
(1)首先在idea的Database面板:
   先写关联查询语句,
   写完后运行看一下执行结果,要和自己的需求一致
(2)按照 mybatis框架 的既有方式去写代码:
   在mapper包里的类中 先写抽象方法,
   因为之前的Admin类不能放下我们需要的结果, 
   所以我们写一个VO类,在写VO类时,
   我们设计的角色:一个管理员可能是好几个角色;数量不确定,所以设计为List集合来表示若干个角色信息,
   在设计VO类之前,我们先设计角色类Role,包含哪些属性先列举出来,
   否则VO类的:声明List类型的roles:List<Role> 中的"Role"写什么元素呢
   
总结上面:
1. 先写Role角色类,
   再写VO类准备好数据,
   再从AdminMapper接口中声明出VO类的抽象方法
2.再配置AdminMapper.xml的sql语句:
  在节点select处,首先明确查询的sql语句,
  结果一定指向某个resultMap,因为用resultMap才能告诉他怎么将4个角色放在VO类里
  那重点来到了resultMap节点:
    一对多的查询,名称相同的也要写出来,不写就查不到,
    主键需要id来配,若用resulyt来配不会保证他的唯一性,
3.通过一系列的配置后,在VO类里声明private List<Role> roles;时:  
  设计为List集合来表示若干个角色信息  , 
  那来到AdminMapper.xml文件里的:
  resultMap节点的:collection节点,
    只有这个节点才能做到配集合属性的意思,
    用此节点,要先声明用的哪个属性:<collection property="roles"  此处用的是角色属性
    否则万一有两个list集合,就混乱了
  还得告诉这个集合里面是什么:  ofType="cn.tedu.mybatis.entity.Role"
     这样才能将结果的4个角色放到指定的Role结果集内 
4.若我们不用别名查询,那id为1对应的角色Role只有1个不是4个了,会出错  
  为什么呢?
  ————>当mybatis在处理结果的时候,若结果集里有一模一样的列名,
       只会取左边的那个,导致查询结果相同。
       ————>为了解决这个问题,我们在sql节点里给相同的取别名:
            给ams_role表格取别名:直接(在后面加 AS 别名)即可!-->  

10.小结

(1)Mybatis小结

(1)关于Spring MVC框架,你应该(1/4): 
   – 了解如何创建一个整合了Spring框架的Mybatis工程 
   – 了解整合了Spring框架的Mybatis工程的配置 
   – 掌握声明抽象方法的原则: 
      – 返回值类型:增删改类型的操作均返回int,表示“受影响的行数”,查询类型操作,
                  根据操作得到的结果集来决定,只要能够放入所有所需的数据即可,
                  通常,统计查询返回int,查询最多1个结果时返回自定义的某个数据类型,
                  查询多个结果时返回List集合类型 
      – 方法名称:自定义,不要使用重载,其它命名建议参考此前的笔记 
      – 参数列表:根据需要执行的SQL语句中的参数来设计,并且,当需要执行的是插入数据操作时,
      			 必须将这些参数进行封装,并在封装的类中添加主键属性,
      			 以便于Mybatis能获取自动编号的值回填到此主键属性中,
      			 当需要执行的是其它类型的操作时,
      			 如果参数数量较多,可以封装,
      			 如果只有1 个,则直接声明为方法参数,
      			 如果超过1个且数量不多,则每个参数之前添加@Param注解

(2)关于Spring MVC框架,你应该(2/4): 
   – 了解使用注解配置SQL语句 
   – 掌握使用XML配置SQL语句 
      – 这类XML文件需要顶部特殊的声明,所以,通常是从网上下载或通过复制粘贴得到此类文件 
      – 根节点必须是<mapper>,且必须配置namespace,取值为对应的Java接口的全限定名 
      – 应该根据要执行的SQL语句不同来选择<insert><delete><update><select>节点,
        这些节点都必须配置id属性,取值为对应的抽象方法的名称 
      – 其实,<delete>节点和<update>节点可以随意替换使用,但不推荐 
      – 在不考虑“获取自动生成的主键值”的情况下,<insert><delete><update>也可以混为一谈, 
        但不推荐 
      – 当插入数据时,当需要获取自动生成的主键值时,
        需要在<insert>节点上配置useGeneratedKeys 和keyProperty属性 
      – 在<select>节点上,必须配置resultMap或resultType属性中的其中1(3)关于Spring MVC框架,你应该(3/4): 
   – 掌握使用<sql>封装SQL语句片段,并使用<include>进行引用,以实现SQL语句的复用 
   – 掌握<resultMap>的配置方式 
     – 主键列与属性的映射必须使用<id>节点配置 
     – 在1对多、多对多的查询中,集合类型的属性的映射必须使用<collection>子节点配置 
     – 其它列与属性的映射使用<result>节点配置
     – 在单表查询中,列与属性名一致时,可以不必显式的配置,
       但是,在关联查询中,即使列与属性名称一致,也必须显式的配置出来

(4)关于Spring MVC框架,你应该(4/4): 
   – 理解resultType与resultMap使用原则 
      – 尽可能的全部使用resultMap,
        如果查询结果是单一某个数据类型(例如基本数据类型或 字符串或某个时间等),则使用resultType 
   – 掌握动态SQL中的`<foreach>`的使用 
   – 大概了解动态SQL中的其它标签 
      – 在后续项目中补充

11.课后阅读

(1)关于#{}和${}格式的占位符

● 在Mybatis中,配置SQL语句时,参数可以使用#{}或${}格式的占位符 
● 例如存在需求:分页查询表中的所有数据。 
● 需要执行的SQL语句大致是:  select * from ams_admin order by id limit ?, ?
● 则此功能的抽象方法应该是:
  List<Admin> listPage(@Param("offset") Integer offset, @Param("size") Integer size);
  
● 配置SQL语句:
  <select id="listPage" resultMap="BaseResultMap"> 
    select 
      <include refid="BaseQueryFields" /> 
    from ams_admin order by id limit #{offset}, #{size} 
  </select>
  
● 执行测试: 
  @Test 
  public void testListPage() {
   Integer offset = 0; 
   Integer size = 3; 
   List<Admin> adminList = adminMapper.listPage(offset, size); 
   System.out.println("查询到的记录数:" + adminList.size()); 
   for (Admin admin : adminList) { 
   System.out.println(admin); 
   } 
  }
  
● 以上代码可以正常通过测试,并且观察结果也都是符合预期的,
  即使把 SQL语句中的#{}换成${}格式,也是完全没有问题的!  
● 例如还存在需求:根据用户名查询此用户的详情 
● 在“根据用户名查询用户详情”时,如果将username=#{username}换成username=${username}会出现错误!
● 其实,使用#{}格式的占位符时,Mybatis在处理时会使用预编译的做法, 
  所以,在编写SQL语句时不必关心数据类型的问题(例如字符串值不需要添加单引号),也不存在SQL注入的风险!
  这种占位符只能用于表示某个值,而不能表示SQL语句片段! 
● 当使用${}格式的占位符时,Mybatis在处理时会先将参数值代入到SQL语句中,
  然后再执行编译相关过程,
  所以需要关心某些值的数据类型问题 (例如涉及字符串值时,需要在编写SQL语句时添加一对单引号框住
  字符串),并且,存在SQL注入的风险!其优点是可以表示SQL语句中的任何片段 
● 在一般情况下,应该尽可能的使用#{}格式的占位符,并不推荐使用${}格式的占位符,
  即使它可以实现“泛用”的效果! 
● 在一些特殊的情况下,如果一定要使用${}格式的占位符,必须考虑SQL注入的风险,
  应该使用正则表达式或其它做法避免出现SQL注入问题!

(2)Mybatis的缓存机制:默认一级缓存

● 缓存:通常是一个临时存储的数据,在未来的某个时间点可能会被删除 
● 通常,存储缓存数据的位置是读写效率较高的,相比其它“非缓存”的数 据有更高的处理效率 
● 由于缓存的数据通常并不是必须的,则需要额外消耗一定的存储空间,
  同时由于从缓存获取数据的效率更高,所以是一种牺牲空间、换取时间的做法 
● 另外,你必须知道,从数据库读取数据的效率是非常低下的
● Mybatis2种缓存机制,分别称之一级缓存和二级缓存 
● 一级缓存是基于SqlSession的缓存,也称之为“会话缓存”,
  仅当是同 一个会话、同一个Mapper、同一个抽象方法(同一个SQL语句)、同样的参数值时有效,
  一级缓存在集成框架的应用中默认是开启的,
  且整个过程不由人为控制(如果是自行得到SqlSession后的操作,可自行清理一级缓存)
● 二级缓存默认是全局开启的,它是基于namespace的,所以也称之为 “namespace缓存”,
  需要在配置SQL语句的XML中添加<cache />节点,
  以表示当前XML中的所有查询都允许开通二级缓存,
  并且,在<select>节点上配置useCache="true",则对应的<select>节点的查询结果将被二级缓存处理,
  并且,此查询返回的结果的类型必须是实现了Serializable 口的,
  如果使用了<resultMap>配置如何封装查询结果,则必须使用<id> 节点来封装主键的映射,
  满足以上条件后,二级缓存将可用,
  只要是当前namespace中查询出来的结果,都会根据所执行的SQL语句及参数进行结果的缓存
● 无论是一级缓存还是二级缓存,只要数据发生了写操作(增、删、改),缓存数据都将被自动清理
● 由于Mybatis的缓存清理机制过于死板,所以,一般在开发实践中并不怎么使用!
  更多的是使用其它的缓存工具并自行制定缓存策

— — — — — — — — — — — — —

4.1、Spring Test

01.基于Spring的测试

(1)Spring Test主要解决了以下问题: 
   – 在普通测试环境下,当需要使用Spring时,需要手动加载Spring配置,
     且手动从 Spring容器中获取对象,使用Spring Test后,只需要通过注解指定Spring配置类, 
     在Spring容器中的对象均可自动装配 
   – 通过@Sql等注解,对数据库编程的测试提供了更好的支持
————————————————————————————————————————————————————————————————
(2)使用Spring Test时需添加的依赖项为:spring-test
   在mybatis——>pom.xml里添加:
        <!--添加Spring Test的依赖项:spring-test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.14</version>
        </dependency>

   注意:你仍需另外添加测试的依赖项 
   注意:需要与其它的spring-???依赖项使用完全相同的版本
—————————————————————————————————————————————————————————————————————
(3)在编写测试时,
   在测试类上添加@SpringJUnitConfig注解,
   并在此注解中配置Spring的配置类作为参数,则执行此类的任何测试方法之前,都会加载这些Spring配置类,
   并且,在编写测试时,只要是在Spring容器中存在的对象,都可以自动装配
   
   在绿色Java包内的:cn/tedu/mybatis 包下创建测试类MybatisSpringTests
   执行以下方法:
/*
在【此注解】中配置Spring的配置类作为参数,则执行此类的任何测试方法之前,都会加载这些Spring配置类,
并且,在编写测试时,只要是在Spring容器中存在的对象,都可以自动装配
 */
@SpringJUnitConfig(SpringConfig.class)
public class MybatisSpringTests {
    @Test
    public void contextLoads(){ //contextLoads:上下文加载
        //soutm:
        System.out.println("MybatisSpringTests.contextLoads()");
    }
    
    @Autowired
    Environment environment;
    @Test
    public void testEnvironment(){ //testEnvironment:测试环境
        System.out.println(environment.getProperty("datasource.url"));
        System.out.println(environment.getProperty("datasource.driver"));
        System.out.println(environment.getProperty("datasource.username"));
        System.out.println(environment.getProperty("datasource.password"));
        System.out.println(environment.getProperty("mybatis.mapper-locations"));
    }

    @Autowired
    DataSource dataSource;//dataSource:数据源
    @Test
    public void testGetConnection() throws Exception { //testGetConnection:测试获取连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }

    /** 测试插入1条数据 */
    @Autowired(required = false)
    AdminMapper  adminMapper;//若此处爆红,但不影响编译
    @Test
    public void testInsert(){
        Admin admin = new Admin();
        admin.setUsername("test-user-001");
        admin.setPassword("000000");

        System.out.println("插入之前:"+admin);
        int rows = adminMapper.insert(admin);
        System.out.println("rows ="+rows);
        System.out.println("插入之后:"+admin);
        /*
        此方法在控制台输出:
        插入之前:Admin{id=null, username='test-user-001', password='000000', nickname='null',
                      ............................................................}
        插入之后:Admin{id=6, username='test-user-001', password='000000', nickname='null',
                     ............................................................}
         */
    }  

02.@Sql注解

(1)当在pom.xml文件中——>添加了spring-test依赖后, 可以在测试时使用@Sql注解,
   以加载某些.sql脚本,使得测试之前或之后将执行这些脚本! 
   
   /1.在测试包test下:
      创建一个和绿色java包同级的resources包,并创建名为:truncate.sql的脚本文件
      里面写清空表格数据的语句:【truncate ams_admin; #清空ams_admin表的数据】
   /2.在 测试类 MybatisSpringTests 中的testInsert方法上添加@Sql("classpath:truncate.sql")注解:
      运行此方法:发现控制台成功输出重复插入的数据了
      
      原因:
      使用此注解( (1)中的@Sql注解 )主要是为了保障可以反复测试,并且得到预期的结果!
—————————————————————————————————————————————————————————————
(2)使用此注解( (1)中的@Sql注解 )主要是为了保障可以反复测试,并且得到预期的结果!
   例如执行删除的测试时,
   假设数据是存在的,第1次删除可以成功,
   但是在这之后的测试将不会成功,因为数据在第1次测试时就已经被删除!
   则可以编写一个.sql脚本,通过脚本向数据表中插入数据,并在每次测试之前执行此脚本,
   即可保证每次测试都是成功的!


   /1.在测试包test下:resources包下,创建名为:insert_data.sql的脚本文件
      里面写插入表格数据的语句:
      【insert into ams_admin (username, password) values ('admin001', '123456');/2.在 测试类 MybatisSpringTests 中写一个:【测试按 ID 删除】方法
      并添加@Sql("classpath:truncate.sql")注解:
      
      /** 测试按 ID 删除 */
      @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
      @Test
      public void testDeleteById() {
          Long id = 1L;
          int rows = adminMapper.deleteById(id);
          System.out.println("rows = " + rows);
      }

      运行此方法:
      发现添加sql注解后,
      控制台输出的结果并不会受第一次删除后没有数据的影响,
—————————————————————————————————————————————————————————————————————
(3)此注解可以添加在测试类上,则对当前测试类的每个测试方法都是有效的
—————————————————————————————————————————————————————————————————— 
(4)此注解也可以添加在测试方法上,则只对当前测试方法是有效的
———————————————————————————————————————————————————————————————————
(5)如果测试类和测试方法上都添加了此注解,则仅测试方法上的注解会生效
———————————————————————————————————————————————————————————————————
(6)此注解除了配置需要执行的.sql脚本以外,
   还可以通过executionPhase 属性配置其执行阶段,
   例如取值为 Sql.ExecutionPhase.AFTER_TEST_METHOD 时将使得.sql脚本会在测试方法之后被执行 


   /1.在 insert_data.sql 的脚本文件里面写插入表格数据的语句:<——————————
# insert into ams_admin (username, password) values ('admin001', '123456');
insert into ams_admin (username, password)
values ('admin001', '123456')
     , ('admin002', '123456')
     , ('admin003', '123456')
     , ('admin004', '123456')
     , ('admin005', '123456');

insert into ams_permission (name, value, description)
values ('商品-商品管理-读取', '/pms/product/read', '读取商品数据,含列表、详情、查询等'),
       ('商品-商品管理-编辑', '/pms/product/update', '修改商品数据'),
       ('商品-商品管理-删除', '/pms/product/delete', '删除商品数据'),
       ('后台管理-管理员-读取', '/ams/admin/read', '读取管理员数据,含列表、详情、查询等'),
       ('后台管理-管理员-编辑', '/ams/admin/update', '编辑管理员数据'),
       ('后台管理-管理员-删除', '/ams/admin/delete', '删除管理员数据');

insert into ams_role (name)
values ('超级管理员'),
       ('系统管理员'),
       ('商品管理员'),
       ('订单管理员');

insert into ams_admin_role (admin_id, role_id)
values (1, 1),
       (1, 2),
       (1, 3),
       (1, 4),
       (2, 1),
       (2, 2),
       (2, 3),
       (3, 1),
       (3, 2),
       (4, 1);
       
   /2.在 truncate.sql 的脚本文件里面写清空表格数据的语句:<——————————
   
     #清空ams_admin表的数据:
     truncate ams_admin;
     truncate ams_role;
     truncate ams_admin_role;
     truncate ams_permission;
    truncate ams_role_permission;
    
   /2.在 测试类 MybatisSpringTests:【testInsert】方法
      上再添加一个sql注解:
      
      @Sql(value = "classpath:truncate.sql",
      	   executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)

	  运行此类,控制台出现:
	  
	  插入之前:Admin{id=null, username='test-user-001', password='000000', nickname='null', 
	  				avatar='null', phone='null', email='null', description='null', isEnable=null,
	  				lastLoginIp='null', loginCount=null, gmtLastLogin=null, gmtCreate=null, 
	  				gmtModified=null}
	  				
	  rows =1
	  
	  插入之后:Admin{id=2, username='test-user-001', password='000000', nickname='null',
	                avatar='null', phone='null', email='null', description='null', 
	                isEnable=null, lastLoginIp='null', loginCount=null, gmtLastLogin=null, 
	                gmtCreate=null, gmtModified=null}
—————————————————————————————————————————————————————————————
(7)每个测试方法可以添加多个@Sql注解

03.断言

(1)断言,
   如同中文的字面意思,可以理解为“一口咬定”,
   在执行断言时,它被认为是正确的。如果失败,JVM将抛出一个名为AssertionError的异常。
   它主要用于测试目的。
   
   在测试 MybatisSpringTests 中:
   (前提是ams_admin表格中只插入了id为1的数据时使用下面代码进行测试):
   :   
   在【testDeleteByIdSuccessfully】和【testDeleteByIdBecauseNotFound】方法中:
   表现为预判测试结果 :  
    /* 测试根据 ID 删除成功 */
    @Autowired(required = false)
    Admin admin;
    @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
    @Test
    public void testDeleteByIdSuccessfully() {
        /*
        Assertions:断言
        assertEquals():断言匹配
         */
        Long id = 1L;
        int rows = adminMapper.deleteById(id);
        System.out.println("rows = "+rows);
        Assertions.assertEquals(1,rows);//给有效id:1,则断言它的值为1
    }

    /* 测试根据 ID 删除失败 */
    @Test
    public void testDeleteByIdBecauseNotFound() {
        Long id = -1L;
        int rows = adminMapper.deleteById(id);
        System.out.println("rows = "+rows);
        Assertions.assertEquals(0,rows);//给无效id:-1,则断言它的值为0
    }
    我们可以根据断言来指定有效id或无效id时的值分别为01
    那么程序会根据我们自己写的断言意愿在控制台输出.
    若执行【testDeleteByIdSuccessfully】方法:   则在控制台输出 rows=1
    若执行【testDeleteByIdBecauseNotFound】方法:则在控制台输出 rows=0
————————————————————————————————————————————————————————————— 
(2)在执行测试时,应该使用**断言**对测试结果进行预判,
   而不是使用输出语句结合肉眼观察结果,
   这样才更符合自动化测试的标准
  (在自动化测试中,可以【一键执行项目中的所有测试方法】,
    并将测试结果汇总到专门的测试报告文件中) 
————————————————————————————————————————————————————————————— 
(3)提示:断言并不是Spring系列框架的功能,而是测试框架的功能
————————————————————————————————————————————————————————————— 
(4)通过调用Assertions类中的静态方法可以对测试结果进行断言,常用方法 有: 
   – assertEquals():断言匹配(相等) 
   – assertNotEquals():断言不匹配(不相等) 
   – assertTrue():断言为“真”  
   – assertFalse():断言为“假” 
   – assertNull():断言为nullassertNotNull():断言不为nullassertThrows():断言将抛出异常 
   – assertDoesNotThrow():断言不会抛出异常 
   – 其它


例1:
– assertTrue():断言为“真”  
– assertEquals():断言匹配(相等) 
:断言为“真” ,与下面的"断言值为1"相同,执行该方法,所以测试结果也会相同
    /*
     * 测试根据 ID 删除成功
     */
    @Autowired(required = false)
    Admin admin;
    @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
    @Test
    public void testDeleteByIdSuccessfully() {
        /*
        Assertions:断言
        assertEquals():断言匹配
         */
        Long id = 1L;
        int rows = adminMapper.deleteById(id);
        System.out.println("rows = "+rows);
        //断言为“真” ,与下面的"断言值为1"相同,所以测试结果也会相同
        Assertions.assertTrue(rows == 1);
        Assertions.assertEquals(1,rows);//给有效id:1,则断言它的值为1
    }2:
– assertNull():断言为nullassertNotNull():断言不为null/*
     * 测试获取 ID 成功
     *
     * 在ams_admin表格中有对应的id为1的数据时,执行下面的:当断言不为null 时
     * 控制台中显示该方法会成功
     *
     */
    @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
    @Test
    public void testGetByIdSuccessfully(){
        Long id = 1L;
        Admin admin = adminMapper.getById(id);
        
        //以下四种断言都是同一个意思:使用自己看着最合适的就行
        Assertions.assertNotNull(admin);//断言不为null   --此写法时最合适的
        //Assertions.assertNotEquals(admin,null);
        //Assertions.assertTrue(admin != null);
        //Assertions.assertFalse(admin == null);
    }


    /*
     * 测试根据 ID 失败,因为找不到
     *
     * 在ams_admin表格中没有对应的id为-1的数据时,执行下面的:当断言为null 时
     * 控制台中显示该方法会成功
     */
    @Test
    public void testGetByIdFailBecauseNotFound(){
        Long id = -1L;
        Admin admin = adminMapper.getById(id);
        Assertions.assertNull(admin);//断言为null
    }

————————————————————————————————————————————————————————————— 

例3:以【testInsert】方法为例,测试插入数据,
   – assertThrows():断言将抛出异常 
   – assertDoesNotThrow():断言不会抛出异常 
   
   若ams_admin表格被 truncate 重置过,执行此方法,第一次该方法可以执行变绿色成功;
   
   将sql注注释后再执行第一次可以成功,若再次执行该方法会变黄色,会报异常:
   SQLIntegrityConstraintViolationExceptionDuplicate entry 'test-user-001' for key 'username'
   SQL 完整性约束违反异常:“用户名”重复条目“test-user-001”
   :
   //@Sql(value ="classpath:truncate.sql")
   //@Sql(value = "classpath:truncate.sql",executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)//将使得.sql脚本会在测试方法之后被执行
    @Test
    public void testInsert(){
        Admin admin = new Admin();
        admin.setUsername("test-user-001");
        admin.setPassword("000000");

        //断言不会抛出异常 :assertDoesNotThrow
        //所以写成下面这样在执行时肯定不会抛出异常
        Assertions.assertDoesNotThrow(() -> {
//            System.out.println("插入之前:"+admin);
            int rows = adminMapper.insert(admin);//执行插入
//            System.out.println("rows ="+rows);
//            System.out.println("插入之后:"+admin);
            Assertions.assertEquals(1,rows);
            Assertions.assertEquals(1,admin.getId());
        });
    }
————————————————————————————————————————————————————————————— 

在【testInsert】方法下面,
再来写一个【插入失败,因为数据冲突: testInsertFailBecauseDuplicatekey】方法:
    /*
     * 插入失败,因为数据冲突
     */
    @Sql({"classpath:truncate.sql","classpath:insert_data.sql"})
    @Test
    public void testInsertFailBecauseDuplicatekey() {
        Admin admin = new Admin();
        admin.setUsername("admin001");
        admin.setPassword("000000");

        //断言它一定会抛出"SQLIntegrityConstraintViolationException"异常
        Assertions.assertThrows(DuplicateKeyException.class, () -> {
            adminMapper.insert(admin);
        });
    }
    执行方法前保证ams_admin时清空的,执行此方法会成功


断言就是实现分析好自己预判的结果再去写代码,
断言不应该出现冲的,
若是出现冲突,是自己没有理清程序的执行

注意:可以在上边导包出降"Assertion"写成 静态导入 ,即加一个:static 和 星号  
     : import static org.junit.jupiter.api.Assertions.*;
     	 这样的话在该类MybatisSpringTests 中下的断言直接写方法就行:
     	 
     	 例如不用Assertions 去打点调用该类方法了,直接写就行:
     	 由【Assertions.assertEquals(1,rows);】改为:
     	 【assertEquals(1,rows);】 

————————————————————————————————————————————————————————————— 

测试类  完整代码:
package cn.tedu.mybatis;
import cn.tedu.mybatis.entity.Admin;
import cn.tedu.mybatis.mapper.AdminMapper;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import javax.sql.DataSource;
import java.sql.Connection;

/*
在【此注解】中配置Spring的配置类作为参数,则执行此类的任何测试方法之前,都会加载这些Spring配置类,
并且,在编写测试时,只要是在Spring容器中存在的对象,都可以自动装配
 */
@SpringJUnitConfig(SpringConfig.class)
public class MybatisSpringTests {

    @Autowired
    Environment environment;
    @Autowired
    DataSource dataSource;
    @Autowired(required = false)
    AdminMapper adminMapper;

    @Test
    public void contextLoads() { //contextLoads:上下文加载
        //soutm:
        System.out.println("MybatisSpringTests.contextLoads()");
    }


    @Test
    public void testEnvironment() { //testEnvironment:测试环境
        System.out.println(environment.getProperty("datasource.url"));
        System.out.println(environment.getProperty("datasource.driver"));
        System.out.println(environment.getProperty("datasource.username"));
        System.out.println(environment.getProperty("datasource.password"));
        System.out.println(environment.getProperty("mybatis.mapper-locations"));
    }


    @Test
    public void testGetConnection() throws Exception { //testGetConnection:测试获取连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }


    /**
     * 测试插入1条数据
     */
    @Sql(value = "classpath:truncate.sql")
    @Sql(value = "classpath:truncate.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)//将使得.sql脚本会在测试方法之后被执行
    @Test
    public void testInsert() {
        Admin admin = new Admin();
        admin.setUsername("test-user-003");
        admin.setPassword("000000");

        //断言不会抛出异常 :assertDoesNotThrow
        //所以写成下面这样在执行时肯定不会抛出异常
        assertDoesNotThrow(() -> {
//            System.out.println("插入之前:"+admin);
            int rows = adminMapper.insert(admin);//执行插入
//            System.out.println("rows ="+rows);
//            System.out.println("插入之后:"+admin);
            assertEquals(1, rows);
            assertEquals(1, admin.getId());
        });
    }

    /*
     * 插入失败,因为数据冲突
     */
    @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
    @Test
    public void testInsertFailBecauseDuplicatekey() {
        Admin admin = new Admin();
        admin.setUsername("admin001");
        admin.setPassword("000000");

        //断言它一定会抛出"DuplicateKeyException"异常
        assertThrows(DuplicateKeyException.class, () -> {
            adminMapper.insert(admin);
        });
    }


    /**
     * 根据断言来指定有效id或无效id时的值分别为0和1 那么程序会根据我们自己写的断言意愿在控制台输出
     */
    /*
     * 测试根据 ID 删除成功
     *
     */
    @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
    @Test
    public void testDeleteByIdSuccessfully() {
        /*
        Assertions:断言
        assertEquals():断言匹配
         */
        Long id = 1L;
        int rows = adminMapper.deleteById(id);
        System.out.println("rows = " + rows);
        //断言为“真” ,与下面的"断言值为1"相同,所以测试结果也会相同
        assertTrue(rows == 1);
        assertEquals(1, rows);//给有效id:1,则断言它的值为1
    }

    /*
     * 测试根据 ID 删除失败
     */
    @Test
    public void testDeleteByIdBecauseNotFound() {
        Long id = -1L;
        int rows = adminMapper.deleteById(id);
        System.out.println("rows = " + rows);
        assertEquals(0, rows);//给无效id:-1,则断言它的值为0
    }


    /*
     * 测试获取 ID 成功
     *
     * 在ams_admin表格中有对应的id为1的数据时,执行下面的:当断言不为null 时
     * 控制台中显示该方法会成功
     *
     */
    @Sql({"classpath:truncate.sql", "classpath:insert_data.sql"})
    @Test
    public void testGetByIdSuccessfully() {
        Long id = 1L;
        Admin admin = adminMapper.getById(id);

        //以下四种断言都是同一个意思:使用自己看着最合适的就行
        assertNotNull(admin);//断言不为null   --此写法时最合适的
//        Assertions.assertNotEquals(admin,null);
//        Assertions.assertTrue(admin != null);
//        Assertions.assertFalse(admin == null);
    }


    /*
     * 测试根据 ID 失败,因为找不到
     *
     * 在ams_admin表格中没有对应的id为-1的数据时,执行下面的:当断言为null 时
     * 控制台中显示该方法会成功
     */
    @Test
    public void testGetByIdFailBecauseNotFound() {
        Long id = -1L;
        Admin admin = adminMapper.getById(id);
        assertNull(admin);//断言为null
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的超市进销存系统的实现参考: 1. 后端实现 1.1 数据库设计 在MySQL中创建一个名为market的数据库,包含以下5个表: 1. 商品表(goods):包含商品的编号(id)、名称(name)、价格(price)、库存(stock)、供应商(supplier)等字段。 2. 供应商表(supplier):包含供应商的编号(id)、名称(name)、联系人(contact)、电话(phone)等字段。 3. 订单表(order):包含订单的编号(id)、日期(date)、总金额(totalPrice)等字段。 4. 订单详情表(order_detail):包含订单详情的编号(id)、订单编号(orderId)、商品编号(goodsId)、商品数量(quantity)、单价(price)等字段。 5. 员工表(employee):包含员工的编号(id)、姓名(name)、密码(password)等字段。 1.2 后端代码实现 在IDEA中创建一个Maven项目,添加SpringSpringMVCMyBatis等依赖。创建以下Java类: 1. Goods实体类:对应goods表中的字段。 2. Supplier实体类:对应supplier表中的字段。 3. Order实体类:对应order表中的字段。 4. OrderDetail实体类:对应order_detail表中的字段。 5. Employee实体类:对应employee表中的字段。 6. GoodsMapper接口:定义对商品表进行增删改查的方法。 7. SupplierMapper接口:定义对供应商表进行增删改查的方法。 8. OrderMapper接口:定义对订单表进行增删改查的方法。 9. OrderDetailMapper接口:定义对订单详情表进行增删改查的方法。 10. EmployeeMapper接口:定义对员工表进行增删改查的方法。 11. GoodsServiceImpl类:实现对商品表进行增删改查的方法。 12. SupplierServiceImpl类:实现对供应商表进行增删改查的方法。 13. OrderServiceImpl类:实现对订单表进行增删改查的方法。 14. OrderDetailServiceImpl类:实现对订单详情表进行增删改查的方法。 15. EmployeeServiceImpl类:实现对员工表进行增删改查的方法。 16. GoodsController类:处理对商品表的请求。 17. SupplierController类:处理对供应商表的请求。 18. OrderController类:处理对订单表和订单详情表的请求。 19. EmployeeController类:处理对员工表的请求。 1.3 Spring配置文件 在resources目录下创建以下配置文件: 1. applicationContext.xml:配置Spring框架相关的bean。 2. spring-mvc.xml:配置SpringMVC相关的bean。 3. mybatis-config.xml:配置MyBatis相关的bean。 4. jdbc.properties:配置数据库连接信息。 1.4 启动项目 在IDEA中启动项目,测试后端接口是否正常工作。 2. 前端实现 2.1 环境搭建 安装Node.js和Vue CLI,并创建一个Vue项目。 2.2 前端页面 在Vue项目中创建以下页面: 1. 商品列表页面(goodsList.vue):显示所有商品的信息,包括编号、名称、价格、库存、供应商等字段。 2. 商品添加页面(goodsAdd.vue):添加新商品。 3. 商品修改页面(goodsEdit.vue):修改商品信息。 4. 供应商列表页面(supplierList.vue):显示所有供应商的信息,包括编号、名称、联系人、电话等字段。 5. 供应商添加页面(supplierAdd.vue):添加新供应商。 6. 供应商修改页面(supplierEdit.vue):修改供应商信息。 7. 订单列表页面(orderList.vue):显示所有订单的信息,包括编号、日期、总金额等字段。 8. 订单添加页面(orderAdd.vue):添加新订单。 9. 订单详情页面(orderDetail.vue):显示订单的详细信息,包括商品编号、名称、数量、单价等字段。 10. 员工登录页面(login.vue):员工登录系统。 2.3 前端交互 使用Axios库进行前后端交互,将接口返回的数据显示在页面上,并将用户输入的数据通过接口传递给后端。 3. 扩展技术 使用SpringBoot对项目进行简化配置和打包部署,使用Vue进行页面开发。可以使用Navicat等数据库连接工具对数据库进行管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值