關於gorm多表聯合查詢(left join)


Golang很流行,但是有些方面資料很少而且不詳實,譬如:gorm的聯合查詢,當然,也不推薦復雜語句使用orm模型。

現將自己總結的寫法和遇到的坑記錄如下: Golang要求使用“駝峰命名法”,比如systemId,因為我以前用的是Python,使用Django的orm序列化后返回的參數和數據庫表字段一致,基於這個不適合Go的思路,我將表字段也建成了systemId,和struct映射參數相同。(其實表字段應該命名為system_id)

一、下面建兩張表,用於聯合查詢(以left join示例)

MySQL > desc go_system_info; +——————+——————-+———+——-+——————-+————————+ | Field | Type | Null | Key | Default | Extra | +——————+——————-+———+——-+——————-+————————+ | id | int(11) | NO | PRI | NULL | auto_increment | | systemId | varchar(30) | NO | MUL | NULL | | | systemName | varchar(50) | NO | | defaultNull | | +——————+——————-+———+——-+——————-+————————+ 3 rows in set (0.01 sec) MySQL > desc go_service_info; +——————-+——————-+———+——-+——————-+————————+ | Field | Type | Null | Key | Default | Extra | +——————-+——————-+———+——-+——————-+————————+ | id | int(11) | NO | PRI | NULL | auto_increment | | systemId | varchar(30) | NO | MUL | NULL | | | serviceId | varchar(50) | NO | MUL | defaultNull | | | serviceName | varchar(50) | NO | | defaultNull | | +——————-+——————-+———+——-+——————-+————————+ 4 rows in set (0.00 sec) MySQL >

二、表建好后,我們來定義表結構體:

type GoSystemInfo struct {
    ID int `gorm:"primary_key"` SystemId string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"` SystemName string `gorm:"column:systemName;type:varchar(50);not null;default:'defaultNull'"` } type GoServiceInfo struct { ID int `gorm:"primary_key"` SystemId string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"` ServiceId string `gorm:"column:serviceId;type:varchar(50);not null;default:'defaultNull';index:ServiceId"` ServiceName string `gorm:"column:serviceName;type:varchar(50);not null;default:'defaultNull'"` }

小知識:ORM(Object Relation Mapping),對象關系映射,實際上就是對數據庫的操作進行封裝,對上層開發人員屏蔽數據操作的細節,開發人員看到的就是一個個對象,大大簡化了開發工作,提高了生產效率,也可以避免sql注入等問題。

由於gorm是使用的orm映射,所以需要定義要操作的表的model,在go中需要定義一個struct, struct的名字就是對應數據庫中的表名,注意gorm查找struct名對應數據庫中的表名的時候會默認把你的struct中的大寫字母轉換為小寫並加上“s”,所以可以加上 db.SingularTable(true) 讓gorm轉義struct名字的時候不用加上“s”。

golang中,首字母大小寫來表示public或者private,因此結構體中字段首字母必須大寫。

定義model,即struct時,我們可以只定義我們需要從數據庫中取回的特定字段: gorm在轉義表名的時候會把struct的大寫字母(首字母除外) 替換成“_”,所以下面的”GoSystemInfo”會轉義成數據庫中對應的“go_system_info”的表名, 對應的字段名的查找會先按照tag里面的名稱去里面查找,如果沒有定義標簽則按照struct定義的字段查找,查找的時候struct字段中的大寫會被轉義成“_”,如:“SystemId”會去查找表中的system_id字段。

在本例,我們在struct使用如gorm:”column:systemId”,column映射mysql表字段名稱。

三、聯合查詢

單表查詢用上面的原表結構體接收數據就可以了, 聯合查詢涉及兩張表中的全部/部分數據,我們定義新的結構體接收取回的特定字段:

type result struct {
    SystemId    string `json:"systemId"` SystemName string `json:"systemName"` ServiceId string `json:"serviceId"` ServiceName string `json:"serviceName"` }

我們從go_service_info取serviceId、serviceName,從go_system_info取對應的systemId、systemName:

db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId").Scan(&results)

where條件:

db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId where go_service_info.serviceId <> ? and go_system_info.systemId = ?", "xxx", "xxx").Scan(&results)

注意:這里需要使用別名as system_id,映射返回值結構體,並且因為查找的時候struct字段中的大寫會被轉義成“_”,所以別名也要將大寫轉為“_”。

若使用原生語句:

db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId").Scan(&results)

where條件:

db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId where a.serviceId <> ? and b.systemId = ?", "xxx", "xxx").Scan(&results)

結果相同。

避坑建議: 表字段命名為如system_id,默認映射到結構體字段SystemId。當然建表原則上也是用小寫和下划線,不過歷史表難免會有大寫命名的情況,所以新表還是遵照相關規范吧。

源碼:

package main
import ( "fmt" "log" "encoding/json" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) type GoSystemInfo struct { ID int `gorm:"primary_key"` SystemId string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"` SystemName string `gorm:"column:systemName;type:varchar(50);not null;default:'defaultNull'"` } type GoServiceInfo struct { ID int `gorm:"primary_key"` SystemId string `gorm:"column:systemId;type:varchar(30);not null;index:SystemId"` ServiceId string `gorm:"column:serviceId;type:varchar(50);not null;default:'defaultNull';index:ServiceId"` ServiceName string `gorm:"column:serviceName;type:varchar(50);not null;default:'defaultNull'"` } type result struct { SystemId string `json:"systemId"` SystemName string `json:"systemName"` ServiceId string `json:"serviceId"` ServiceName string `json:"serviceName"` } //定義數據庫連接 type ConnInfo struct { MyUser string Password string Host string Port int Db string } func dbConn(MyUser, Password, Host, Db string, Port int) *gorm.DB { connArgs := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", MyUser,Password, Host, Port, Db ) db, err := gorm.Open("mysql", connArgs) if err != nil { log.Fatal(err) } db.SingularTable(true) return db } func mapToJson(result interface{}) string { // map轉 json str jsonBytes, _ := json.Marshal(result) jsonStr := string(jsonBytes) return jsonStr } func main() { var results []result cn := ConnInfo{ "xxx", "xxx", "127.0.0.1", 3306, "xxx", } db := dbConn(cn.MyUser,cn.Password,cn.Host,cn.Db,cn.Port) defer db.Close() /* // 創建表 db.AutoMigrate(&GoSystemInfo{}) product := GoSystemInfo{SystemId:"sysid", SystemName:"sysname"} fmt.Println(db.NewRecord(product)) db.AutoMigrate(&GoServiceInfo{}) products := GoServiceInfo{SystemId:"sysid", ServiceId:"serid", ServiceName:"sername"} fmt.Println(db.NewRecord(products)) */ // 聯合查詢(left join) db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId").Scan(&results) fmt.Println(mapToJson(results)) // where db.Table("go_service_info").Select("go_service_info.serviceId as service_id, go_service_info.serviceName as service_name, go_system_info.systemId as system_id, go_system_info.systemName as system_name").Joins("left join go_system_info on go_service_info.systemId = go_system_info.systemId where go_service_info.serviceId <> ? and go_system_info.systemId = ?", "xxx", "xxx").Scan(&results) fmt.Println(mapToJson(results)) // 原生sql db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId").Scan(&results) fmt.Println(mapToJson(results)) // where db.Raw("SELECT a.serviceId as service_id,a.serviceName as service_name, b.systemId as system_id, b.systemName as system_name FROM go_service_info a LEFT JOIN go_system_info b ON a.systemId = b.systemId where a.serviceId <> ? and b.systemId = ?", "xxx", "xxx").Scan(&results) fmt.Println(mapToJson(results)) }

示例結果:

參考:https://www.jb51.net/article/151051.htm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM