『No19: Gorm 上手指南』

image.png

大家好,我叫謝偉,是一名程序員。

如果你是做后端開發(fā)的,日常工作中,除了熟悉編程語(yǔ)言之外,數(shù)據(jù)庫(kù)怕是最常用的技術(shù)了吧。

比如搭建一個(gè)Web后臺(tái)管理系統(tǒng),你需要數(shù)據(jù)吧,你總不能指望網(wǎng)頁(yè)都是靜態(tài)數(shù)據(jù)吧。需要數(shù)據(jù),那么就要和數(shù)據(jù)庫(kù)打交道。日常開發(fā)中你可能會(huì)使用關(guān)系型數(shù)據(jù)庫(kù),比如 MySQL、PostgresSQL,也可能使用NoSQL型數(shù)據(jù)庫(kù),比如MongoDB,redis等,甚至?xí)褂酶鞣N各樣的符合特定場(chǎng)景下的數(shù)據(jù)庫(kù)。

但我建議,至少需要熟練掌握一門關(guān)系型數(shù)據(jù)庫(kù),日常開發(fā)中你會(huì)發(fā)現(xiàn)絕大多數(shù)的需求的實(shí)現(xiàn)都需要和數(shù)據(jù)庫(kù)打交道。你僅僅只會(huì)簡(jiǎn)單的增刪改查,是不太夠用的。僅僅只會(huì)在編程語(yǔ)言層面編寫簡(jiǎn)單SQL,也是不太夠用。

你需要會(huì):

  • 數(shù)據(jù)庫(kù)的設(shè)計(jì):數(shù)據(jù)庫(kù)設(shè)計(jì)三大范式
  • 數(shù)據(jù)庫(kù)多表操作
  • 數(shù)據(jù)庫(kù)服務(wù)端操作
  • 備份恢復(fù)
  • 事務(wù)等操作
  • 分庫(kù)分表等操作

本節(jié)的主題:gorm 的使用。

大綱:

  • 原生database/sql 接口
  • 豐富的第三方驅(qū)動(dòng)
  • gorm 的使用

1. 原生 database/sql 接口

Go 官方并沒(méi)有提供數(shù)據(jù)庫(kù)驅(qū)動(dòng),只定義了一些標(biāo)準(zhǔn)的接口。所以你會(huì)看看各種各樣的第三方驅(qū)動(dòng)。

本質(zhì)上都在實(shí)現(xiàn)官方提供的標(biāo)準(zhǔn)接口。

sql.Register

作用用來(lái)注冊(cè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)。

所以你使用第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng),最重要的一點(diǎn)就是要導(dǎo)入該庫(kù),實(shí)現(xiàn)第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng)的init 函數(shù)。該init 函數(shù)即是實(shí)現(xiàn)注冊(cè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)。

sqlite3 導(dǎo)入: _ "github.com/mattn/go-sqlite3"

init 函數(shù)


func init() {
    sql.Register("sqlite3", &SQLiteDriver{})
}

原生函數(shù)如下:

func Register(name string, driver driver.Driver) {
    driversMu.Lock()
    defer driversMu.Unlock()
    if driver == nil {
        panic("sql: Register driver is nil")
    }
    if _, dup := drivers[name]; dup {
        panic("sql: Register called twice for driver " + name)
    }
    drivers[name] = driver
}

可以看出,核心是driver.Driver接口,定義了一個(gè) open 方法

type Driver interface {
    // Open returns a new connection to the database.
    // The name is a string in a driver-specific format.
    //
    // Open may return a cached connection (one previously
    // closed), but doing so is unnecessary; the sql package
    // maintains a pool of idle connections for efficient re-use.
    //
    // The returned connection is only used by one goroutine at a
    // time.
    Open(name string) (Conn, error)
}

Conn 是一個(gè)數(shù)據(jù)庫(kù)連接接口,定義了Prepare、Close、Begin方法

  • Prepare:SQL語(yǔ)句準(zhǔn)備階段
  • Close: 關(guān)閉連接
  • Begin: 事務(wù)處理
type Conn interface {
    // Prepare returns a prepared statement, bound to this connection.
    Prepare(query string) (Stmt, error)

    // Close invalidates and potentially stops any current
    // prepared statements and transactions, marking this
    // connection as no longer in use.
    //
    // Because the sql package maintains a free pool of
    // connections and only calls Close when there's a surplus of
    // idle connections, it shouldn't be necessary for drivers to
    // do their own connection caching.
    Close() error

    // Begin starts and returns a new transaction.
    //
    // Deprecated: Drivers should implement ConnBeginTx instead (or additionally).
    Begin() (Tx, error)
}

