## 一. talk is cheap, show me your code
sql
```
DROP TABLE IF EXISTS "users";
CREATE TABLE "users" (
"uid" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"age" integer NOT NULL
);
INSERT INTO "users" VALUES (1, 'gorose', 18);
INSERT INTO "users" VALUES (2, 'goroom', 18);
INSERT INTO "users" VALUES (3, 'fizzday', 18);
```
### 1. 查詢操作
原生sql查詢
```go
package main
import (
"fmt"
"github.com/gohouse/gorose/v2"
_ "github.com/mattn/go-sqlite3"
)
var err error
var engin *gorose.Engin
func init() {
// 全局初始化數據庫,并復用
// 這里的engin需要全局保存,可以用全局變量,也可以用單例
// 配置&gorose.Config{}是單一數據庫配置
// 如果配置讀寫分離集群,則使用&gorose.ConfigCluster{}
// mysql Dsn示例 "root:root@tcp(localhost:3306)/test?charset=utf8&parseTime=true"
engin, err = gorose.Open(&gorose.Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
}
func DB() gorose.IOrm {
return engin.NewOrm()
}
func main() {
// 這里定義一個變量db, 是為了復用db對象, 可以在最后使用 db.LastSql() 獲取最后執行的sql
// 如果不復用 db, 而是直接使用 DB(), 則會新建一個orm對象, 每一次都是全新的對象
// 所以復用 db, 一定要在當前會話周期內
db := DB()
res,err := db.Query("select * from users limit 2")
fmt.Println(res,err)
}
```
結果集綁定到`map`
```go
type user gorose.Data
func (u *user) TableName() string {
return "users"
}
func main() {
// 查詢一條
// 這里的對象是map, 所以需要初始化(var u = user{}), 不能像struct那樣, 可以直接 `var u Users`
var u = user{}
// 查詢數據并綁定到 user{} 上
err = db.Table(&u).Fields("uid,name,age").Where("age",">",0).OrderBy("uid desc").Select()
if err!=nil {
fmt.Println(err)
}
fmt.Println(u, u["name"])
fmt.Println(db.LastSql())
// 查詢多條
// 查詢數據并綁定到 []user{} 上, 這里復用了 db 及上下文條件參數
// 如果不想復用,則可以使用DB()就會開啟全新會話,或者使用db.Reset()
// db.Reset()只會清除上下文參數干擾,不會更換鏈接,DB()則會更換鏈接
var u2 = make([]user,0)
err = db.Limit(10).Offset(1).Select()
fmt.Println(u2)
// 統計數據
var count int64
// 這里reset清除上邊查詢的參數干擾, 可以統計所有數據, 如果不清楚, 則條件為上邊查詢的條件
// 同時, 可以新調用 DB(), 也不會產生干擾
count,err = db.Reset().Count()
// 或
count, err = DB().Table(&u).Count()
fmt.Println(count, err)
}
```
綁定到struct
```
type Users struct {
Uid int64 `gorose:"uid"`
Name string `gorose:"name"`
Age int64 `gorose:"age"`
}
var u Users
err := DB().Table(&u).Select()
```
如果不知道數據庫字段類型對應golang的類型,可以參考文末的"附錄"
> 如果覺得手動定義struct太麻煩, 可以使用 [https://github.com/gohouse/converter](https://github.com/gohouse/converter) 這個包,一鍵生成遷移struct
table傳入string
```
res,err := DB().Table("users").First()
fmt.Println(res["Name"])
res2,err := DB().Table("users").Limit(5).Get()
fmt.Println(res2)
```
> 注意: 當傳入的table是string時,需要調用`Get()`或`First()`才能取到數據, 同時需要多接收一個參數`res`存放查詢結果,其中`First()`返回一條數據`map[string]interface{}`,`Get()`返回多條數據`[]map[string]interface{}`, 快捷類型為 `gorose.Data`.
使用`Get()`或`First()`方法, 一定要接收返回的第一個參數為結果,使用`Select()`則為綁定結果,為了減少內存占用,只要使用了`Get()`或`First()`,即使傳入的是`struct`綁定對象,也不會有數據,只能通過返回的第一個參數接收結果集
需要說明的東西,基本都在注釋里了
### 2.增刪改
```
DB().Table(&user).Data(gorose.Data{"name":"fizzday"}).Insert()
// 如果傳入的是struct, 則可以不用指定Table(), orm會根據傳入的struct,解析出table. update()操作類似
DB().Insert(&Users{Name:"fizz",Age:18})
DB().Table(&user).Where("uid",1).Delete()
```
### 3.事務操作
一鍵事務, 自動回滾和提交, 我們只需要關注業務即可
```
db:= DB()
err := db.Transaction(
// 第一個業務
func(db IOrm) error {
_,err := db.Where("uid",1).Update(&Data{"name":"gorose2"})
if err!=nil {
return err
}
_,err = db.Insert(&Data{"name":"gorose2"})
if err!=nil {
return err
}
return nil
},
// 第二個業務
func(db IOrm) error {
_,err := db.Where("uid",1).Delete()
if err!=nil {
return err
}
_,err = db.Insert(&Data{"name":"gorose2"})
if err!=nil {
return err
}
return nil
})
```
手動事務
```
db:= DB()
db.Begin()
err :=myfunc()
if err!=nil {
db.Rollback()
}
db.Commit()
```
## 二. 廢話一下使用技巧
1. 全局使用engin的示例
使用全局變量
```
var Engin *gorose.Engin
Engin = gorose.Open("xxxxx")
```
或使用單例
```
var once sync.Once
var engin *gorose.Engin
func NewGorose() *gorose.Engin {
once.Do(func(){
engin = gorose.Open("xxxxx")
})
return engin
}
```
2. 不復用會話時可以有兩種用法
直接使用全局engin
```
err := NewGorose().NewOrm().Table(&user).Select()
```
或封裝一個`DB()`函數
```
func DB() gorose.IOrm {
return engin.NewOrm()
}
err := DB().Table(&user).Select()
```
3. 復用會話參見代碼示例和對應注釋, 另外, 事務一定要復用會話,確保用的是同一個鏈接. 當然不需要自己手動`db.Reset()`,系統會在檢測到事務時,自動`Reset()`, 所以使用事務, 每一次都需要傳入全部的上下文參數
4. 關于綁定對象: 如果傳入的slice, 如: []struct, []map這種結構, 則會取多條數據. 如果傳入不是slice, 則只會取一條數據, 因為取多條毫無意義, 只會是后一條覆蓋前一條, 最終返回的還是最后一條
## 附錄
```go
// key為mysql的字段類型, value為golang的數據類型
var typeForMysqlToGo = map[string]string{
"int": "int",
"integer": "int",
"tinyint": "int",
"smallint": "int",
"mediumint": "int",
"bigint": "int64",
"int unsigned": "int",
"integer unsigned": "int",
"tinyint unsigned": "int",
"smallint unsigned": "int",
"mediumint unsigned": "int",
"bigint unsigned": "int64",
"bit": "int",
"bool": "bool",
"enum": "string",
"set": "string",
"varchar": "string",
"char": "string",
"tinytext": "string",
"mediumtext": "string",
"text": "string",
"longtext": "string",
"blob": "string",
"tinyblob": "string",
"mediumblob": "string",
"longblob": "string",
"date": "time.Time", // time.Time or string
"datetime": "time.Time", // time.Time or string
"timestamp": "time.Time", // time.Time or string
"time": "time.Time", // time.Time or string
"float": "float64",
"double": "float64",
"decimal": "float64",
"binary": "string",
"varbinary": "string",
}
```