Go orm框架gorm學習

以前我們學習過原生的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`設置的表名不受影響

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

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{})
相關文章
相關標籤/搜索