关于gorm的问题
DB的search无法置空
下面是一个有问题的代码;大体逻辑是locatino表里面有PID,一级一级向上找父节点;第二次for循环的时候没有取到正常的结果,而是record not found
;原因是由于查询条件重叠了
// GetLocationFullNameByID 根据指定ID返回位置的全路径名
func (repo *MySQLRepo) GetLocationFullNameByID(id uint) (name string, err error) {
if id <= 0 {
return "", nil
}
var loc model.Location
for id > 0 {
// 此处第二个for循环的sql会变成 where id = ? and id =?
if err := repo.db.Model(&model.Location{}).Where("id = ?", id).Find(&loc).Error; err != nil {
if err != gorm.ErrRecordNotFound {
repo.log.Errorf("Query location by id(%d) error: %s", id, err.Error())
}
return "", err
}
if loc.PID > 0 {
id = loc.PID
name = loc.Name + "-" + name
}
}
return strings.TrimRight(name, "-"), nil
}
看一下源码:
func (s *DB) Model(value interface{}) *DB {
c := s.clone()
c.Value = value
return c
}
func (s *DB) Where(query interface{}, args ...interface{}) *DB {
return s.clone().search.Where(query, args...).db
}
func (s *DB) clone() *DB {
db := DB{
db: s.db,
parent: s.parent,
logger: s.logger,
logMode: s.logMode,
values: map[string]interface{}{},
Value: s.Value,
Error: s.Error,
blockGlobalUpdate: s.blockGlobalUpdate,
}
for key, value := range s.values {
db.values[key] = value
}
if s.search == nil {
db.search = &search{limit: -1, offset: -1}
} else {
db.search = s.search.clone()
}
db.search.db = &db
return &db
}
从源码当中可以看到,不管是Model、Where还是其他的查询相关函数都使用了clone方法,而clone里面若原search不为nil,则进行叠加; main.go
解决方法
// GetLocationFullNameByID 根据指定ID返回位置的全路径名
func (repo *MySQLRepo) GetLocationFullNameByID(id uint) (name string, err error) {
if id <= 0 {
return "", nil
}
for id > 0 {
var loc model.Location
if err := repo.db.NewScope(nil).DB().Model(&model.Location{}).Where("id =?", id).Find(&loc).Error; err != nil {
if err != gorm.ErrRecordNotFound {
repo.log.Errorf("Query location by id(%d) error: %s", id, err.Error())
}
return "", err
}
if loc.PID >= 0 {
id = loc.PID
name = loc.Name + "-" + name
}
}
return strings.TrimRight(name, "-"), nil
}
// NewDB create a new DB without search information
func (scope *Scope) NewDB() *DB {
if scope.db != nil {
db := scope.db.clone()
db.search = nil
db.Value = nil
return db
}
return nil
}
从源码当中可以看到从Scope里面New出来的DB,将search可以置空,因此就不会发生查询条件重复的问题
或者使用原生的sql机制
// GetLocationFullNameByID 根据指定ID返回位置的全路径名
func (repo *MySQLRepo) GetLocationFullNameByID(id uint) (name string, err error) {
if id <= 0 {
return "", nil
}
for id > 0 {
var loc model.Location
if err := repo.db.Raw("select * from locations where id =? and deleted_at is null ", id).Scan(&loc).Error; err != nil {
if err != gorm.ErrRecordNotFound {
repo.log.Errorf("Query location by id(%d) error: %s", id, err.Error())
}
return "", err
}
if loc.PID >= 0 {
id = loc.PID
name = loc.Name + "-" + name
}
}
return strings.TrimRight(name, "-"), nil
}
看源码
// Exec execute raw sql
func (s *DB) Exec(sql string, values ...interface{}) *DB {
scope := s.clone().NewScope(nil)
generatedSQL := scope.buildWhereCondition(map[string]interface{}{"query": sql, "args": values})
generatedSQL = strings.TrimSuffix(strings.TrimPrefix(generatedSQL, "("), ")")
scope.Raw(generatedSQL)
return scope.Exec().db
}
Exec的与NewScop的本质没有区别,都是使用NewScope进行操作
JSON结构查询不出来
select t1.oob->>'$.network.ip' as oob_ip
目前这个数据查出来是空
limiter和offset
// 此处limit和offset不生效,需要将其放在Find前面
if err := db.Find(&result).Limit(limiter.Limit).Offset(limiter.Offset).Error; err != nil {
repo.log.Error(err.Error())
return nil, err
}
db = db.Limit(limiter.Limit).Offset(limiter.Offset)
if err := db.Find(&result).Error; err != nil {
repo.log.Error(err.Error())
return nil, err
}