## 一. 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",
}
```