Stmt是一種準(zhǔn)備好的狀態(tài),和Conn相關(guān)聯(lián),而且只能應(yīng)用于一個(gè)goroutine中,不能應(yīng)用于多個(gè)goroutine。


type Stmt interface {
    // Close closes the statement.
    //
    // As of Go 1.1, a Stmt will not be closed if it's in use
    // by any queries.
    Close() error

    // NumInput returns the number of placeholder parameters.
    //
    // If NumInput returns >= 0, the sql package will sanity check
    // argument counts from callers and return errors to the caller
    // before the statement's Exec or Query methods are called.
    //
    // NumInput may also return -1, if the driver doesn't know
    // its number of placeholders. In that case, the sql package
    // will not sanity check Exec or Query argument counts.
    NumInput() int

    // Exec executes a query that doesn't return rows, such
    // as an INSERT or UPDATE.
    //
    // Deprecated: Drivers should implement StmtExecContext instead (or additionally).
    Exec(args []Value) (Result, error)

    // Query executes a query that may return rows, such as a
    // SELECT.
    //
    // Deprecated: Drivers should implement StmtQueryContext instead (or additionally).
    Query(args []Value) (Rows, error)
}

大概就是定義了一系列的標(biāo)準(zhǔn)接口,接口內(nèi)注意輸入?yún)?shù)和返回值,然后一步步下去,就大概可以知道官方的接口是如何定義的。

官方的這些接口,需要被第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng)實(shí)現(xiàn),不管是sqlite、mysql、PostgresSQL 都需要實(shí)現(xiàn)這些接口,實(shí)際的使用過(guò)程中調(diào)用這些接口即可。

比如:

數(shù)據(jù)庫(kù)表:

id created_at updated_at deleted_at type_name
1 "2018-08-11 13:29:45.773658+08" "2018-08-11 13:29:45.773658+08" "詩(shī)"
2 "2018-08-11 13:29:45.779012+08" "2018-08-11 13:29:45.779012+08" "詞"
3 "2018-08-11 13:29:45.781381+08" "2018-08-11 13:29:45.781381+08" "曲"
4 "2018-08-11 13:29:45.784009+08" "2018-08-11 13:29:45.784009+08" "文言文"

如何獲取這4條記錄:


package main

import (
    "database/sql"

    "fmt"

    "time"

    _ "github.com/jinzhu/gorm/dialects/postgres"
)

func main() {
    db, _ := sql.Open("postgres", "host=127.0.0.1 user=xiewei dbname=crawler_info port=5432 sslmode=disable password=admin")

    rows, _ := db.Query(`select * from poetry_types limit 4`)
    fmt.Println(rows.Columns())
    for rows.Next() {
        var id int
        var createdAt time.Time
        var updatedAt time.Time
        var deletedAt *time.Time
        var typeName string
        rows.Scan(&id, &createdAt, &updatedAt, &deletedAt, &typeName)
        fmt.Println(id, createdAt, updatedAt, deletedAt, typeName)
    }

}

使用這種方法,你只需知道原生database/sql 的接口是如何調(diào)用的即可,具體的實(shí)現(xiàn)由第三方已實(shí)現(xiàn)。

知道數(shù)據(jù)庫(kù)(crawler_info), 知道數(shù)據(jù)庫(kù)表(poetry_type),知道數(shù)據(jù)庫(kù)內(nèi)字段的類型( id int, createdAt time.Time, updatedAt time.Time, deletedAt *time.Time, typeName string)

即可寫SQL 語(yǔ)句操作數(shù)據(jù)庫(kù),實(shí)現(xiàn)增刪改查。

這種接口的定義有什么好處?

不管你操作sqlite、mysql、PostgreSQL 等數(shù)據(jù)庫(kù),只是連接驅(qū)動(dòng)這塊不一致,其他的操作一模一樣。

# postgresql
    db, _ := sql.Open("postgres", "host=127.0.0.1 user=xiewei dbname=crawler_info port=5432 sslmode=disable password=admin")

# sqlite
    db, _ := sql.Open("sqlite3", "*****")

# mysql
    db, _ := sql.Open("mysql", "*****")


2. gorm

