轉載請註明出處: http://www.javashuo.com/article/p-sbygisjx-e.htmlhtml
```java
我先閒扯下,前天(也就是2018年11月16號)的某個時候,突然有人在QQ上私聊我,一看是公司羣覺得是有人來慰問新人了,也沒弄清楚身份就調侃起來,就這樣:mysql
問題是:我竟傻乎乎滴沒看出來是行政那邊的人,中午吃飯的時候和老同事聊起此事,才知道這位大鍋是人事部boss,一時間感受事情變得搞笑起來,固然,有意思的還不止這一件,就在兩週前入職的時候,當時是複試,行政總監把車開到我以前公司樓下接我,出發到現場前給我買了杯咖啡,我說美式中杯就行了,這人說怎麼也得大杯,面試過了後,到晚上,這人又發朋友圈說他興奮的狠。。。git
說實話,二當家也真夠zuo的。。。😅,固然這夥計在我第一面的時候就閒聊了一個多小時,還不止,他居然知道我小名😓github
```golang
閒聊到這兒,如今就進入本次的主題:golang web開發之Dao配置web
在正式進入主題前,先說說框架的現狀,我的用的是gin-gonic框架,這是個在校大學生寫的基於go語言的高性能web框架,在此以前我對比過beego 、 iris 、gin-gonic這幾個在維護頻度和依賴支持以及star熱度方面,我的選擇了gin-gonic這個框架 ,同時也在github上選用了一套比較前衛的成型的框架代碼,東西十分的好,可是我的以爲框架集成的mysql實在是看不下去(主要是性能低了+ 穩定性不夠好+升級麻煩),遂就將數據庫換成postgresql,配置完成就開始測試Dao,須要說的是其中gorm是位臺灣胸弟寫的ORM框架,因而開始~面試
且先無論現有的mysql的配置,因爲框架自己只集成了mysql,因此如今須要安裝一個pg的鏈接driver,放到指定的目錄就裝好依賴了,至於怎麼安裝,大體有二。redis
A>其一是使用go命令直接安裝:sql
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() { 18 bytes, err := ioutil.ReadFile("./config.json") 19 if err != nil { 20 fmt.Println("ReadFile: ", err.Error()) 21 os.Exit(-1) 22 } 23 24 configStr := string(bytes[:]) 25 reg := regexp.MustCompile(`/\*.*\*/`) 26 27 configStr = reg.ReplaceAllString(configStr, "") 28 bytes = []byte(configStr) 29 30 if err := json.Unmarshal(bytes, &jsonData); err != nil { 31 fmt.Println("invalid config: ", err.Error()) 32 os.Exit(-1) 33 } 34 } 35 36 type dBConfig struct { 37 Dialect string 38 Database string 39 User string 40 Password string 41 Host string 42 Port int 43 Charset string 44 URL string 45 MaxIdleConns int 46 MaxOpenConns int 47 ConnMaxLifetime int64 48 Sslmode string 49 } 50 51 // DBConfig 數據庫相關配置 52 var DBConfig dBConfig 53 54 func initDB() { 55 utils.SetStructByJSON(&DBConfig, jsonData["database"].(map[string]interface{})) 56 /* 57 mysql數據庫的鏈接方式 58 url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local", 59 DBConfig.User, DBConfig.Password, DBConfig.Host, DBConfig.Port, DBConfig.Database, DBConfig.Charset) 60 */ 61 /** 62 更改mysql數據庫爲postgresql 63 具體鏈接方式爲> 64 host=myhost port=myport user=gorm dbname=gorm password=mypassword 65 */ 66 url := fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=%s", 67 DBConfig.Host, 68 DBConfig.Port, 69 DBConfig.User, 70 DBConfig.Database, 71 DBConfig.Password, 72 DBConfig.Sslmode) 73 74 DBConfig.URL = url 75 } 76 77 type serverConfig struct { 78 APIPoweredBy string 79 SiteName string 80 Host string 81 ImgHost string 82 Env string 83 LogDir string 84 LogFile string 85 APIPrefix string 86 UploadImgDir string 87 ImgPath string 88 MaxMultipartMemory int 89 Port int 90 StatsEnabled bool 91 TokenSecret string 92 TokenMaxAge int 93 PassSalt string 94 LuosimaoVerifyURL string 95 LuosimaoAPIKey string 96 CrawlerName string 97 MailUser string //域名郵箱帳號 98 MailPass string //域名郵箱密碼 99 MailHost string //smtp郵箱域名 100 MailPort int //smtp郵箱端口 101 MailFrom string //郵件來源 102 Github string 103 BaiduPushLink string 104 } 105 106 107 func init() { 108 initJSON() 109 initDB() 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() { 21 DB, ERR = gorm.Open(config.DBConfig.Dialect, config.DBConfig.URL) 22 if ERR != nil { 23 fmt.Println(ERR.Error()) 24 os.Exit(-1) 25 } 26 if config.ServerConfig.Env == DevelopmentMode { 27 DB.LogMode(true) 28 } 29 DB.DB().SetMaxIdleConns(config.DBConfig.MaxIdleConns) 30 DB.DB().SetMaxOpenConns(config.DBConfig.MaxOpenConns) 31 32 /** 33 禁用表名複數> 34 !!!如不由用則會出現表 y結尾邊ies的問題 35 !!!若是隻是部分表須要使用源表名,請在實體類中聲明TableName的構造函數 36 ``` 37 func (實體名) TableName() string { 38 return "數據庫表名" 39 } 40 ``` 41 */ 42 DB.SingularTable(true) 43 //db.DB().SetConnMaxLifetime(config.DBConfig.ConnMaxLifetime) 44 } 45 46 47 func init() { 48 initDB()
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 { ID uint `gorm:"primary_key" sql:"auto_increment;primary_key;unique" json:"id"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` DeletedAt *time.Time `sql:"index" json:"deletedAt"` Name string `json:"name"` BrowseCount uint `json:"browseCount"` CommentCount uint `json:"commentCount"` CollectCount uint `json:"collectCount"` Status int `json:"status"` Content string `json:"content"` HTMLContent string `json:"htmlContent"` ContentType int `json:"contentType"` Categories []Category `gorm:"many2many:article_category;ForeignKey:ID;AssociationForeignKey:ID" json:"categories"` Comments []Comment `gorm:"ForeignKey:SourceID" json:"comments"` UserID uint `json:"userID"` User User `json:"user"` LastUserID uint `json:"lastUserID"` //最後一個回覆話題的人 LastUser User `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的用戶須要特別注意的是:若要使用數據庫的主鍵自增,請務必聲明以上幾個屬性,不然數據插入必定會報錯!這又是一坑。。。
JSON序列化標籤: 其實,這個標籤跟ORM半毛錢關係也沒有,這裏只是提一下(由於頗有用),這個標籤在對象打印或者輸出到請求端的時候能夠將model的字段以別名的形式輸出,若使用默認序列化的方式將字段輸出則全部的地段都是大寫開頭,因此說十分有用~,在結構體(model)裏大概這麼定義
BrowseCount uint `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 ,各位晚安哈~