用Golang寫爬蟲(八) - 使用GORM存入MySQL數據庫

上篇文章把數據存進了文件,這篇文章將把數據存入MySQL數據庫。數據庫的使用是每一個開發者必備的技能,因此本篇文章咱們使用Golang操做數據庫。Golang沒有內置的驅動支持任何的數據庫,可是Go定義了database/sql接口,開發者能夠基於驅動接口開發相應數據庫的驅動。固然如今各類數據庫驅動生態已經很穩定了,能夠直接使用。mysql

我在實際開發工做中通常不直接用數據庫驅動(如github.com/go-sql-driver/mysql),而是用ORM。ORM有三大優勢:git

  1. 安全性。使用底層的數據庫驅動是在「拼」SQL,這樣很容易出現SQL注入等風險。
  2. 易於理解。拼 SQL 這種形式放在代碼中是很亂的,只有開發者在當時編寫時能夠理解,可是後來的維護者就不那麼好理解了。
  3. 易於維護。若是數據庫操做代碼寫多了每每會有不少重複、可重用的SQL實際上是能夠避免的,若是用ORM,他把CURD這些操做都費裝起來,這種代碼看起來會很是直觀,很容易找到一些錯誤的,很差的,重複的用法。
  4. 可移植。ORM每每支持多種數據庫,如MySQL、PostgreSQL、SQLite等,這樣在本地開發你可選擇SQLite,而在生產環境能夠切換MySQL,徹底不須要改動代碼邏輯,只是改一下配置。

因此使用ORM能提升開發效率,下降開發成本,可是缺點是ORM的封裝是有成本的,會稍微影響到性能,不過我認爲這點性能損耗微乎其微。github

目前Golang世界裏面最受歡迎的ORM叫作gorm,咱們就使用它,先安裝它:golang

❯ go get -u github.com/jinzhu/gorm
❯ go get -u github.com/go-sql-driver/mysql  # 也要安裝對應數據庫驅動
複製代碼

gorm用的MySQL驅動其實就是go-sql-driver/mysql,但一般這麼import:sql

import (
  "github.com/jinzhu/gorm"
  _ "github.com/jinzhu/gorm/dialects/mysql"
)
複製代碼

有時候對於某個包,不須要把整個包都導入進來,只是執行他的init函數。可使用import _這樣的寫法,不過要注意,這種方式沒法經過包名來調用包中的其餘函數。數據庫

定義模型

接着咱們聲明Model:安全

type Movie struct {
    ID int
    Title string `gorm:"type:varchar(100);unique_index"`
}
複製代碼

這個結構體叫作Movie,模型有2個字段ID和Title。Title後面有個反引號,這個部分叫作成員變量標籤(Tag),當須要用到Tag中的內容時可使用反射包(reflect)中的方法來獲取,這是gorm支持的用法。它表示定義title資格字段的長度是100,而且有惟一索引。其餘支持的標籤能夠看官方文檔。bash

若是看文檔或者不少文章,常常能看到繼承了gorm.Model的模型,以下面這個:函數

type Movie struct {
    gorm.Model
    Title string `gorm:"type:varchar(100);unique_index"`
}
複製代碼

gorm.Model 自帶了ID, CreatedAt, UpdatedAt, DeletedAt這麼4個字段,在咱們這裏徹底沒有必要。性能

初始化

在main函數裏面鏈接數據庫,並建立表:

func main() {
    start := time.Now()
    ch := make(chan bool)
    db, err := gorm.Open("mysql", "root:@/test?charset=utf8")
    defer db.Close()
    checkError(err)

    db.DropTableIfExists(&Movie{})
    db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&Movie{})

    for i := 0; i < 11; i++ {
        go parseUrls("https://movie.douban.com/top250?start="+strconv.Itoa(25*i), ch, db)
    }

    for i := 0; i < 11; i++ {
        <-ch
    }

    elapsed := time.Since(start)
    log.Printf("Took %s", elapsed)
}
複製代碼

gorm.Open的第一個參數是數據庫驅動名字,第二個就是具體的db uri,我這裏用了默認的test庫,用戶名root,密碼爲空,字符集爲utf-8。

爲了每次都初始化表,因此先用DropTableIfExists刪除表,再用AutoMigrate建立表。Migrate是遷移的意思,自動遷移僅僅會建立表,缺乏列和索引,而且不會改變現有列的類型或刪除未使用的列以保護數據。這樣能夠更新表結構,通常不會直接用CreateTable建立表了。db.Set是爲了建立表時添加表後綴。

parseUrls的最後一個參數db是*gorm.DB類型的。

好了,運行一下:

❯ go run doubanCrawler.go
複製代碼

執行結束能夠用MySQL CLI看到數據了:

mysql> select * from movies order by id desc limit 3;
+----------|-----------------------------+
| id       | title                       |
+----------|-----------------------------+
| 30170448 | 何覺得家                    |
| 27191492 | 四個春天                    |
| 26799731 | 請以你的名字呼喚我          |
+----------|-----------------------------+
3 rows in set (0.00 sec)
複製代碼

用gorm查詢

上面用到的只是db.Create建立記錄,接着看看怎麼查詢(按行註釋):

func main() {
    db, err := gorm.Open("mysql", "root:@/test?charset=utf8")
    defer db.Close()
    checkError(err)

    var movie Movie
    var movies []Movie
    db.First(&movie, 30170448)  // 查詢ID爲30170448的記錄,只支持主鍵
    log.Println(movie)
    log.Println(movie.ID, movie.Title) // 能夠得到對應屬性

    db.Order("id").Limit(3).Find(&movies) // 按ID升序排,取前三個記錄賦值給movies
    log.Println(movies)
    log.Println(movies[0].ID)

    db.Order("id desc").Limit(3).Offset(1).Find(&movies)  // Order也支持desc選擇降序, offset表示對結果集從第2個記錄開始
    log.Println(movies)
    db.Select("title").First(&movies, "title = ?", "四個春天") // 用Select能夠限定只返回那些字段,First也支持條件
    log.Println(movie)

    var count int64
    db.Where("id = ?", 30170448).Or("title = ?", "四個春天").Find(&movies).Count(&count)  // Where後加條件,在這裏是id爲30170448或者title爲"四個春天"2個條件符合之一便可,最後用Count算一下符合的記錄數
    log.Println(count)
}
複製代碼

執行一下:

❯ go run doubanCrawler2.go
2019/07/13 21:16:24 {30170448 何覺得家}
2019/07/13 21:16:24 30170448 何覺得家
2019/07/13 21:16:24 [{1291543 功夫} {1291545 大魚} {1291546 霸王別姬}]
2019/07/13 21:16:24 1291543
2019/07/13 21:16:24 [{27191492 四個春天} {26799731 請以你的名字呼喚我} {26787574 奇蹟男孩}]
2019/07/13 21:16:24 [{0 何覺得家}]
2019/07/13 21:16:24 {30170448 何覺得家}
2019/07/13 21:16:24 2
複製代碼

更新和刪除用法就不展現了,看文檔便可。

代碼地址

完整代碼能夠在這個地址找到。

延伸閱讀

  1. gorm.io/docs/
相關文章
相關標籤/搜索