今天这篇把我使用GoFrame ORM
的过程中认为有价值、可能踩坑、比较好的实践等相关的知识点分享出来。
值类型和指针类型
初学go的时候一直在纠结值类型和指针类型,后来在用的过程中发现大可不必纠结。
指针类型的优势就是节省内存空间,多个变量指向同一个内存地址,一荣俱荣,一个修改,处处修改。
而值类型可以简单理解为:避免这种一处修改处处修改的问题,有些场景下我们不能单纯的考虑性能,不能单纯地只考虑复用
。
在GoFrame中也鼓励我们使用指针类型,举个栗子:
使用Scan
将查询结果转换成struct
对象
Scan
支持将查询结果转换为一个struct
对象,查询结果应当是特定的一条记录,并且pointer
参数应当为struct
对象的指针地址(*struct
或者**struct
),使用方式例如:
type User struct { Id int Passport string Password string NickName string CreateTime *gtime.Time } user := User{} g.Model("user").Where("id", 1).Scan(&user)
或者
var user = User{} g.Model("user").Where("id", 1).Scan(&user)
前两种方式都是预先初始化对象(提前分配内存)
推荐的方式如下,这种方式只有在查询到数据的时候才会执行初始化及内存分配:
var user *User g.Model("user").Where("id", 1).Scan(&user)
注意在用法上的区别,特别是传递参数类型的差别(前两种方式传递的参数类型是*User
,这里传递的参数类型其实是**User
)。
插入数据
InsertIgnore
这个函数需要重点和大家说一下,因为我之前有Laravel的使用经验,Laravel中的InsertIgnore
是配合唯一索引使用,意思是如果存在唯一主键或者唯一索引就忽略掉,不重复插入。
在GoFrame中是相反的:
GoFrame中默认的insert
函数,就会根据唯一索引(或主键)执行,如果有存在的数据返回失败,不重复插入。
而GoFrame中的InsertIgnore是忽略错误继续插入,而不是存在就不插入。
大家在使用中要注意。
灵活执行SQL
我们要拼接自定义的sql参数怎么办法呢?
gdb.Raw()
示例如下:
OnDuplicate(g.Map{ "nickname": gdb.Raw("CONCAT('name_', VALUES(`nickname`))"), })
gdb.Raw()
中的参数将作为sql执行的语句,不会自动转换成字符串类型,也不会作为预处理参数。
示例如下:
// INSERT INTO `user`(`id`,`passport`,`password`,`nickname`,`create_time`) VALUES(id+2,'80','123456','wzy',now()) g.Model("user").Data(g.Map{ "id": gdb.Raw("id+2"), "passport": "80", "password": "123456", "nickname": "wzy", "create_time": gdb.Raw("now()"), }).Insert()
链式安全
我们首先要有这个概念:什么是链式安全?
其实很简单:链式安全就是操作model模型时不会改变当前model对象,模型属性修改/条件叠加需要使用赋值来操作。
而链式非安全
或者非链式安全
就是:操作model模型时会改变当前model对象的属性。
在GoFrame开发过程中,强烈建议使用gf gen dao
生成的model和dao文件!!!
通过gf gen dao
生成的model和dao文件默认是链式安全的。
举例如下:
func NewGomeStockDao() *GomeStockDao { return &GomeStockDao{ M: g.DB("default").Model("gome_stock").Safe(), //注意看这里 DB: g.DB("default"), Table: "gome_stock", Columns: gomeStockColumns{ Id: "id", MchWhseCode: "mch_whse_code", MchWhseName: "mch_whse_name", IsNationWide: "is_nation_wide", Status: "status", LastUpateTime: "last_upate_time", }, } }
链式安全时我们如何叠加条件改变呢?
答案就是:通过赋值。
链式安全时通过 m = m.xxx 方式赋值实现链式调用
举例如下:
m := user.Where("status IN(?)", g.Slice{1,2,3}) if vip { // 查询条件通过赋值叠加 m = m.And("money>=?", 1000000) } else { // 查询条件通过赋值叠加 m = m.And("money<?", 1000000) } // vip: SELECT * FROM user WHERE status IN(1,2,3) AND money >= 1000000 // !vip: SELECT * FROM user WHERE status IN(1,2,3) AND money < 1000000 r, err := m.All() // vip: SELECT COUNT(1) FROM user WHERE status IN(1,2,3) AND money >= 1000000 // !vip: SELECT COUNT(1) FROM user WHERE status IN(1,2,3) AND money < 1000000 n, err := m.Count()