go web開發(gin&gorm) 之DB配置及DAO的基本使用
轉載請註明出處: ofollow,noindex">https://www.cnblogs.com/funnyzpc/p/9501376.html
```
我先閒扯下,前天(也就是2018年11月16號)的某個時候,忽然有人在QQ上私聊我,一看是公司群以為是有人來慰問新人了,也沒弄清楚身份就調侃起來,就這樣:
問題是:我竟傻乎乎滴沒看出來是行政那邊的人,中午吃飯的時候和老同事聊起此事,才知道這位大鍋是人事部boss,一時間感覺事情變得搞笑起來,當然,有意思的還不止這一件,就在兩週前入職的時候,當時是複試,行政總監把車開到我之前公司樓下接我,出發到現場前給我買了杯咖啡,我說美式中杯就好了,這人說怎麼也得大杯,面試過了後,到晚上,這人又發朋友圈說他興奮的狠。。。
說實話,二當家也真夠zuo的。。。:sweat_smile:,當然這夥計在我第一面的時候就閒聊了一個多小時,還不止,他竟然知道我小名:sweat:
```
閒聊到這兒,現在就進入本次的主題: golang web開發之Dao配置
在正式進入主題前,先說說框架的現狀,個人用的是gin-gonic框架,這是個在校大學生寫的基於go語言的高效能web框架,在此之前我對比過beego 、 iris 、gin-gonic這幾個在維護頻度和依賴支援以及star熱度方面,個人選擇了gin-gonic這個框架 ,同時也在github上選用了一套比較前衛的成型的框架程式碼,東西十分的好,但是個人覺得框架整合的mysql實在是看不下去(主要是效能低了+ 穩定性不夠好+升級麻煩),遂就將資料庫換成postgresql,配置完成就開始測試Dao,需要說的是其中gorm是位臺灣胸弟寫的ORM框架,於是開始~
且先不管現有的mysql的配置,由於框架本身只集成了mysql,所以現在需要安裝一個pg的連線driver,放到指定的目錄就裝好依賴了,至於怎麼安裝,大致有二。
A>其一是使用go命令直接安裝 :
1 go get -u github.com/lib/pq
B>其二是跟我一樣keng地手動安裝 ,就是找到github.com的原始碼頁面,將整個專案以一個zip包下載下來,而後解壓到指定目錄
需要注意的是 手動安裝一定要將github.com後面的路徑改成以目錄為結構的包地址 。
連線元件安裝完畢開始寫一個db.go的資料庫初始化類和一個引數結構體,這裡我給出原始碼:
引數結構體:
1 package config 2 3 import ( 4"encoding/json" 5"fmt" 6"io/ioutil" 7"os" 8"regexp" 9"strings" 10"unicode/utf8" 11 12"github.com/shen100/golang123/utils" 13 ) 14 15 var jsonData map[string]interface{} 16 17 func initJSON() { 18bytes, err := ioutil.ReadFile("./config.json") 19if err != nil { 20fmt.Println("ReadFile: ", err.Error()) 21os.Exit(-1) 22} 23 24configStr := string(bytes[:]) 25reg := regexp.MustCompile(`/\*.*\*/`) 26 27configStr = reg.ReplaceAllString(configStr, "") 28bytes = []byte(configStr) 29 30if err := json.Unmarshal(bytes, &jsonData); err != nil { 31fmt.Println("invalid config: ", err.Error()) 32os.Exit(-1) 33} 34 } 35 36 type dBConfig struct { 37Dialectstring 38Databasestring 39Userstring 40Passwordstring 41Hoststring 42Portint 43Charsetstring 44URLstring 45MaxIdleConns int 46MaxOpenConns int 47ConnMaxLifetime int64 48Sslmodestring 49 } 50 51 // DBConfig 資料庫相關配置 52 var DBConfig dBConfig 53 54 func initDB() { 55utils.SetStructByJSON(&DBConfig, jsonData["database"].(map[string]interface{})) 56/* 57mysql資料庫的連線方式 58url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", 59DBConfig.User, DBConfig.Password, DBConfig.Host, DBConfig.Port, DBConfig.Database, DBConfig.Charset) 60*/ 61/** 62更改mysql資料庫為postgresql 63具體連線方式為> 64host=myhost port=myport user=gorm dbname=gorm password=mypassword 65*/ 66url := fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=%s", 67DBConfig.Host, 68DBConfig.Port, 69DBConfig.User, 70DBConfig.Database, 71DBConfig.Password, 72DBConfig.Sslmode) 73 74DBConfig.URL = url 75 } 76 77 type serverConfig struct { 78APIPoweredBystring 79SiteNamestring 80Hoststring 81ImgHoststring 82Envstring 83LogDirstring 84LogFilestring 85APIPrefixstring 86UploadImgDirstring 87ImgPathstring 88MaxMultipartMemory int 89Portint 90StatsEnabledbool 91TokenSecretstring 92TokenMaxAgeint 93PassSaltstring 94LuosimaoVerifyURLstring 95LuosimaoAPIKeystring 96CrawlerNamestring 97MailUserstring //域名郵箱賬號 98MailPassstring //域名郵箱密碼 99MailHoststring //smtp郵箱域名 100MailPortint//smtp郵箱埠 101MailFromstring //郵件來源 102Githubstring 103BaiduPushLinkstring 104 } 105 106 107 func init() { 108initJSON() 109initDB() 110 }
連線地址一定要根據所使用的orm框架來拼接相應的連線地址才對,這算是一個 坑 ,下面這個是gorm的官方文件以作參考 :
http://doc.gorm.io/database.html#connecting-to-a-database
db.go初始化 :
1 package model 2 3 import ( 4"fmt" 5"os" 6"time" 7 8"github.com/garyburd/redigo/redis" 9"github.com/globalsign/mgo" 10"github.com/jinzhu/gorm" 11_ "github.com/jinzhu/gorm/dialects/postgres" 12"github.com/shen100/golang123/config" 13 ) 14 15 // DB 資料庫連線 16 var DB *gorm.DB 17 var ERR error 18 19 20 func initDB() { 21DB, ERR = gorm.Open(config.DBConfig.Dialect, config.DBConfig.URL) 22if ERR != nil { 23fmt.Println(ERR.Error()) 24os.Exit(-1) 25} 26if config.ServerConfig.Env == DevelopmentMode { 27DB.LogMode(true) 28} 29DB.DB().SetMaxIdleConns(config.DBConfig.MaxIdleConns) 30DB.DB().SetMaxOpenConns(config.DBConfig.MaxOpenConns) 31 32/** 33禁用表名複數> 34!!!如不禁用則會出現表 y結尾邊ies的問題 35!!!如果只是部分表需要使用源表名,請在實體類中宣告TableName的建構函式 36``` 37func (實體名) TableName() string { 38return "資料庫表名" 39} 40``` 41*/ 42DB.SingularTable(true) 43//db.DB().SetConnMaxLifetime(config.DBConfig.ConnMaxLifetime) 44 } 45 46 47 func init() { 48initDB() 49 }
這裡的初始化就是 呼叫 gorm.Open 方法來開啟db的連線 ,連線正常開啟後設置連線池( 空閒連線數、最大連線數 ),到這兒基本就完成了,不過,需要注意到的是: gorm預設的結構體對映是複數形式 ,比如你的部落格表為blog,對應的結構體名就會是blogs,同時若表名為多個單詞, 對應的model結構體名字必須是駝峰式 ,首字母也必須大寫,可能不太理解gorm的命名方式,個人也是被這個邏輯給折騰的不輕,查官方資料才知道需要配置一個引數, 以實現結構體名為非複數形式:DB.SingularTable(true); 預設不設定的時候就是false ;這是一坑。
好了,結構體設定完成就需要在mian.go(啟動類)中引入這兩個檔案所在的package (包);像這樣 :
因為個人在啟動方法中使用到這兩個包的相關方法,所以是正常引入, 若是當前檔案內沒有使用到,請在包的引號前加一個 " _ " ,以表示自動呼叫相關包內的init方法 (因為在main中使用過,故也會自動呼叫包內的init方法)。
db的基本配置已經完成了,啟動main.go若無報錯,則配置成功~
配置完成得測試下,Dao的呼叫,以及在結構體內配置相關對映引數,以及實現主鍵自增(很重要,裡面有 坑 )。
這裡本人用的是本人已經寫完的一個業務來測試,簡要的介紹下gorm的配置引數以及Dao的呼叫方式方法~
通過物件的方式操作資料表時,必須要有個model的結構體和資料庫表結構,這裡我給一個結構體的go程式碼和表結構的截圖 :
結構體:
package model import "time" // Article 文章 type Article struct { IDuint`gorm:"primary_key" sql:"auto_increment;primary_key;unique" json:"id"` CreatedAttime.Time`json:"createdAt"` UpdatedAttime.Time`json:"updatedAt"` DeletedAt*time.Time `sql:"index" json:"deletedAt"` Namestring`json:"name"` BrowseCountuint`json:"browseCount"` CommentCountuint`json:"commentCount"` CollectCountuint`json:"collectCount"` Statusint`json:"status"` Contentstring`json:"content"` HTMLContentstring`json:"htmlContent"` ContentTypeint`json:"contentType"` Categories[]Category `gorm:"many2many:article_category;ForeignKey:ID;AssociationForeignKey:ID" json:"categories"` Comments[]Comment`gorm:"ForeignKey:SourceID" json:"comments"` UserIDuint`json:"userID"` UserUser`json:"user"` LastUserIDuint`json:"lastUserID"` //最後一個回覆話題的人 LastUserUser`json:"lastUser"` LastCommentAt *time.Time `json:"lastCommentAt"` }
資料庫表結構 :
由於postgresql的特殊性,在構建表的時候主鍵ID必須是 serial型別 才會在結構儲存的時候生成一個主鍵自增的觸發器,主鍵在表結構儲存後就是int型別,這是一坑(當然也只有在postgresql中存在),不論用的是oracleDB還是mySqlDB亦或是PostgreSQLDB,實現主鍵自增都需要(至少)設定一個主鍵。
再就是表結構對應的程式碼結構體(Model類或實體類),配置的時候一定要注意,一定要定義欄位引數標籤,標籤就目前用到的一共有三類:
gorm標籤 :gorm構造標籤 ,這裡面可以 定義欄位型別、主鍵、長度、關聯關係等等 ,這個定義一定要有的,若欄位存在多個屬性需要以key:value的形式給出,整個標籤屬性均在英文雙引號內;目前官方給出的標籤型別可以有以下幾種
sql標籤 :很奇怪的是這個標籤在官方gorm裡面並沒有提到,就個人來看這個標籤可能是資料庫driver提供的,就目前用到的就只有以下幾個( 自增、主鍵、唯一 ),若有多個屬性的時候請以分號隔開
sql:"auto_increment;primary_key;unique"
PostgreSQL的使用者需要特別注意的是:若要使用資料庫的主鍵自增,請務必宣告以上幾個屬性,否則資料插入一定會報錯 !這又是一 坑 。。。
J SO N序列化標籤 : 其實,這個標籤跟ORM半毛錢關係也沒有,這裡只是提一下(因為很有用),這個標籤在物件列印或者輸出到請求端的時候 可以將model的欄位以別名的形式輸出 ,若使用預設序列化的方式將欄位輸出則所有的地段都是大寫開頭,所以說十分有用~,在結構體(model)裡大概這麼定義
BrowseCountuint`json:"browseCount"`
現在就嘗試做一個儲存操作,我的程式碼程式碼 :
saveErr = model.DB.Create(&article).Error if saveErr == nil { if userErr := model.DB.Model(&user).Update(map[string]interface{}{ "article_count": user.ArticleCount, "score":user.Score, }).Error; userErr != nil { fmt.Println(userErr.Error()) } }
由於我的DB操作都是定義在db的配置檔案裡面的一個變數
var DB *gorm.DB
所以使用的時候直接看Create方法即可(注意, 儲存物件一定要提前定義,使用指標的方式將物件儲存 )。
儲存成功日誌 :
[2018-11-24 22:02:03][5.87ms]INSERT INTO "article" ("created_at","updated_at","deleted_at","name","browse_count","comment_count","collect_count","status","content","html_content","content_type","user_id","last_user_id","last_comment_at") VALUES ('2018-11-24T22:02:03+08:00','2018-11-24T22:02:03+08:00','<nil>','怎能不說呢','0','0','0','1','欸~','','1','1','0','<nil>') RETURNING "article"."id"
由於go的特性, 所有為空(null)欄位均在記錄操作的時候以<nil>代替 ,介意的話可以將欄位設定一個預設值,或者給表字段新增一個預設值。
在此,gorm的配置已經完成,接下來所有dao的操作均使用gorm提供Delete、Update、Insert、select等方法來實現,具體請參見官方文件(好像有中文版):
雖然,大多數 dao操作都可以通過gorm提供的api來實現 ,但也存在些不便的地方,主要在以下幾點:
>事務 :事務是比較麻煩的一個地方,若確實需要用到事務請在第一個dao操作前呼叫gorm的 Begin()方法,在最後一個dao操作成功後呼叫Commit()方法,若儲存出現異常,需要在每個dao操作後做下判斷,若失敗使用
Rollback()做回退處理,坑。
>級聯查詢 : 雖然官方的gorm提供級聯的方式,但在gorm標籤定義外來鍵型別後並沒任何用,這裡給出的建議(比如一對多)是:在外層查詢完成後迴圈記錄,使用連線欄位查詢出關聯記錄才可, 坑 。
>複雜查詢 :複雜查詢需要手動寫sql( 坑 ),由於gorm並沒有提供任何sql模板(類似於java 的 mybatis),遂,需要在程式碼中手動做動態sql處理,個人建議是用大括號做模板變數,各個例子哈~
var sql = `SELECT b.id,b.cid,b.name,b.browse_count,b.comment_count, b.collect_count,b.created_at,b.created_by,b.updated_at, b.last_comment_at,b.last_comment_by from blog as b, blog_category as bc, blog_top as t WHERE b.cid=bc.id and b.id=t.blog_id and b.status=1 {filterByCid} ORDER BY b.created_at desc , b.updated_at desc {filterLimit}` /* 這裡當分類為所有時>取最近20條部落格記錄 當分類為指定分類時>取指定分類下所有部落格記錄 */ if 0== cId { sql = strings.Replace(sql, "{filterByCid}", "", -1) sql = strings.Replace(sql, "{filterLimit}", "limit 20", -1) }else{ sql = strings.Replace(sql, "{filterByCid}", "and b.cid = "+strconv.Itoa(cId), -1) sql = strings.Replace(sql, "{filterLimit}", "", -1) }
具體的呼叫方式是(一下程式碼中的紅色部分) :
if err := model.DB.Raw(sql).Scan(&blogs).Error; err != nil { SendErrJSON("error", c) return }
>分頁 :gorm提供了Limit和Offset 這兩個方法來配合分頁操作,但,這裡需要說的是,在連表查詢(複雜查詢)下必須手動使用limit offset or rownum來分頁( 坑 ),是不是很原始~
ok,本篇就到這裡就結束了,內容如有疏漏,請參閱以下文件:
gorm文件 :
gin-gonic文件 :
https://github.com/gin-gonic/gin
https://github.com/shen100/golang123
現在是:2018-11-24 23:36:28 ,各位晚安哈~