以前我們學習過原生的Go鏈接MYSQL的方法,使用Go自帶的"database/sql"
數據庫鏈接api,"github.com/go-sql-driver/mysql"
MYSQL驅動,經過比較原生的寫法去寫sql和處理事務。目前開源界也有不少封裝好的orm操做框架,幫咱們簡省一些重複的操做,提升代碼可讀性。gorm
就是這樣的一款做品,咱們來學習一下gorm的操做流程。mysql
go get -u github.com/jinzhu/gorm
要鏈接到數據庫首先要導入驅動程序。例如git
import _ "github.com/go-sql-driver/mysql"
爲了方便記住導入路徑,GORM包裝了一些驅動。github
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"
因此包名能夠改成如上:web
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
做爲參數。sql
在Gorm中,表名是結構體名的複數形式,列名是字段名的蛇形小寫。數據庫
即,若是有一個user表,那麼若是你定義的結構體名爲:User,gorm會默認表名爲users而不是user。api
例若有以下表結構定義:數組
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 }
如何全局禁用表名複數呢?tcp
能夠在建立數據庫鏈接的時候設置以下參數:
// 全局禁用表名複數 db.SingularTable(true) // 若是設置爲true,`User`的默認表名爲`user`,使用`TableName`設置的表名不受影響
這樣的話,表名默認即爲結構體的首字母小寫形式。
下面咱們使用一張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這個對象來接收。
上面的查詢都將返回表中全部的字段,若是你想指定查詢某些字段該怎麼作呢?
//指定查詢字段 db.Select("name,age").Where(map[string]interface{}{"age":12,"sex":1}).Find(&u)
//使用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)
//where name not in ("xiaoming","xiaohong") db.Not("name","xiaoming","xiaohong").Find(&u) //同上 db.Not("name",[]string{"xiaoming","xiaohong"}).Find(&u)
//where age > 12 or sex = 1 db.Where("age > ?",12).Or("sex = ?",1).Find(&u)
//order by age desc db.Where("age > ?",12).Or("sex = ?",1).Order("age desc").Find(&u)
//limit 10 db.Not("name",[]string{"xiaoming","xiaohong"}).Limit(10).Find(&u)
//limit 300,10 db.Not("name",[]string{"xiaoming","xiaohong"}).Limit(10).Offset(300).Find(&u)
//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不相符。
rows, _ := db.Table("user").Select("count(*),sex").Group("sex"). Having("age > ?", 10).Rows() for rows.Next() { fmt.Print(rows.Columns()) }
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{})