使用上述方法的缺點(diǎn)是使你代碼內(nèi)充斥著 SQL 語(yǔ)句。使用 ORM (對(duì)象關(guān)系映射)可以解決這個(gè)問(wèn)題,使我們操作對(duì)象即可達(dá)到操作數(shù)據(jù)庫(kù)的目的。

gorm 的使用步驟:

  • 定義model 即對(duì)象層(知道操作的對(duì)象是誰(shuí))
  • 建立連接
  • 創(chuàng)建數(shù)據(jù)表(數(shù)據(jù)庫(kù)中存在表也可不執(zhí)行該步,定義model 即可,字段變更會(huì)新增字段)
  • 操作數(shù)據(jù)庫(kù)

package main

import (
  "github.com/jinzhu/gorm"
  _ "github.com/jinzhu/gorm/dialects/sqlite"
)


// 聲明數(shù)據(jù)庫(kù)表的形式

type Product struct {
  gorm.Model
  Code string
  Price uint
}

func main() {

  // 建立連接
  db, err := gorm.Open("sqlite3", "test.db")
  if err != nil {
    panic("failed to connect database")
  }
  defer db.Close()

  // 生成數(shù)據(jù)庫(kù)表
  db.AutoMigrate(&Product{})

  // 新增一條記錄:將Product 對(duì)象轉(zhuǎn)換成數(shù)據(jù)庫(kù)內(nèi)一條記錄
  db.Create(&Product{Code: "L1212", Price: 1000})

  // 獲取對(duì)象:將數(shù)據(jù)庫(kù)內(nèi)一條記錄轉(zhuǎn)換成 product 對(duì)象
  var product Product
  db.First(&product, 1) // find product with id 1
  db.First(&product, "code = ?", "L1212") // find product with code l1212

  // 更新記錄
  db.Model(&product).Update("Price", 2000)

  // 刪除記錄
  db.Delete(&product)
}

使用的orm 技術(shù)的關(guān)鍵在于理解,對(duì)象和數(shù)據(jù)庫(kù)表的映射。

1. 要操作數(shù)據(jù)庫(kù)內(nèi)已存在的數(shù)據(jù)表怎么使用 orm ?

  • 定義和數(shù)據(jù)庫(kù)內(nèi)對(duì)應(yīng)的數(shù)據(jù)表的model

數(shù)據(jù)內(nèi)存在這么一張表(dynasties):

id  created_at                  updated_at              deleted_at  name
1   2018-08-11 05:29:45.647432  2018-08-11 05:29:45.647432      先秦
2   2018-08-11 05:29:45.660827  2018-08-11 05:29:45.660827      兩漢
3   2018-08-11 05:29:45.664372  2018-08-11 05:29:45.664372      魏晉
4   2018-08-11 05:29:45.668520  2018-08-11 05:29:45.668520      南北朝
5   2018-08-11 05:29:45.672059  2018-08-11 05:29:45.672059      隋代
6   2018-08-11 05:29:45.675465  2018-08-11 05:29:45.675465      唐代
7   2018-08-11 05:29:45.678486  2018-08-11 05:29:45.678486      五代
8   2018-08-11 05:29:45.680459  2018-08-11 05:29:45.680459      宋代
9   2018-08-11 05:29:45.682840  2018-08-11 05:29:45.682840      金朝
10  2018-08-11 05:29:45.684378  2018-08-11 05:29:45.684378      元代
11  2018-08-11 05:29:45.686548  2018-08-11 05:29:45.686548      明代
12  2018-08-11 05:29:45.688638  2018-08-11 05:29:45.688638      清代
13  2018-08-11 12:02:59.433187  2018-08-11 12:02:59.433187      近代
14  2018-08-11 12:29:02.976485  2018-08-11 12:29:02.976485      現(xiàn)代
15  2018-08-11 12:29:52.015595  2018-08-11 12:29:52.015595      未知

則定義 model 對(duì)象 :

type Dynasty struct {
    gorm.Model
    Name string `gorm:"tye:varchar" json:"name"`
}

gorm.model gorm 內(nèi)置的 格式如下:

type Model struct {
    ID        uint `gorm:"primary_key"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt *time.Time `sql:"index"`
}
  • 默認(rèn)取定義的結(jié)構(gòu)體的小寫的復(fù)數(shù)形式為數(shù)據(jù)庫(kù)表名(Dynasty/dynasties)

建立連接即可操作:

db, err := gorm.open("postgres", "*****")
  • 獲取記錄:

newDynastyRecord 即獲取到id = 1 的一條記錄。該條記錄的值填充在newDynastyRecord內(nèi)。

比如獲取該條記錄的name: 即 newDynastyRecord.Name


var newDynastyRecord Dynasty

db.Where("id = ?", 1).First(&newDynastyRecord)

fmt.Println(newDynastyRecord.Name)

var newDynastyRecordList []Dynasty

db.Find(&newDynastyRecordList)

fmt.Println(len(newDynastyRecordList)) // 15

  • Where 限定條件,和 sql 語(yǔ)句中的 Where 用法差不多
  • First 即獲取滿足條件的第一條記錄
  • Find 可以獲取滿足條件的所有記錄

2. 數(shù)據(jù)庫(kù)內(nèi)不存在數(shù)據(jù)庫(kù)表怎么操作

  • 定義 model 對(duì)象
type PoetryType struct {
    gorm.Model
    TypeName string `gorm:"type:varchar" json:"type_name"`
}

不存在的表,首先需要?jiǎng)?chuàng)建表:db.AutoMigrate(&PoetryType)

創(chuàng)建之后操作:

即把新增的記錄新增入poetry_types 表內(nèi)

var poetryType PoetryType
poetryType = PoetryType{
    Typaname: "詩(shī)"
}

db.create(&poetryType)

核心只有一條,在 gorm 內(nèi)操作對(duì)象(struct),以達(dá)到操作數(shù)據(jù)表的目的。有對(duì)象,沒(méi)數(shù)據(jù)庫(kù)表,操作失敗;沒(méi)對(duì)象,有數(shù)據(jù)庫(kù)表,操作失敗。找不到對(duì)應(yīng)關(guān)系,操作失敗; 對(duì)應(yīng)關(guān)系搞錯(cuò),操作失敗。

3. 實(shí)例

我這邊利用爬蟲技術(shù),把一系列的詩(shī)人的基本信息,詩(shī)人的詩(shī)文,朝代和詩(shī)的類型入庫(kù)了。

大概的樣子如下:

dynasties 表:朝代

1   2018-08-11 05:29:45.647432  2018-08-11 05:29:45.647432      先秦
2   2018-08-11 05:29:45.660827  2018-08-11 05:29:45.660827      兩漢
3   2018-08-11 05:29:45.664372  2018-08-11 05:29:45.664372      魏晉

poetry_types 表:詩(shī)類型


1   2018-08-11 05:29:45.773658  2018-08-11 05:29:45.773658      詩(shī)
2   2018-08-11 05:29:45.779012  2018-08-11 05:29:45.779012      詞
3   2018-08-11 05:29:45.781381  2018-08-11 05:29:45.781381      曲

poetry_infos 表:詩(shī)文的具體內(nèi)容

1   2018-08-11 13:39:37.341955  2018-08-11 13:39:37.341955      將進(jìn)酒 君不見(jiàn),黃河之水天上來(lái),奔流到海不復(fù)回。君不見(jiàn),高堂明鏡悲白發(fā),朝如青絲暮成雪。人生得意須盡歡,莫使金樽空對(duì)月。天生我材必有用,千金散盡還復(fù)來(lái)。烹羊宰牛且為樂(lè),會(huì)須一飲三百杯。岑夫子,丹丘生,將進(jìn)酒,杯莫停。與君歌一曲,請(qǐng)君為我傾耳聽。(傾耳聽 一作:側(cè)耳聽)鐘鼓饌玉不足貴,但愿長(zhǎng)醉不復(fù)醒。(不足貴 一作:何足貴;不復(fù)醒 一作:不愿醒/不用醒)古來(lái)圣賢皆寂寞,惟有飲者留其名。(古來(lái) 一作:自古;惟 通:唯)陳王昔時(shí)宴平樂(lè),斗酒十千恣歡謔。主人何為言少錢,徑須沽取對(duì)君酌。五花馬,千金裘,呼兒將出換美酒,與爾同銷萬(wàn)古愁。   34290   1   6
2   2018-08-11 13:39:37.414925  2018-08-11 13:39:37.414925      水調(diào)歌頭·明月幾時(shí)有  "丙辰中秋,歡飲達(dá)旦,大醉,作此篇,兼懷子由。
明月幾時(shí)有?把酒問(wèn)青天。不知天上宮闕,今夕是何年。我欲乘風(fēng)歸去,又恐瓊樓玉宇,高處不勝寒。起舞弄清影,何似在人間?(何似 一作:何時(shí);又恐 一作:惟 / 唯恐)轉(zhuǎn)朱閣,低綺戶,照無(wú)眠。不應(yīng)有恨,何事長(zhǎng)向別時(shí)圓?人有悲歡離合,月有陰晴圓缺,此事古難全。但愿人長(zhǎng)久,千里共嬋娟。(長(zhǎng)向 一作:偏向)"    33531   2   8

poets 表:詩(shī)人信息

1   2018-08-11 13:39:37.334029  2018-08-11 13:39:37.334029      李白  https://img.gushiwen.org/authorImg/libai.jpg    1310    6169    李白(701年-762年),字太白,號(hào)青蓮居士,唐朝浪漫主義詩(shī)人,被后人譽(yù)為“詩(shī)仙”。祖籍隴西成紀(jì)(待考),出生于西域碎葉城,4歲再隨父遷至劍南道綿州。李白存世詩(shī)文千余篇,有《李太白集》傳世。762年病逝,享年61歲。其墓在今安徽當(dāng)涂,四川江油、湖北安陸有紀(jì)念館。 1310篇詩(shī)文    6
2   2018-08-11 13:39:37.412562  2018-08-11 13:39:37.412562      蘇軾  https://img.gushiwen.org/authorImg/sushi.jpg    3637    4024    蘇軾(1037-1101),北宋文學(xué)家、書畫家、美食家。字子瞻,號(hào)東坡居士。漢族,四川人,葬于潁昌(今河南省平頂山市郟縣)。一生仕途坎坷,學(xué)識(shí)淵博,天資極高,詩(shī)文書畫皆精。其文汪洋恣肆,明白暢達(dá),與歐陽(yáng)修并稱歐蘇,為“唐宋八大家”之一;詩(shī)清新豪健,善用夸張、比喻,藝術(shù)表現(xiàn)獨(dú)具風(fēng)格,與黃庭堅(jiān)并稱蘇黃;詞開豪放一派,對(duì)后世有巨大影響,與辛棄疾并稱蘇辛;書法擅長(zhǎng)行書、楷書,能自創(chuàng)新意,用筆豐腴跌宕,有天真爛漫之趣,與黃庭堅(jiān)、米芾、蔡襄并稱宋四家;畫學(xué)文同,論畫主張神似,提倡“士人畫”。著有《蘇東坡全集》和《東坡樂(lè)府》等。 3637篇詩(shī)文    8
3   2018-08-11 13:39:37.495146  2018-08-11 13:39:37.495146      陶淵明 https://img.gushiwen.org/authorImg/taoyuanming.jpg  186 1466    陶淵明(約365年—427年),字元亮,(又一說(shuō)名潛,字淵明)號(hào)五柳先生,私謚“靖節(jié)”,東晉末期南朝宋初期詩(shī)人、文學(xué)家、辭賦家、散文家。漢族,東晉潯陽(yáng)柴桑人(今江西九江)。曾做過(guò)幾年小官,后辭官回家,從此隱居,田園生活是陶淵明詩(shī)的主要題材,相關(guān)作品有《飲酒》、《歸園田居》、《桃花源記》、《五柳先生傳》、《歸去來(lái)兮辭》等。 186篇詩(shī)文    3

基于上述的信息,構(gòu)建如下API 服務(wù):

圖片發(fā)自簡(jiǎn)書App

核心步驟如下:

  • 建立 gin api server
  • 根據(jù)上文的數(shù)據(jù)庫(kù)表定義對(duì)象 model
  • gin 路由和控制器的編寫

api server

func GinInit() {
    r := gin.New()
    gin.SetMode(gin.DebugMode)

    r.Use(gin.Logger())
    //r.Use(gin.Recovery())

    v1 := r.Group("/v1/api")
    {
        poet.Register(v1) // 所有的路由和控制器
    }
    getAllApi(r)

    r.Run(":8080")

}

路由和控制器

package poet

import "github.com/gin-gonic/gin"

func Register(r *gin.RouterGroup) {
    r.GET("/poet/:id", ShowPoetHandler)
    r.GET("/poetry/:id", ShowPoetryHandler)
    r.GET("/dynasty/:id", ShowDynastyHandler)
    r.GET("/poetryType/:id", ShowPoetTypeHandler)
    r.GET("/gushiwen/poet", ShowListPoetHandler)
    r.GET("/gushiwen/poetry", ShowListPoetryHandler)
    r.GET("/gushiwen/dynasty", ShowListDynastyHandler)
    r.GET("/gushiwen/poetryType", ShowListPoetTypeHandler)
}


model

package model

import "github.com/jinzhu/gorm"

// 朝代的表: 先秦、兩漢、魏晉、南北朝、隋代、唐代、五代、宋代、金朝、元代、明代、清代...
type Dynasty struct {
    gorm.Model
    Name string `gorm:"tye:varchar" json:"name"`
}

// 詩(shī)人的表
type Poet struct {
    gorm.Model
    Name        string `gorm:"type:varchar" json:"name"`
    ImageURL    string `gorm:"type:varchar" json:"image_url"`
    Number      uint   `gorm:"type:integer" json:"number"`
    Liked       uint   `gorm:"type:integer" json:"liked"`
    Description string `gorm:"type:varchar" json:"description"`
    DynastyID   uint   `gorm:"type:integer" json:"dynasty_id"`
    PoetryInfo  []PoetryInfo
}

// 詩(shī)類型的表: 詩(shī)、詞、曲、文言文
type PoetryType struct {
    gorm.Model
    TypeName string `gorm:"type:varchar" json:"type_name"`
}

// 詩(shī)文的表
type PoetryInfo struct {
    gorm.Model
    Title     string `gorm:"type:varchar" json:"title"`
    Content   string `gorm:"type:varchar" json:"content"`
    Liked     uint   `gorm:"type:integer;default(0)" json:"liked"`
    PoetID    uint   `gorm:"type:integer" json:"poet_id"`
    DynastyID uint   `gorm:"type:integer" json:"dynasty_id"`
}

路由 1 和 控制器 1

r.GET("/poet/:id", ShowPoetHandler)

func NotFound() (int, map[string]interface{}) {
    return http.StatusBadGateway, gin.H{"Message": "not found record"}

}

func ShowPoetHandler(c *gin.Context) {
    id, _ := strconv.Atoi(c.Param("id"))

    var poet model.Poet
    if dbError := initial.DataBase.Where("id = ?", id).First(&poet).Error; dbError != nil {
        c.JSON(NotFound())
        return
    }
    c.JSON(http.StatusOK, poet)

}

路由 2 和控制 2

r.GET("/gushiwen/poet", ShowListPoetHandler)

type ListPoetParams struct {
    CommonPagination
    Search     string `form:"search" json:"search"`
    Return     string `form:"return" json:"return" binding:"omitempty,eq=all_list|eq=all_count"`
    PoetNumber uint   `form:"number" json:"number"`
    Like       uint   `form:"like" json:"like"`
    Dynasty    string `form:"dynasty" json:"dynasty"`
}

func ShowListPoetHandler(c *gin.Context) {

    var param ListPoetParams
    if err := c.ShouldBind(&param); err != nil {
        fmt.Println("error", err)
        return
    }
    offset := param.PerPage * (param.Page - 1)
    order := param.OrderBy + " " + param.SortBy

    var poet []model.Poet
    query := initial.DataBase.Preload("PoetryInfo").Model(&poet)
    if param.Return != "all_list" {
        query = query.Offset(offset).Limit(param.PerPage)
    }

    if param.Search != "" {
        query = query.Where("name = ?", param.Search)
    }

    if param.PoetNumber != 0 {
        query = query.Where("number < ?", param.PoetNumber)
    }

    if param.Like != 0 {
        query = query.Where("liked < ?", param.Like)
    }

    if param.Dynasty != "" {
        var dynasty model.Dynasty
        if dbError := initial.DataBase.Where("name = ?", param.Dynasty).First(&dynasty).Error; dbError != nil {
            c.JSON(NotFound())
            return
        }
        query = query.Where("dynasty_id = ?", dynasty.ID)
    }
    query.Order(order).Find(&poet)
    c.JSON(http.StatusOK, poet)
}

效果演示:

image.png
image.png
image.png

實(shí)際操作的SQL 語(yǔ)句:

image.png

參考代碼: 參考代碼

  1. 使用的PostgreSQL 數(shù)據(jù)庫(kù),確保本機(jī)有服務(wù)啟動(dòng)
  2. 先db 下的壓縮包使用 pg_restore 命令導(dǎo)入數(shù)據(jù)庫(kù)
  3. 修改 configs/setting.yml 數(shù)據(jù)庫(kù)配置
  4. make install 安裝依賴庫(kù)
  5. make dev 啟動(dòng)服務(wù)

全文完,再會(huì)。

多看文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評(píng)論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,371評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容