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