Open geektutu opened 4 years ago
for i, value := range values {
v := value.([]interface{})
if bindStr == "" {
bindStr = genBindVars(len(v))
}
sql.WriteString(fmt.Sprintf("(%v)", bindStr))
if i+1 != len(values) {
sql.WriteString(", ")
}
vars = append(vars, v...)
}
value.([]interface{})
这里是什么意图没看懂?能解释下吗为什么是一个空接口的切片呢
我在一个测试文件中看到TestMain方法 那里面有一句 _ = TestDB.Close() 为什么db close后紧接着的测试还能正常进行呢 DB不是已经Close了吗
@walkmiao
我在一个测试文件中看到TestMain方法 那里面有一句 _ = TestDB.Close() 为什么db close后紧接着的测试还能正常进行呢 DB不是已经Close了吗
func TestMain(m *testing.M) {
TestDB, _ = sql.Open("sqlite3", "../gee.db")
code := m.Run()
_ = TestDB.Close()
os.Exit(code)
}
这个是 Go 测试工程的 setup
和 teardown
的机制,允许在每个用例运行m.Run()
前后做一些事情,Open
是用例执行前运行的,Close
是用例执行后运行的。
谢谢我去学习下 lch 邮箱:372815340@qq.com 签名由 网易邮箱大师 定制 在2020年04月03日 11:57,Dai Jie 写道: @walkmiao 我在一个测试文件中看到TestMain方法 那里面有一句 _ = TestDB.Close() 为什么db close后紧接着的测试还能正常进行呢 DB不是已经Close了吗 func TestMain(m *testing.M) {
TestDB, _ = sql.Open("sqlite3", "../gee.db")
code := m.Run()
_ = TestDB.Close()
os.Exit(code)
} 这个是 Go 测试工程的 setup 和 teardown 的机制,允许在每个用例运行m.Run()前后做一些事情,Open 是用例执行前运行的,Close 是用例执行后运行的。 参考 Go语言单元测试简明教程#setup 和 teardown — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.
Find 的代码,真的看不懂,咋整
@walkmiao
for i, value := range values { v := value.([]interface{}) if bindStr == "" { bindStr = genBindVars(len(v)) } sql.WriteString(fmt.Sprintf("(%v)", bindStr)) if i+1 != len(values) { sql.WriteString(", ") } vars = append(vars, v...) }
value.([]interface{})
这里是什么意图没看懂?能解释下吗为什么是一个空接口的切片呢
每个Model的RecordValues都是一个切片,这里个values是一个二维数组
func (s *Session) Insert(values ...interface{}) (int64, error) {
recordValues := make([]interface{}, 0)
for _, value := range values {
table := s.Model(value).RefTable()
s.clause.Set(clause.INSERT, table.Name, table.FieldNames)
recordValues = append(recordValues, table.RecordValues(value))
}
s.clause.Set(clause.VALUES, recordValues...)
sql, vars := s.clause.Build(clause.INSERT, clause.VALUES)
result, err := s.Raw(sql, vars...).Exec()
if err != nil {
return 0, err
}
return result.RowsAffected()
}
请教一下,这里s.clause.Set(clause.INSERT, table.Name, table.FieldNames)
是不是只用设置一次,同一个Type一次执行应该只能设置一次query string吧,这里是为了书写方便是吗?
@furthergo 你的理解是对的,table.Name
只需要设置一次,的确是为了书写方便。
type generator func(values ...interface{}) (string, []interface{})
var generators map[Type]generator 请问这段是什么意思,函数作为MAP的VALUE吗?
@xiezhenyu19970913 对的,把函数看做普通变量来使用即可。
看到geeorm这一章感觉有点吃力,主要是对反射和函数型编程不熟
func (s *Session) Find(values interface{}) error { destSlice := reflect.Indirect(reflect.ValueOf(values)) destType := destSlice.Type().Elem() table := s.Model(reflect.New(destType).Elem().Interface()).RefTable()
会不会这样写 table := s.Model(reflect.New(destType).Interface()).RefTable() 好点
先介绍generator,然后当中的map[Type]generator的Type会比较让人费解,通过github代码cmd+A发现实际上Type定义在clause里面,但是介绍顺序导致读者理解起来比较尴尬。
这就是大佬在Top公司一天的任务量吗,换做是我可能要一周。没有比较,就没有暴击!
太难了😂
看来不把反射搞清楚是看不下去了……
@tmphuang6 func (s *Session) Find(values interface{}) error { destSlice := reflect.Indirect(reflect.ValueOf(values)) destType := destSlice.Type().Elem() table := s.Model(reflect.New(destType).Elem().Interface()).RefTable()
会不会这样写 table := s.Model(reflect.New(destType).Interface()).RefTable() 好点
不会,因为需要destSlice,destType
@tmphuang6 func (s *Session) Find(values interface{}) error { destSlice := reflect.Indirect(reflect.ValueOf(values)) destType := destSlice.Type().Elem() table := s.Model(reflect.New(destType).Elem().Interface()).RefTable()
会不会这样写 table := s.Model(reflect.New(destType).Interface()).RefTable() 好点
不太行,reflect.New注释里面
New returns a Value representing a pointer to a new zero value ,the returned Value's Type is PointerTo(typ)
,给的是一个指针的Value,需要使用Elem方法提取具体的值
Find方法里给values传递dest各个field的指针然后通过scan(values...)这样间接给dest赋值这个方法感觉很巧妙。
for i, value := range values {
v := value.([]interface{})
if bindStr == "" {
bindStr = genBindVars(len(v))
}
sql.WriteString(fmt.Sprintf("(%v)", bindStr))
if i+1 != len(values) {
sql.WriteString(", ")
}
vars = append(vars, v...)
}
_value函数中for循环中是否应该将关于bindStr的判断去掉呢?如果有if判断的话只会在第一个循环中给bindStr赋值,但是每个value的长度应该是不同的吧?
@bowenddd
for i, value := range values { v := value.([]interface{}) if bindStr == "" { bindStr = genBindVars(len(v)) } sql.WriteString(fmt.Sprintf("(%v)", bindStr)) if i+1 != len(values) { sql.WriteString(", ") } vars = append(vars, v...) }
_value函数中for循环中是否应该将关于bindStr的判断去掉呢?如果有if判断的话只会在第一个循环中给bindStr赋值,但是每个value的长度应该是不同的吧?
v := value.([]interface{}) // 将value转成了切片,切片长度只有1
bindStr = genBindVars(len(v)) // len(v) = 1, bindStr = (?)
这里应该没有问题。
func (s *Session) Find(values interface{}) error {
destSlice := reflect.Indirect(reflect.ValueOf(values))
destType := destSlice.Type().Elem()
table := s.Model(reflect.New(destType).Elem().Interface()).RefTable()
s.clause.Set(clause.SELECT, table.Name, table.FieldNames)
sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT)
rows, err := s.Raw(sql, vars...).QueryRows()
if err != nil {
return err
}
for rows.Next() {
dest := reflect.New(destType).Elem()
var values []interface{}
for _, name := range table.FieldNames {
values = append(values, dest.FieldByName(name).Addr().Interface())
}
if err := rows.Scan(values...); err != nil {
return err
}
destSlice.Set(reflect.Append(destSlice, dest))
}
return rows.Close()
}
destSlice.Set(reflect.Append(destSlice, dest)) 每次循环Set一次,能否改成循环结束后,一次Set?
@bowenddd
for i, value := range values { v := value.([]interface{}) if bindStr == "" { bindStr = genBindVars(len(v)) } sql.WriteString(fmt.Sprintf("(%v)", bindStr)) if i+1 != len(values) { sql.WriteString(", ") } vars = append(vars, v...) }
_value函数中for循环中是否应该将关于bindStr的判断去掉呢?如果有if判断的话只会在第一个循环中给bindStr赋值,但是每个value的长度应该是不同的吧?
是的 我也检测出了这个问题 我改了一下
func _values(values ...interface{}) (string, []interface{}) {
// VALUES ($v1), ($v2), ...
var bindStr string
var sql strings.Builder
var vars []interface{}
sql.WriteString("VALUES ")
for i, value := range values {
v := value.([]interface{}) // 将value转成了切片,切片长度只有1
// 转换成对应v切片数量的问号
bindStr = genBindVars(len(v))
sql.WriteString(fmt.Sprintf("(%v)", bindStr))
if i+1 != len(values) { // 不结束就分割一下
sql.WriteString(", ")
}
vars = append(vars, v...)
}
return sql.String(), vars
}
@drone789
func (s *Session) Find(values interface{}) error { destSlice := reflect.Indirect(reflect.ValueOf(values)) destType := destSlice.Type().Elem() table := s.Model(reflect.New(destType).Elem().Interface()).RefTable() s.clause.Set(clause.SELECT, table.Name, table.FieldNames) sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT) rows, err := s.Raw(sql, vars...).QueryRows() if err != nil { return err } for rows.Next() { dest := reflect.New(destType).Elem() var values []interface{} for _, name := range table.FieldNames { values = append(values, dest.FieldByName(name).Addr().Interface()) } if err := rows.Scan(values...); err != nil { return err } destSlice.Set(reflect.Append(destSlice, dest)) } return rows.Close() }
destSlice.Set(reflect.Append(destSlice, dest)) 每次循环Set一次,能否改成循环结束后,一次Set?
不可以 一个dest是一个对应的结构体而不是切片,不能存储多个值,只能一次一次往里加
@paopaoshuaige
@bowenddd
for i, value := range values { v := value.([]interface{}) if bindStr == "" { bindStr = genBindVars(len(v)) } sql.WriteString(fmt.Sprintf("(%v)", bindStr)) if i+1 != len(values) { sql.WriteString(", ") } vars = append(vars, v...) }
_value函数中for循环中是否应该将关于bindStr的判断去掉呢?如果有if判断的话只会在第一个循环中给bindStr赋值,但是每个value的长度应该是不同的吧?
是的 我也检测出了这个问题 我改了一下
func _values(values ...interface{}) (string, []interface{}) { // VALUES ($v1), ($v2), ... var bindStr string var sql strings.Builder var vars []interface{} sql.WriteString("VALUES ") for i, value := range values { v := value.([]interface{}) // 将value转成了切片,切片长度只有1 // 转换成对应v切片数量的问号 bindStr = genBindVars(len(v)) sql.WriteString(fmt.Sprintf("(%v)", bindStr)) if i+1 != len(values) { // 不结束就分割一下 sql.WriteString(", ") } vars = append(vars, v...) } return sql.String(), vars }
这里没问题,只是实现的方式不同。s.Insert这样写想达到的目的是批量插入同类型多笔数据。
INSERT INTO table (c1,c2,c3) VALUES(1,2,3),(1,2,3)
如果想一次插入多种类型的多笔数据的话需要改造一下,类似于
func (s *Session) Insert(values ...interface{}) (int64, error) {
for _, value := range values {
table := s.Model(value).RefTable()
s.clause.Set(clause.INSERT, table.Name, table.FieldNames)
// 类似于这样改,思路是这样
s.clause.Set(clause.VALUES, table.RecordValues(value))
sql, vars := s.clause.Build(clause.INSERT, clause.VALUES)
result, err := s.Raw(sql, vars...).Exec()
if err != nil {
return 0, err
}
}
......
return result.RowsAffected()
}
这样每个value都会反射,SQL都会有一个INSERT开头
s.Insert(&User{}, &Student{}) 可以这样用,但是不推荐。
INSERT INTO table1 (c1,c2,c3) VALUES(1,2,3)
INSERT INTO table2 (c1,c2) VALUES(1,2)
@drone789
func (s *Session) Find(values interface{}) error { destSlice := reflect.Indirect(reflect.ValueOf(values)) destType := destSlice.Type().Elem() table := s.Model(reflect.New(destType).Elem().Interface()).RefTable() s.clause.Set(clause.SELECT, table.Name, table.FieldNames) sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT) rows, err := s.Raw(sql, vars...).QueryRows() if err != nil { return err } for rows.Next() { dest := reflect.New(destType).Elem() var values []interface{} for _, name := range table.FieldNames { values = append(values, dest.FieldByName(name).Addr().Interface()) } if err := rows.Scan(values...); err != nil { return err } destSlice.Set(reflect.Append(destSlice, dest)) } return rows.Close() }
destSlice.Set(reflect.Append(destSlice, dest)) 每次循环Set一次,能否改成循环结束后,一次Set?
这个不是挺正常的吗,dest保存一行查询数据赋值给相应结构体的值,每次赋好一个值就向slice里面append一下
for rows.Next() {
dest := reflect.New(destType).Elem()
var values []interface{}
for _, name := range table.FieldNames {
values = append(values, dest.FieldByName(name).Addr().Interface())
}
if err := rows.Scan(values...); err != nil {
// 此处
return err
}
destSlice.Set(reflect.Append(destSlice, dest))
}
大佬们,为什么上面的“此处”报错返回时,不做rows.Close()?
https://geektutu.com/post/geeorm-day3.html
7天用 Go语言/golang 从零实现 ORM 框架 GeeORM 教程(7 days implement golang object relational mapping framework from scratch tutorial),动手写 ORM 框架,参照 gorm, xorm 的实现。实现新增(insert)记录的功能;使用反射(reflect)将数据库的记录转换为对应的结构体实例,实现查询(select)功能。