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