`
April 20, 2018 本文阅读量

gorm使用记录

使用gorm的相关笔记。

关于Gorm

gorm文档

遇见问题

无法通过结构体的方式更新或查询零值

这里零值是说,各个类型的默认值。

关于这一点是在这里中注明了的,也提供了解决方案:

WARNING when update with struct, GORM will only update those fields that with non blank value

For below Update, nothing will be updated as “”, 0, false are blank values of their types

NOTE When query with struct, GORM will only query with those fields has non-zero value, that means if your field’s value is 0, ‘’, false or other zero values, it won’t be used to build query conditions,

You could consider to use pointer type or scanner/valuer to avoid this.

// Use pointer value
type User struct {
  gorm.Model
  Name string
  Age  *int
}

// Use scanner/valuer
type User struct {
  gorm.Model
  Name string
  Age  sql.NullInt64
}

同样适用其他形式的数据来进行更新也是可以,如:

updateData := map[string]interface{}{
	"age": 0,
	"update_time": time.Now(),
}
db.Model(&User{}).Update(updateData)

同一个连接无法多次查询

在实际使用过程中遇到的情况跟https://github.com/jinzhu/gorm/issues/1574描述类似,但是回复中的解决办法并不能帮我解决问题。

我的代码如下:

// mysql.go
var myDB *gorm.DB

func GetMyDB() *gorm.DB {
	return myDB
}

func ConnMysql() {
	//... 
	db, err := gorm.Open(...)
	//...

	myDB = db
}

使用

// models/user.go
type User struct {
	Name string `gorm:"column:name"`
	Age  int    `gorm:"column:age"`
	//...
}

type UserColl struct {
	*gorm.DB
}

func NewUserColl() *UserColl{
	return &UserColl{
		DB: GetMyDB().Model(&User{})
	}
}

func FindUserWithName(name string) (error, *User) {
	uc := NewUserColl()
	u := new(User)
	if err := uc.Where("name = ?", name).First(u).Error; err != nil {
		return err, nil
	}
 	return nil, u
}

// 无法正常使用的函数
func FindUsersWithNames(names []string) (map[string]error, []*User) {
	uc := NewUserColl()
	us := make([]*User, len(names))
	errs := map[string]error{}

	for _, name := range names {
		u := new(User)
		if err := uc.Where("name = ?", name).First(u).Error; err != nil {
			map[name] = err
			continue
		}
		us = append(us, u)
	}
	return errs, us
}

上述的FindUsersWithNames在使用过程中,多个查询只有第一个查询是正常的,其他的会报错:“record not found”,但其实数据是存在的。

进过检索我发现了,在gorm实现中,db是复用的,因此多次查询会在已经存在的结果中查询,那么找不到就能解释了。那么如何解决呢?issue#1574中提到了重新申请一个connection能解决这个问题。因此我尝试了一下更改:

// 更改1 - 无效
func FindUsersWithNames(names []string) (map[string]error, []*User) {
	us := make([]*User, len(names))
	errs := map[string]error{}

	for _, name := range names {
		uc := NewUserColl().New() // 每次loop都调用一次`New`
		u := new(User)
		if err := uc.Where("name = ?", name).First(u).Error; err != nil {
			map[name] = err
			continue
		}
		us = append(us, u)
	}
	return errs, us
}

// 更改2 - 无效
func FindUsersWithNames(names []string) (map[string]error, []*User) {
	us := make([]*User, len(names))
	errs := map[string]error{}

	for _, name := range names {
		ConnMysql() // 每次loop都重新连接一次数据库
		uc := NewUserColl()

		u := new(User)
		if err := uc.Where("name = ?", name).First(u).Error; err != nil {
			map[name] = err
			continue
		}
		us = append(us, u)
	}
	return errs, us
}

// 更改3 可用
func FindUsersWithNames(names []string) (map[string]error, []*User) {
	us := make([]*User, len(names))
	errs := map[string]error{}

	for _, name := range names {
		err, u := FindUserWithName(name) // 调用一次函数
		if err != nil {
			errs[name] = err
			continue
		}
		us = append(us, u)
	}
	return errs, us
}

经过以上尝试,找到了一个解决办法。

后言

但是问题并不是到此为止,为什么调用一层函数,就可以解决,而gorm.DB.New却不行呢?大胆分析一下:上述的三种尝试的区别在于,uc在第三种方式中,每次循环都是一个全新的变量,而不是重新赋值,但这依然不能解释为什么New不能解决上述问题。具体的原因后续研究后补上。