在準備數據庫以前,我們根據原型圖抽像出`teacher`表如下:
| 字段 | 類型 | 主外鍵 | 備注 |
|---- |----| ----| ---- |
| id | bigint un | p(主鍵) | 自增 |
| name | varchar(255) | | 姓名 |
| username | varchar(255) | | 用戶名 |
| email | varchar(255) | | 郵箱 |
| sex | bit(1) | | 性別 |
| create_time | bigint un | | 創建時間 |
| update_time | bigint un | | 更新時間 |
# 數據庫
數據庫我們仍然采用免費的`mysql`,版本選擇常用的`5.7`。獲取該版本的方式有很多,比如我們可以通過官方來獲取,也可以通過下載一些集成的開發工具包比如`XAMPP`,`PHPStudy`或者使用`docker`來獲取它。
每個學習的階段都有學習的重點,本階段我們的重點并不是掌握如何來安裝`mysql`,所以如果你還不是`docker`用戶,那么推薦安裝[xampp](https://sourceforge.net/projects/xampp/files/)。
## docker環境
**注意:** 如果你還沒有使用過的`docker`及`docker-compose`,請直接略過本小節。本小節的作用同`xampp`一樣,也是在安裝`mysql`。
如果你是`docker` 用戶,可以使用以下的配置文件來達到與教程中的版本完全統一。
Dockerfile
```
FROM registry.cn-beijing.aliyuncs.com/mengyunzhi/mysql:5.7
```
docker-compose.yml
```
# 版本號
version: '3'
# 定義服務
services:
mysql57:
build:
context: ./
image: mysql:5.7
volumes:
- ./custom.cnf:/etc/mysql/conf.d/custom.cnf
- ./db:/var/lib/mysql
ports:
- "3307:3306"
environment:
- MYSQL_USER=root
- MYSQL_PASSWORD=
- MYSQL_ALLOW_EMPTY_PASSWORD=true
```
custom.cnf
```
[mysqld]
character-set-server = utf8
collation-server = utf8_unicode_ci
skip-character-set-client-handshake
```
相對位置如下:
```
panjiedeMac-Pro:mysql5.7 panjie$ tree -L 1
.
├── Dockerfile
├── custom.cnf
├── db
└── docker-compose.yml
1 directory, 3 files
```
>[info] `mysql`的版本號最低為5.7,請勿嘗試5.6及以下的數據庫。
# 數據庫管理軟件
數據庫管理,我們仍然使用`navicat-for-mysql`這款國人開發的優秀數據庫管理軟件。產品地址:[https://www.navicat.com.cn/products/navicat-for-mysql](https://www.navicat.com.cn/products/navicat-for-mysql),點擊右上角的`試用`按鈕來到下載界面,其為普通用戶提供了14天的試用期。如果你并不認同它的收費模式,還有很多個免費的數據庫管理軟件,比如:[workbench](http://dev.mysql.com/downloads/workbench/)、[Sequel Pro](http://www.sequelpro.com/)、[HeidiSQL](http://www.heidisql.com/)、[phpmyadmin](http://www.phpmyadmin.net/home_page/)、[dbForge Studio Express](http://www.devart.com/dbforge/mysql/studio/)、[DBTools Manager](http://www.dbtools.com.br/EN/dbmanagerpro/)或者[MyDB Studio](http://www.mydb-studio.com/)。
# 數據庫初始化
> 傳送門:[啟動xampp中的mysql](http://www.hmoore.net/yunzhiclub/thinkphp5guide/163808)、[使用navicat連接數據數](http://www.hmoore.net/yunzhiclub/thinkphp5guide/165009)
新建數據庫名:`yunzhi_spring_boot`,字符設置為`utf8mb4`,默認排序方式選擇`utf8mb4_general_ci`。

然后執行執行以下`sql`語句來達到快速建表的目的。
```sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT '' COMMENT '姓名',
`sex` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0男,1女',
`username` varchar(255) NOT NULL COMMENT '用戶名',
`email` varchar(255) DEFAULT '' COMMENT '郵箱',
`create_time` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '創建時間',
`update_time` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
PRIMARY KEY (`id`),
UNIQUE KEY `nx1HkMqiUveGnJz5lHE7mEcFI5WVew3iXbv3HCwF` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of teacher
-- ----------------------------
BEGIN;
INSERT INTO `teacher` VALUES (1, '張三', 1, 'zhangsan', 'zhangsan@mail.com', 1569721598000, 1569721598000);
INSERT INTO `teacher` VALUES (2, '李四', 0, 'lisi', 'lisi@yunzhi.club', 1569721598000, 1569721598000);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
```
成功執行后,打開數據表管理,刷新后能看到`teacher`表說明執行成功。

# 本節小測
無
## 上節答案
規律一:方法????同步,必然順序執行。而??????去除異步??后,為:????,故??????錯誤。
規律二:異步方法由同步方法觸發,所以?必然晚于?執行,?必然晚于?執行,故??????錯誤。
# 延伸閱讀
以下內容需要一定的數據庫知識,為選學內容。對該內容的學習有助于更好的掌握一些基礎的知識點,它們可能是面試中的考點,也可能是你心中的疑點。慢慢的越來越多的弄懂這些,你會有一種一通百通的暢快感。如果沒弄懂,那則是本教程書寫水平的問題。是否掌握該問題不會影響你對教程后續章節的學習。
## 解讀sql語句
```sql
SET NAMES utf8mb4; ?
SET FOREIGN_KEY_CHECKS = 0; ?
-- ---------------------------- ?
-- Table structure for teacher ?
-- ---------------------------- ?
DROP TABLE IF EXISTS `teacher`; ?
CREATE TABLE `teacher` ( ?
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT, ?
`name` varchar(255) DEFAULT '' COMMENT '姓名', ?
`sex` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0男,1女',
`username` varchar(255) NOT NULL COMMENT '用戶名',
`email` varchar(255) DEFAULT '' COMMENT '郵箱',
`create_time` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '創建時間',
`update_time` bigint(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
PRIMARY KEY (`id`), ?
UNIQUE KEY `nx1HkMqiUveGnJz5lHE7mEcFI5WVew3iXbv3HCwF` (`username`) USING BTREE ?
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; ?
-- ---------------------------- ?
-- Records of teacher ?
-- ---------------------------- ?
BEGIN; ??
INSERT INTO `teacher` VALUES (1, '張三', 0, 'zhangsan', 'zhangsan@mail.com', 1569721598000, 1569721598000); ??
INSERT INTO `teacher` VALUES (2, '李四', 0, 'lisi', 'lisi@yunzhi.club', 1569721598000, 1569721598000);
COMMIT; ??
SET FOREIGN_KEY_CHECKS = 1; ??
```
? 設置編碼為`utf8mb4`
? 關閉外鍵檢查
? 注釋內容
? 如果表存在,則將老表刪除
? 創建數據表
? 創建int類型字段,顯示長度為11,無符號數,不允許為null,自動增加字段
? 創建varchar可變字長字符串類型字段,最大長度255
? 聲明主鍵
? 在username字段上設置UNIQUE索引
? 設置引擎為InnoDB,自增值為3,默認字符編碼為`utf8mb4`
?? 開啟事務
?? 插入數據
?? 提交事務
?? 開啟外鍵檢查
## 字符編碼
做個小實驗,執行以下語句建立一張TEST表。
```sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for test
-- ----------------------------
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
`name_ascii` varchar(255) CHARACTER SET ascii DEFAULT NULL,
`name_gb2312` varchar(255) CHARACTER SET gb2312 DEFAULT NULL,
`name_utf8` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`name_utf8mb4` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SET FOREIGN_KEY_CHECKS = 1;
```
執行上述代碼得到一張數據表,該表中存在4個字段,除了字符編碼不同外,其它的屬性均相同。
>[warning] 使用navicate打開此表時,會得到一個沒有主鍵的警告,該警告并不影響本次測試,請忽略。
接下來,我依次嘗試向以上4字字段中輸入以下4個字符:
`a`、`夢`、`?`、`??`
? 輸入`a`時,四個字段全部可以正常保存。
? 輸入`夢`時,后三個字段是可以正常保存的,第一個字段`name_ascii`發生以下錯誤提示。

```
1366 - Incorrect string value: '\xE6\xA2\xA6' for column 'name_ascii' at row 1
錯誤代碼1366 - 在第1行`name_ascii`的字段上發現了不正確的字符串數值:'\xE6\xA2\xA6'
```
? 輸入`?`時,后兩個字段可以正常保存。

```
1366 - Incorrect string value: '\xE2\x98\x82' for column 'name_gb2312' at row 1
錯誤代碼1366 - 在第1行`name_gb2312`的字段上發現了不正確的字符串數值:'\xE2\x98\x82'
```
? 輸入`??`時,只有最后一個字段可以正常保存。

```
1366 - Incorrect string value: '\xF0\xA0\x9C\x8E' for column 'name_utf8' at row 4
錯誤代碼1366 - 在第1行`name_utf8`的字段上發現了不正確的字符串數值:'\xF0\xA0\x9C\x8E'
```
相同的文字,存入不同編碼類型中的字段中,有的就會報錯,有的就不會。這是為什么呢?
*****
下面我們共同簡單地補一下基礎知識。
筆者認為:**編碼(Character encoding)** 本質上是:將一種數據形式轉換成另外一種數據形式的方法。計算機本質上存的數據非`0`即`1`,也就是說我們是沒有辦法將除了01之外的東西直接存在計算機上的。這時候如果想把文字存在計算機中,就要將文字轉換為01格式的二進制數。由于二進制數據在進行交流時不太符合人類固化的交互方式,所以在進行二進制交流的時候,為了更加友好服務于非程序員的廣大用戶群體,我們在交流二進制時往往會做兩項處理:
1. 在適當的位置進行劃分。
2. 將其轉換為10進制或是16進制。
比如我們說某個IP地址是`192.168.0.1`,其實是在說某個IP地址是`11000000101010000000000000000001`。
此IP地址,經過兩次處理的過程如下:
1. 在適當的位置進行劃分 - `11000000 10101000 00000000 00000001`
2. 將其轉換為10進制或是16進制。
```
11000000 10101000 00000000 00000001 =
2? +2? 2? + 2? 0 2? =
128+64 128+32 0 1 =
192 168 0 1
```
這雖然對非程序員友好了,但對我們并不太友好。由于2進制與16進制天然的關系,程序員進行交流的時候,更喜歡用16進制來表示二進制。所以上述二制制如果用16進制來表示的話,轉換方法如下:
```
1100 0000 1010 1000 0000 0000 0000 0001 =
8400 0000 8020 8000 0000 0000 0000 0001 =
12 0 10 8 0 0 0 1 =
c 0 a 8 0 0 0 1 =
c0 a8 00 01
```
在轉換過程中的第二行`8400`并不該讀成`8千4百`,而應該讀成`8加4加0加0`,這也是`8421碼`的由來,當對應的二制進位為`1`時,我們在下方對應寫入`8421碼`的權重。反之亦然,由于`a8`\= `10 8` = `8+2+0+0 8+0+0+0` = `1100 1000`,所以可能很輕松的轉換成二進制數。
>[success] 我國早在宋代的時候,就已經使用了16兩為一斤計數方法。成語半斤八兩往往指不相上下。這是因為在當時一斤是16兩,那么半斤當然就是8兩了。
*****
有了以上知識的鋪墊,再理解編碼就輕松一些了。字符編碼是一種將字符映射為二進制的方法,只所以要映射為二進制,是由于計算機只能識別二進制。ascii編碼是針對英文編碼的方法,此編碼每個英文字符都對應一個不大于255(1111 1111)的二進制數。所以能夠表示的字符最多有256(0-255)個,比如我們熟悉的`a`,它對應的ascii編碼為`97(0110 0001)`。也就是說:當我們使用該編碼像計算機存入`a`,最終計算機保存的數據是`0110 0001`。在比如`b`,它對應的ascii編碼為`98(0110 0010)`。在該編碼下,每個字符都占用8個二進制位。8個二進制
位等于1個字節。所以我們又常說ascii中,每個字符占用的空間是1個字節。
ascii編碼占用了1個字節,可以表示256個不同的字符,這在英文系統下,已經足夠用了。但我們中國漢字博大精深,256個字符根本就容不下。這時候就發明了一種可以容的下中文字符的編碼`gb2312`,它占用了2個字節的空間,可以最多容納2的16次方即65536個文字。
但隨著全國化進程的推進,人們很快的發現了新問題:那就是各個國家的文字都有不同的編碼,大家雖然都能夠在自己的編碼下HAPPY的不行,但發現如果去對方的天空下,就完全啞了。這就好像我只會說漢語,有一天把我扔到以英文為交流語言的國家一樣。這時候`utf8`編碼就產生了。`utf8`就是世界通用語言,這下大家都用這種語言交流,但沒有了障礙。這時候2個字節就不夠它用了,所以`utf8`用了3個字節,即24個二制位,最多容納2的24次方即16777216個文字。一下子解決全世界所有語言的問題。然后大家把這個字典進行印刷,發布給了全球所有的計算機,世界語的問題終于得到解決了。
但實際應用總和理論開些玩笑,`utf8`用了一段時間后,我們突然發現:咦?我們博大精深的中文文字竟然還有沒有被世界語涵蓋的(我猜是忘 了,畢竟中國字那么多,新華字典也是人編的,落下幾個太正常不過了),而且不止一兩個再比如:?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??。上面這些都沒有。讀到這,相信你已經猜到結果了,那就是出現了`utf8mb4`。該編碼每個字符占用4個字節,最多容易2的32次方個字字符。
現在我們回顧下前面的錯誤:
```
1366 - Incorrect string value: '\xE6\xA2\xA6' for column 'name_ascii' at row 1
錯誤代碼1366 - 在第1行`name_ascii`的字段上發現了不正確的字符串數值:'\xE6\xA2\xA6'
```
'\\xE6\\xA2\\xA6'代表 16進制下的E6 A2 A6,其本質上是個3個字節,長度為24的二進制數。而`name_ascii`的字符集是`ascii`,長度為8,此時想往里存入24位的數據,當然就會發生錯誤了。
`gb2312`的長度為16,那么為什么在存`夢`的時候,沒報錯呢?剛剛我們不是說`夢`的長度是24嗎?這個是因為這樣:我們從網上復制數據的時候,由于網頁的編碼格式是utf8。所以我們復制下去的`夢`本身就是個`utf-8`格式的,在剪切板中,存的它的值是`0xE6 0xA2 0xA6` [傳送門](https://www.fileformat.info/info/unicode/char/68a6/index.htm)。數據在進行保存的時候,會嘗試將復制過來的utf-8格式的字符先轉成gb2312,然后再進行保存。夢字是可以轉換成功的,所以實際上最后存的不是`0xE6 0xA2 0xA6`,而`0x34 0x56`[傳送門](https://bianma.supfree.net/chaye.asp?id=68A6)。
但`?`、`??`,并沒有存在于gb2312編碼中,所以轉換失敗后嘗試直接存剪切板中的二制數據,此數值長度分別是24位及32位,當然就報錯了。教程中在存字符串時我們使用了更為寬泛的`utf8mb4`編碼,以保證在存入特殊字符時,不發生錯誤。
## bigint(11)
todo: int(4),int(11),bigint(11),括號中的數字是什么意思呢,好像從來沒有管過也沒有受過影響 。
# 你可能遇到的問題
* [MySQL 5.6 建立數據表時出現1071錯誤,原因分析及解決辦法](https://segmentfault.com/a/1190000020954608)
- 序言
- 第一章:Hello World
- 第一節:Angular準備工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二節:Hello Angular
- 第三節:Spring Boot準備工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四節:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven國內源配置
- 4 package與import
- 第五節:Hello Spring Boot + Angular
- 1 依賴注入【前】
- 2 HttpClient獲取數據【前】
- 3 數據綁定【前】
- 4 回調函數【選學】
- 第二章 教師管理
- 第一節 數據庫初始化
- 第二節 CRUD之R查數據
- 1 原型初始化【前】
- 2 連接數據庫【后】
- 3 使用JDBC讀取數據【后】
- 4 前后臺對接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三節 CRUD之C增數據
- 1 新建組件并映射路由【前】
- 2 模板驅動表單【前】
- 3 httpClient post請求【前】
- 4 保存數據【后】
- 5 組件間調用【前】
- 第四節 CRUD之U改數據
- 1 路由參數【前】
- 2 請求映射【后】
- 3 前后臺對接【前】
- 4 更新數據【前】
- 5 更新某個教師【后】
- 6 路由器鏈接【前】
- 7 觀察者模式【前】
- 第五節 CRUD之D刪數據
- 1 綁定到用戶輸入事件【前】
- 2 刪除某個教師【后】
- 第六節 代碼重構
- 1 文件夾化【前】
- 2 優化交互體驗【前】
- 3 相對與絕對地址【前】
- 第三章 班級管理
- 第一節 JPA初始化數據表
- 第二節 班級列表
- 1 新建模塊【前】
- 2 初識單元測試【前】
- 3 初始化原型【前】
- 4 面向對象【前】
- 5 測試HTTP請求【前】
- 6 測試INPUT【前】
- 7 測試BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后臺對接【前】
- 第三節 新增班級
- 1 初始化【前】
- 2 響應式表單【前】
- 3 測試POST請求【前】
- 4 JPA插入數據【后】
- 5 單元測試【后】
- 6 惰性加載【前】
- 7 對接【前】
- 第四節 編輯班級
- 1 FormGroup【前】
- 2 x、[x]、{{x}}與(x)【前】
- 3 模擬路由服務【前】
- 4 測試間諜spy【前】
- 5 使用JPA更新數據【后】
- 6 分層開發【后】
- 7 前后臺對接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五節 選擇教師組件
- 1 初始化【前】
- 2 動態數據綁定【前】
- 3 初識泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再識單元測試【前】
- 7 其它問題
- 第六節 刪除班級
- 1 TDD【前】
- 2 TDD【后】
- 3 前后臺對接
- 第四章 學生管理
- 第一節 引入Bootstrap【前】
- 第二節 NAV導航組件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三節 footer組件【前】
- 第四節 歡迎界面【前】
- 第五節 新增學生
- 1 初始化【前】
- 2 選擇班級組件【前】
- 3 復用選擇組件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校驗【后】
- 7 唯一性校驗【后】
- 8 @PrePersist【后】
- 9 CM層開發【后】
- 10 集成測試
- 第六節 學生列表
- 1 分頁【后】
- 2 HashMap與LinkedHashMap
- 3 初識綜合查詢【后】
- 4 綜合查詢進階【后】
- 5 小試綜合查詢【后】
- 6 初始化【前】
- 7 M層【前】
- 8 單元測試與分頁【前】
- 9 單選與多選【前】
- 10 集成測試
- 第七節 編輯學生
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 功能開發【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成測試
- 7 @Input 異步傳值【前】
- 8 值傳遞與引入傳遞
- 9 @PreUpdate【后】
- 10 表單驗證【前】
- 第八節 刪除學生
- 1 CSS選擇器【前】
- 2 confirm【前】
- 3 功能開發與測試【后】
- 4 集成測試
- 5 定制提示框【前】
- 6 引入圖標庫【前】
- 第九節 集成測試
- 第五章 登錄與注銷
- 第一節:普通登錄
- 1 原型【前】
- 2 功能設計【前】
- 3 功能設計【后】
- 4 應用登錄組件【前】
- 5 注銷【前】
- 6 保留登錄狀態【前】
- 第二節:你是誰
- 1 過濾器【后】
- 2 令牌機制【后】
- 3 裝飾器模式【后】
- 4 攔截器【前】
- 5 RxJS操作符【前】
- 6 用戶登錄與注銷【后】
- 7 個人中心【前】
- 8 攔截器【后】
- 9 集成測試
- 10 單例模式
- 第六章 課程管理
- 第一節 新增課程
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 async管道【前】
- 4 優雅的測試【前】
- 5 功能開發【前】
- 6 實體監聽器【后】
- 7 @ManyToMany【后】
- 8 集成測試【前】
- 9 異步驗證器【前】
- 10 詳解CORS【前】
- 第二節 課程列表
- 第三節 果斷
- 1 初始化【前】
- 2 分頁組件【前】
- 2 分頁組件【前】
- 3 綜合查詢【前】
- 4 綜合查詢【后】
- 4 綜合查詢【后】
- 第節 班級列表
- 第節 教師列表
- 第節 編輯課程
- TODO返回機制【前】
- 4 彈出框組件【前】
- 5 多路由出口【前】
- 第節 刪除課程
- 第七章 權限管理
- 第一節 AOP
- 總結
- 開發規范
- 備用