GORM vs XORM
本来我是用GORM来着,原因是它在GitHub上的Star是最多的,随大流总是不会错的,
github.com/search?q=or…
后来无意中发现XORM的案例中好多耳熟能详的项目:
xorm.io/zh/docs/cha…
加上平常使用GORM时,确实有一些不适应的地方,索性就给换成XORM了。
然后累积了一点点个人经验,不一定适合其他人,但也想写个文章总结一下。
Sync 同步数据库结构
xorm.io/zh/docs/cha…
这种模式叫代码优先,先写struct再生成数据库,
我也尝试了XORM的Sync方法,挺好的,没啥毛病,
但我个人还是喜欢数据库优先,先用数据库工具创建表,再写struct。
(可以搜索一下sql to struct,有在线工具将数据库转换为struct)
created & updated
type TableName struct {
CreatedAt *carbon.DateTime `xorm:"created" `
UpdatedAt *carbon.DateTime `xorm:"updated" `
}
在tag中添加created或updated,XORM会自动填充创建时间和更新时间。
但我更喜欢在创建数据库时设置默认CURRENT_TIMESTAMP
CREATE TABLE `table_name` (
`created_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
)
然后tag中添加<-标记为只读
type TableName struct {
CreatedAt *carbon.DateTime `xorm:"<-" `
UpdatedAt *carbon.DateTime `xorm:"<-" `
}
id 要添加 autoinc 和 pk 两个标签
type TableName struct {
Id int64 `xorm:"autoincr pk" `
}
添加了autoincr调用Insert方法之后Id才会被填充,
添加了pk才能调用ID方法按主键查询。
type User struct {
Id int64 `xorm:"autoincr pk" `
NickName string
}
func TestUser(t *testing.T) {
user := &User{NickName: "xxx"}
tmd.DB2().Insert(user)
fmt.Printf("id: %v", user.Id) // 标签中带有 autoincr,Id才会被填充
user2 := &User{}
tmd.DB2().ID(1).Get(user2) // 标签中带有 pk,才能调用 .ID(1) 查询
fmt.Printf("user2: %v", user2)
}
存为JSON的自定义类型 不需要实现 Scanner 和 Valuer 接口
实际开发中自定义类型经常是存为json,
在GORM每个自定义类型都需要实现Scanner和Valuer接口。
gorm.io/zh_CN/docs/…
XORM文档是说struct默认会存为json
xorm.io/zh/docs/cha…
但实际上要加json标签才会存为json,
不然会抛出no primary key for col xxx错误。
不知道是不是我理解有误,
不管怎样总比一个个实现Scanner & Valuer来得方便。
type SpotPosInfo struct {
Name string
Address string
Latitude string
Longitude string
}
type Spot struct {
Id int64 `xorm:"autoincr pk" `
PosInfo *SpotPosInfo `xorm:"json" ` // 不加json会出现错误:no primary key for col pos_info
}
func TestUser(t *testing.T) {
spot := &Spot{
PosInfo: &SpotPosInfo{
Name: "aaa",
Address: "bbb",
Latitude: "ccc",
Longitude: "ddd",
},
}
rows, err := tmd.DB2().Insert(spot)
dump.P(rows, err)
}
另外如果不想存为JSON,比如只想以逗号连接字符串,
Scanner & Valuer在XORM是ToDB和FromDB,
就命名而言 个人感觉比Scan和Value直观很多,
不过XORM也支持标准的Scanner & Valuer接口。
不用复制 struct
这一条其实和库无关,只是换成XORM后发现这样更适合自己,
比如用户表有三个字段id,username,password,有时候我们只需要id,username。
用GORM时候看到文档这么写的:
gorm.io/zh_CN/docs/…
XORM当然也可以这么用,
但是表结构一有改动,就得检查所有struct,而且会存在大量相似的struct,
换成XORM后我这么写:
type User struct {
ID uint `json:"id"`
Name string `json:"name"`
Age int `json:"age,omitempty"`
Gender string `json:"gender,omitempty"`
}
const UserApiFields = "id,name"
func TestUser(t *testing.T) {
users := make([]User, 0)
err := tmd.DB2().Limit(10).Select(UserApiFields).Find(&users)
dump.P(err)
}
重点在json标签中加上,omitempty,然后把常用的字段定义成常量。
这样子序列化成json的时候就不会看到age,gender
未完待续
其它的暂时没想到,等想到了再来更新。