Go orm框架gorm學習


之前咱們學習過原生的Go連接MYSQL的方法,使用Go自帶的"database/sql"數據庫連接api,"github.com/go-sql-driver/mysql"MYSQL驅動,通過比較原生的寫法去寫sql和處理事務。目前開源界也有很多封裝好的orm操作框架,幫我們簡省一些重復的操作,提高代碼可讀性。gorm就是這樣的一款作品,我們來學習一下gorm的操作流程。

安裝#

go get -u github.com/jinzhu/gorm 

數據庫連接#

要連接到數據庫首先要導入驅動程序。例如

import _ "github.com/go-sql-driver/mysql" 

為了方便記住導入路徑,GORM包裝了一些驅動。

import _ "github.com/jinzhu/gorm/dialects/mysql" // import _ "github.com/jinzhu/gorm/dialects/postgres" // import _ "github.com/jinzhu/gorm/dialects/sqlite" // import _ "github.com/jinzhu/gorm/dialects/mssql" 

所以包名可以改為如上:

import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) func main() { db, err := gorm.Open("mysql", "user:password@tcp(IP:port)/dbname?charset=utf8&parseTime=True&loc=Local") db.DB().SetMaxIdleConns(10) db.DB().SetMaxOpenConns(100) defer db.Close() } 

注:為了處理time.Time,你需要包括parseTime作為參數。

數據模型定義#

表名,列名如何對應結構體

在Gorm中,表名是結構體名的復數形式,列名是字段名的蛇形小寫。

即,如果有一個user表,那么如果你定義的結構體名為:User,gorm會默認表名為users而不是user。

例如有如下表結構定義:

CREATE TABLE `areas` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id', `area_id` int(11) NOT NULL COMMENT '區縣id', `area_name` varchar(45) NOT NULL COMMENT '區縣名', `city_id` int(11) NOT NULL COMMENT '城市id', `city_name` varchar(45) NOT NULL COMMENT '城市名稱', `province_id` int(11) NOT NULL COMMENT '省份id', `province_name` varchar(45) NOT NULL COMMENT '省份名稱', `area_status` tinyint(3) NOT NULL DEFAULT '1' COMMENT '該條區域信息是否可用 : 1:可用 2:不可用', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間', `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='區域表' 

那么對應的結構體定義如下:

type Area struct { Id int AreaId int AreaName string CityId int CityName string ProvinceId int ProvinceName string AreaStatus int CreatedAt time.Time UpdatedAt time.Time } 

如何全局禁用表名復數呢?

可以在創建數據庫連接的時候設置如下參數:

// 全局禁用表名復數 db.SingularTable(true) // 如果設置為true,`User`的默認表名為`user`,使用`TableName`設置的表名不受影響 

這樣的話,表名默認即為結構體的首字母小寫形式。

CRUD 使用#

下面我們使用一張User表來就CRUD做一些操作示例:

表結構如下:

CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(30) NOT NULL DEFAULT '', `age` int(3) NOT NULL DEFAULT '0', `sex` tinyint(3) NOT NULL DEFAULT '0', `phone` varchar(40) NOT NULL DEFAULT '', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 

首先初始化數據庫連接:

package main import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) var db *gorm.DB type User struct { Id int Name string Age int Sex byte Phone string } func init() { var err error db, err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local") if err != nil { panic(err) } //設置全局表名禁用復數 db.SingularTable(true) } 

下面所有的操作都是在上面的初始化連接上執行的操作。

插入
//插入數據 func (user *User) Insert() { //這里使用了Table()函數,如果你沒有指定全局表名禁用復數,或者是表名跟結構體名不一樣的時候 //你可以自己在sql中指定表名。這里是示例,本例中這個函數可以去除。 db.Table("user").Create(user) } 
更新
//注意,Model方法必須要和Update方法一起使用 //使用效果相當於Model中設置更新的主鍵key(如果沒有where指定,那么默認更新的key為id),Update中設置更新的值 //如果Model中沒有指定id值,且也沒有指定where條件,那么將更新全表 //相當於:update user set name='xiaoming' where id=1; user := User{Id: 1,Name:"xiaoming"} db.Model(&user).Update(user) //注意到上面Update中使用了一個Struct,你也可以使用map對象。 //需要注意的是:使用Struct的時候,只會更新Struct中這些非空的字段。 //對於string類型字段的"",int類型字段0,bool類型字段的false都被認為是空白值,不會去更新表 //下面這個更新操作只使用了where條件沒有在Model中指定id //update user set name='xiaohong' wehre sex=1 db.Model(&User{}).Where("sex = ?",1).Update("name","xiaohong") 

如果你想手動將某個字段set為空值, 可以使用單獨選定某些字段的方式來更新:

user := User{Id: 1} db.Model(&user).Select("name").Update(map[string]interface{}{"name":"","age":0}) 

忽略掉某些字段:

當你的更新的參數為結構體,而結構體中某些字段你又不想去更新,那么可以使用Omit方法過濾掉這些不想update到庫的字段:

user := User{Id: 1,Name:"xioaming",Age:12} db.Model(&user).Omit("name").Update(&user) 
刪除
//delete from user where id=1; user := User{Id: 1} db.Delete(&user) //delete from user where id > 11; db.Delete(&User{},"id > ?",11) 
事務
func CreateAnimals(db *gorm.DB) err { tx := db.Begin() // 注意,一旦你在一個事務中,使用tx作為數據庫句柄 if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil { tx.Rollback() return err } if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil { tx.Rollback() return err } tx.Commit() return nil } 
查詢:
func (user *User) query() (u []User) { //查詢所有記錄 db.Find(&u) //Find方法可以帶 where 參數 db.Find(&u,"id > ? and age > ?",2,12) //帶where 子句的查詢,注意where要在find前面 db.Where("id > ?", 2).Find(&u) // where name in ("xiaoming","xiaohong") db.Where("name in (?)",[]string{"xiaoming","xiaohong"}).Find(&u) //獲取第一條記錄,按照主鍵順序排序 db.First(&u) //First方法可以帶where 條件 db.First(&u,"where sex = ?",1) //獲取最后一條記錄,按照主鍵順序排序 //同樣 last方法也可以帶where條件 db.Last(&u) return u } 

注意:方法中帶的&u表示是返回值用u這個對象來接收。

上面的查詢都將返回表中所有的字段,如果你想指定查詢某些字段該怎么做呢?

指定查詢字段-Select
//指定查詢字段 db.Select("name,age").Where(map[string]interface{}{"age":12,"sex":1}).Find(&u) 
使用Struct和map作為查詢條件
//使用Struct,相當於:select * from user where age =12 and sex=1 db.Where(&User{Age:12,Sex:1}).Find(&u) //等同上一句 db.Where(map[string]interface{}{"age":12,"sex":1}).Find(&u) 
not 條件的使用
//where name not in ("xiaoming","xiaohong") db.Not("name","xiaoming","xiaohong").Find(&u) //同上 db.Not("name",[]string{"xiaoming","xiaohong"}).Find(&u) 
or 的使用
//where age > 12 or sex = 1 db.Where("age > ?",12).Or("sex = ?",1).Find(&u) 
order by 的使用
//order by age desc db.Where("age > ?",12).Or("sex = ?",1).Order("age desc").Find(&u) 
limit 的使用
//limit 10 db.Not("name",[]string{"xiaoming","xiaohong"}).Limit(10).Find(&u) 
offset 的使用
//limit 300,10 db.Not("name",[]string{"xiaoming","xiaohong"}).Limit(10).Offset(300).Find(&u) 
count(*)
//count(*) var count int db.Table("user").Where("age > ?",0).Count(&count) 

注意:這里你在指定表名的情況下sql為:select count(*) from user where age > 0;

如上代碼如果改為:

var count int var user []User db.Where("age > ?",0).Find(&user).Count(&count) 

相當於你先查出來[]User,然后統計這個list的長度。跟你預期的sql不相符。

group & having
rows, _ := db.Table("user").Select("count(*),sex").Group("sex"). Having("age > ?", 10).Rows() for rows.Next() { fmt.Print(rows.Columns()) } 
join
db.Table("user u").Select("u.name,u.age").Joins("left join user_ext ue on u.user_id = ue.user_id").Row() 

如果有多個連接,用多個Join方法即可。

原生函數
db.Exec("DROP TABLE user;") db.Exec("UPDATE user SET name=? WHERE id IN (?)", "xiaoming", []int{11,22,33}) db.Exec("select * from user where id > ?",10).Scan(&user) 
一些函數

FirstOrInit 和 FirstOrCreate

獲取第一個匹配的記錄,若沒有,則根據條件初始化一個新的記錄:

//注意:where條件只能使用Struct或者map。如果這條記錄不存在,那么會新增一條name=xiaoming的記錄 db.FirstOrInit(&u,User{Name:"xiaoming"}) //同上 db.FirstOrCreate(&u,User{Name:"xiaoming"}) 

Attrs

如果沒有找到記錄,則使用Attrs中的數據來初始化一條記錄:

//使用attrs來初始化參數,如果未找到數據則使用attrs中的數據來初始化一條 //注意:attrs 必須 要和FirstOrInit 或者 FirstOrCreate 連用 db.Where(User{Name:"xiaoming"}).Attrs(User{Name:"xiaoming",Age:12}).FirstOrInit(&u) 

Assign

//不管是否找的到,最終返回結構中都將帶上Assign指定的參數 db.Where("age > 12").Assign(User{Name:"xiaoming"}).FirstOrInit(&u) 

Pluck

如果user表中你只想查詢age這一列,該怎么返回呢,gorm提供了Pluck函數用於查詢單列,返回數組:

var ages []int db.Find(&u).Pluck("age",&ages) 

Scan

Scan函數可以將結果轉存儲到另一個結構體中。

type SubUser struct{ Name string Age int } db.Table("user").Select("name,age").Scan(&SubUser) 

sql.Row & sql.Rows

row和rows用戶獲取查詢結果。

//查詢一行 row := db.Table("user").Where("name = ?", "xiaoming").Select("name, age").Row() // (*sql.Row) //獲取一行的結果后,調用Scan方法來將返回結果賦值給對象或者結構體 row.Scan(&name, &age) //查詢多行 rows, err := db.Model(&User{}).Where("sex = ?",1).Select("name, age, phone").Rows() // (*sql.Rows, error) defer rows.Close() for rows.Next() { ... rows.Scan(&name, &age, &email) ... } 

日志#

Gorm有內置的日志記錄器支持,默認情況下,它會打印發生的錯誤。

// 啟用Logger,顯示詳細日志 db.LogMode(true) // 禁用日志記錄器,不顯示任何日志 db.LogMode(false) // 調試單個操作,顯示此操作的詳細日志 db.Debug().Where("name = ?", "xiaoming").First(&User{})


免責聲明!

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



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