XORM
xorm 一个简单强大的 Go 语言 ORM 库。通过它让操作数据库变得更加简单。
XORM 关系映射,只是 Go 操作数据库的其中之一。比如还有 GORM。至于两者的区别,一搜一大堆,不再介绍。
这里只介绍一下 XORM 的基本使用。
导入包
go get -u github.com/go-sql-driver/mysql
go get -u github.com/xormplus/xorm
定义结构体
名称映射规则
-
指责
结构体名称 —– 表名
结构体字段 —— 表字段
结构体字段属性 —– 对象的表子段属性
go type’s kind | value method | xorm type |
---|---|---|
implemented Conversion | Conversion.ToDB / Conversion.FromDB | Text |
int, int8, int16, int32, uint, uint8, uint16, uint32 | Int | |
int64, uint64 | BigInt | |
float32 | Float | |
float64 | Double | |
complex64, complex128 | json.Marshal / json.UnMarshal | Varchar(64) |
[]uint8 | Blob | |
array, slice, map except []uint8 | json.Marshal / json.UnMarshal | Text |
string | Varchar(255) | |
time.Time | DateTime | |
cascade struct | primary key field value | BigInt |
struct | json.Marshal / json.UnMarshal | Text |
Others | Text | |
bool | 1 or 0 | Bool |
前缀映射、后缀映射、缓存映射
通过 core.NewPrefixMapper(core.SnakeMapper{}, "prefix") 可以创建一个在 SnakeMapper 的基础上在命名中添加统一的前缀
例如,如果希望所有的表名都在结构体自动命名的基础上加一个前缀而字段名不加前缀,则可以在 engine 创建完成后执行以下语句:
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, "pre_")
engine.SetTableMapper(tbMapper)
执行之后,结构体 type User struct 默认对应的表名就变成了 pre_user 了,而之前默认的是 user
- 通过 core.NewSufffixMapper(core.SnakeMapper{}, "suffix") 可以创建一个在 SnakeMapper 的基础上在命名中添加统一的后缀
- 通过 core.NewCacheMapper(core.SnakeMapper{}) 可以创建一个组合了其它的映射规则,起到在内存中缓存曾经映射过的命名映射
创建 xorm 引擎
在 xorm 里面,可以同时存在多个 Orm 引擎,一个 Orm 引擎称为 Engine,一个 Engine 一般只对应一个数据库
db/db.go
package db
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/xormplus/xorm"
)
var engine *xorm.Engine
func Init() *xorm.Engine {engine, err := xorm.NewEngine("mysql", "root:root@/test?charset=utf8mb4")
if err != nil {panic(err)
}
err = engine.Ping()
if err != nil {fmt.Printf("connect ping failed: %v", err)
}
engine.ShowSQL(true)
return engine
}
写入数据库
先创建两张表,表比较简单,只是做演示使用
CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(100) NOT NULL,
`password` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `address` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`addr` varchar(100) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ORM 方式写入
同一张表的操作
-
写入一条数据
type Users struct {Username string
xorm:"username"
Password stringxorm:"password""
Email stringxorm:"email"
CreatedAt time.Timexorm:"created"
UpdatedAt time.Timexorm:"updated"
DeletedAt time.Timexorm:"deleted"
} func main() {engine := db.Init() u := &Users{} u.Username = "test" u.Password = "123456" u.Email = "test@sina.com" affected, err := engine.Insert(u) if err != nil {log.Fatal(fmt.Printf("insert into failed, err: %v", err)) } fmt.Println(affected) } -
批量写入
- 使用 slice
func main() {engine := db.Init() u := make([]Users, 2) u[0].Username = "test2" u[0].Password = "123456" u[0].Email = "test2@sina.com" u[1].Username = "test3" u[1].Password = "123456" u[1].Email = "test3@sina.com" affected, err := engine.Insert(u) if err != nil {log.Fatal(fmt.Printf("insert into failed, err: %v", err)) } fmt.Println(affected) }
- 使用 slice 指针批量写入
func main() {engine := db.Init() u := make([]*Users, 2) u[0] = &Users{} u[0].Username = "test4" u[0].Password = "123456" u[0].Email = "test4@sina.com" u[1] = &Users{} u[1].Username = "test5" u[1].Password = "123456" u[1].Email = "test5@sina.com" affected, err := engine.Insert(u) if err != nil {log.Fatal(fmt.Printf("insert into failed, err: %v", err)) } fmt.Println(affected) }
多张表的操作
-
写入一条记录
type Users struct {Username string
xorm:"username"
Password stringxorm:"password""
Email stringxorm:"email"
CreatedAt time.Timexorm:"created"
UpdatedAt time.Timexorm:"updated"
DeletedAt time.Timexorm:"deleted"
} type Address struct {Addr stringxorm:"Addr"
CreatedAt time.Timexorm:"created"
UpdatedAt time.Timexorm:"updated"
DeletedAt time.Timexorm:"deleted"
} func main() {engine := db.Init() u := &Users{} u.Username = "test4" u.Password = "123456" u.Email = "test4@sina.com" a := &Address{} a.Addr = "杭州" affected, err := engine.Insert(u, a) if err != nil {log.Fatal(fmt.Printf("insert into failed, err: %v", err)) } fmt.Println(affected) } -
批量写入
func main() {engine := db.Init() u := make([]*Users, 2) u[0] = &Users{} u[0].Username = "test6" u[0].Password = "123456" u[0].Email = "test4@sina.com" u[1] = &Users{} u[1].Username = "test7" u[1].Password = "123456" u[1].Email = "test5@sina.com" a := make([]*Address, 2) a[0] = &Address{} a[0].Addr = "杭州" a[1] = &Address{} a[1].Addr = "上海" affected, err := engine.Insert(u, a) if err != nil {log.Fatal(fmt.Printf("insert into failed, err: %v", err)) } fmt.Println(affected) }
Note: 这里的多表写入并没有使用事务。如果部分成功,部分失败不支持回滚
执行原生 sql 写入数据
func main() {engine := db.Init()
sql := "INSERT INTO users(username, password, email) values (?, ?, ?)"
res, err := engine.Exec(sql, "original", "123", "123@sina.com")
if err != nil {log.Fatal(fmt.Printf("insert into failed, err: %v", err))
}
fmt.Println(res)
}
Note: 执行原生 sql 写入数据,created_at、updated_at 时间的值不写的则为空。
原生 sql 写入还有其余三种方式,这里不再赘述
删除数据
ORM 方式删除 – 软删除
在 Delete() 时,deleted 标记的字段将会被自动更新为当前时间而不是去删除该条记录
func main() {engine := db.Init()
var u = &Users{}
u.Username = "test1"
affected, err := engine.Delete(u)
if err != nil {log.Fatal(fmt.Printf("deleter into failed, err: %v", err))
}
fmt.Println(affected)
}
执行原生 sql 删除 – 物理删除
func main() {engine := db.Init()
sql := "DELETE FROM users where id = ?"
affected, err := engine.Exec(sql, 1)
if err != nil {log.Fatal(fmt.Printf("deleter into failed, err: %v", err))
}
fmt.Println(affected)
}
Note: 也可以使用原生 sql update 更新 deleted_at 时间为当前时间戳,实现软删除。
更新数据
-
update 方式
更新数据使用 engine.Update 方法,update 的参数可以是一个结构体指针或者一个 Map[string]interface{} 类型。
- 当传入的为结构体指针时,只有非空和 0 的 field 才会被作为更新的字段。如果非要更新空字段,需要使用 Cols 方法显示指定更新的列
- 当传入的为 Map 类型时,key 为数据库 Column 的名字,value 为要更新的内容。且需要使用 engine.Table 方法指定表名
func main() {engine := db.Init()
engine.ID(13).Update(&Users{Username: "test"})
engine.ID(13).Cols("username", "email").Update(&Users{Username: "test2"}) // 会更新 username 和 email 两个子段,email 为空
// map 类型
affected, err := engine.Table(&Users{}).ID(13).Update(map[string]interface{}{"username": "update_original",})
if err != nil {log.Fatal(fmt.Printf("update username failed, err: %v", err))
}
fmt.Println(affected)
}
-
执行原生 sql 更新数据
func main() {engine := db.Init() sql := "UPDATE users SET username = ?, updated_at = ? WHERE id = ?" res, err := engine.Exec(sql, "aaa", time.Now().Format("2006-01-02 15:04:05"), 13) if err != nil {log.Fatal(fmt.Printf("update username failed, err: %v", err)) } fmt.Println(res) }
查询数据
ORM
-
查询一条数据 – GET 方法
func main() {engine := db.Init() // SELECT * FROM user LIMIT 1 user1 := &Users{} has, _ := engine.ID(1).Get(user1) if has {fmt.Printf("user1:%v\n", user1) } // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 user2 := &Users{} has, _ = engine.Where("username = ?", "aaa").Desc("id").Get(user2) if has {fmt.Printf("user1:%v\n", user1) } }
-
查询多条数据 – Find 方法
Find()
需要传入对象切片的指针或 map 的指针
func main() {engine := db.Init()
slicUsers := make([]Users, 0)
_ = engine.Find(&slicUsers)
fmt.Println(slicUsers)
mapUsers := make([]Users, 0)
engine.Where("username = ?", "aaa").Find(&mapUsers)
fmt.Println(mapUsers)
}
-
Iterate 效果与 Find 方法一样,对了一个回调函数处理每条记录
func main() {engine := db.Init() engine.Where("username = ?", "aaa").Iterate(new(Users), func(i int, bean interface{}) error {users := bean.(*Users) fmt.Println(users) return nil }) }
-
Count 统计满足条件的数量,参数为 struct 指针
func main() {engine := db.Init() count, _ := engine.Where("length(username) > ?", 3).Count(&Users{}) fmt.Println(count) }
-
Rows 方法 和 Iterate 方法类似。
func main() {engine := db.Init() u := &Users{} rows, _ := engine.Where("id> ?", 5).Rows(u) defer rows.Close() for rows.Next() {rows.Scan(u) fmt.Println(u) } }
原生 sql 查询
写 sql 语句,然后执行即可。和更新删除类似,不再赘述。