从特定函数创建通用函数(重构)
Create a general func from particular function (refactoring)
我正在使用 createUsers
函数来填充我的假数据库,仅用于测试。
我正在使用 pq
(https://godoc.org/github.com/lib/pq#hdr-Bulk_imports) 的批量导入功能。
func createUsers() {
users := []models.User{}
for i := 0; i < 10; i++ {
users = append(users, models.User{Username: "username"+i, Age: i})
}
connStr := "user=postgres password=postgres dbname=dbname sslmode=disable"
DB, err = sql.Open("postgres", connStr)
checkErr(err)
txn, err := DB.Begin()
checkErr(err)
stmt, err := txn.Prepare(pq.CopyIn("users", "username", "age"))
checkErr(err)
for _, user := range users {
_, err = stmt.Exec(user.Username, user.Age)
checkErr(err)
}
_, err = stmt.Exec()
checkErr(err)
err = stmt.Close()
checkErr(err)
err = txn.Commit()
checkErr(err)
}
此代码中的所有内容都运行良好。
需要:
我现在需要的是制作它"general",而不仅仅是用户模型。
我想我需要这样的东西:
DBBulkInsert(users, "users", "username", "age")
与 func DBBulkInsert
类似:
func DBBulkInsert(rows []interface{}, tableName string, tableColumns ...string) {
// DB var from connection func
txn, err := DB.Begin()
checkErr(err)
stmt, err := txn.Prepare(pq.CopyIn(tableName, tableColumns...))
checkErr(err)
for _, row := range rows {
_, err = stmt.Exec(row[0], row[1]) //THIS IS TOTALLY WRONG! WHAT TO DO HERE?
checkErr(err)
}
_, err = stmt.Exec()
checkErr(err)
err = stmt.Close()
checkErr(err)
err = txn.Commit()
checkErr(err)
}
问题:
显然_, err = stmt.Exec(row[0], row[1])
是完全错误的。我不明白如何用我的用户数组调用 DBBulkInsert
。
更好:
也许我也可以删除 DBBulkInsert(users, "users", "username", "age")
中的参数 "users", "username", "age"
,但是怎么做呢?反思?
您的 rows
类型需要是 [][]interface{}
,即行列表,其中每一行都是列值的列表。然后使用该类型,每个 row
都可以 "unpacked" 进入 Exec
调用,使用 ...
.
即:
for _, row := range rows {
_, err = stmt.Exec(row...)
}
要从 []model.User
或 []model.Whatever
到 [][]interface{}
,您需要使用反射。如果需要,您还可以使用反射来获取列名和 table 名称。
假设您的模型类型如下:
type User struct {
_ struct{} `rel:"users"`
Username string `col:"username"`
Age int `col:"age"`
}
现在您可以使用反射从字段的结构标签中获取 table 名称和列列表。 (请注意,_
(空白)字段的使用只是如何指定 table 名称的一种选择,它有其缺点和优点,因此由您选择,这里我只是试图演示如何利用反射包)。
以下是一个更完整的示例,说明如何从标签收集 "meta" 数据以及如何从结构字段聚合列值。
func DBBulkInsert(source interface{}) {
slice := reflect.ValueOf(source)
if slice.Kind() != reflect.Slice {
panic("not a slice")
}
elem := slice.Type().Elem()
if elem.Kind() == reflect.Ptr {
elem = elem.Elem()
}
if elem.Kind() != reflect.Struct {
panic("slice elem not a struct, nor a pointer to a struct")
}
// get table and column names
var tableName string
var cols []string
for i := 0; i < elem.NumField(); i++ {
f := elem.Field(i)
if rel := f.Tag.Get("rel"); len(rel) > 0 {
tableName = rel
}
if col := f.Tag.Get("col"); len(col) > 0 {
cols = append(cols, col)
}
}
// aggregate rows
rows := [][]interface{}{}
for i := 0; i < slice.Len(); i++ {
m := slice.Index(i)
if m.Kind() == reflect.Ptr {
m = m.Elem()
}
vals := []interface{}{}
for j := 0; j < m.NumField(); j++ {
ft := m.Type().Field(j)
if col := ft.Tag.Get("col"); len(col) > 0 {
f := m.Field(j)
vals = append(vals, f.Interface())
}
}
rows = append(rows, vals)
}
// ...
}
我正在使用 createUsers
函数来填充我的假数据库,仅用于测试。
我正在使用 pq
(https://godoc.org/github.com/lib/pq#hdr-Bulk_imports) 的批量导入功能。
func createUsers() {
users := []models.User{}
for i := 0; i < 10; i++ {
users = append(users, models.User{Username: "username"+i, Age: i})
}
connStr := "user=postgres password=postgres dbname=dbname sslmode=disable"
DB, err = sql.Open("postgres", connStr)
checkErr(err)
txn, err := DB.Begin()
checkErr(err)
stmt, err := txn.Prepare(pq.CopyIn("users", "username", "age"))
checkErr(err)
for _, user := range users {
_, err = stmt.Exec(user.Username, user.Age)
checkErr(err)
}
_, err = stmt.Exec()
checkErr(err)
err = stmt.Close()
checkErr(err)
err = txn.Commit()
checkErr(err)
}
此代码中的所有内容都运行良好。
需要:
我现在需要的是制作它"general",而不仅仅是用户模型。
我想我需要这样的东西:
DBBulkInsert(users, "users", "username", "age")
与 func DBBulkInsert
类似:
func DBBulkInsert(rows []interface{}, tableName string, tableColumns ...string) {
// DB var from connection func
txn, err := DB.Begin()
checkErr(err)
stmt, err := txn.Prepare(pq.CopyIn(tableName, tableColumns...))
checkErr(err)
for _, row := range rows {
_, err = stmt.Exec(row[0], row[1]) //THIS IS TOTALLY WRONG! WHAT TO DO HERE?
checkErr(err)
}
_, err = stmt.Exec()
checkErr(err)
err = stmt.Close()
checkErr(err)
err = txn.Commit()
checkErr(err)
}
问题:
显然_, err = stmt.Exec(row[0], row[1])
是完全错误的。我不明白如何用我的用户数组调用 DBBulkInsert
。
更好:
也许我也可以删除 DBBulkInsert(users, "users", "username", "age")
中的参数 "users", "username", "age"
,但是怎么做呢?反思?
您的 rows
类型需要是 [][]interface{}
,即行列表,其中每一行都是列值的列表。然后使用该类型,每个 row
都可以 "unpacked" 进入 Exec
调用,使用 ...
.
即:
for _, row := range rows {
_, err = stmt.Exec(row...)
}
要从 []model.User
或 []model.Whatever
到 [][]interface{}
,您需要使用反射。如果需要,您还可以使用反射来获取列名和 table 名称。
假设您的模型类型如下:
type User struct {
_ struct{} `rel:"users"`
Username string `col:"username"`
Age int `col:"age"`
}
现在您可以使用反射从字段的结构标签中获取 table 名称和列列表。 (请注意,_
(空白)字段的使用只是如何指定 table 名称的一种选择,它有其缺点和优点,因此由您选择,这里我只是试图演示如何利用反射包)。
以下是一个更完整的示例,说明如何从标签收集 "meta" 数据以及如何从结构字段聚合列值。
func DBBulkInsert(source interface{}) {
slice := reflect.ValueOf(source)
if slice.Kind() != reflect.Slice {
panic("not a slice")
}
elem := slice.Type().Elem()
if elem.Kind() == reflect.Ptr {
elem = elem.Elem()
}
if elem.Kind() != reflect.Struct {
panic("slice elem not a struct, nor a pointer to a struct")
}
// get table and column names
var tableName string
var cols []string
for i := 0; i < elem.NumField(); i++ {
f := elem.Field(i)
if rel := f.Tag.Get("rel"); len(rel) > 0 {
tableName = rel
}
if col := f.Tag.Get("col"); len(col) > 0 {
cols = append(cols, col)
}
}
// aggregate rows
rows := [][]interface{}{}
for i := 0; i < slice.Len(); i++ {
m := slice.Index(i)
if m.Kind() == reflect.Ptr {
m = m.Elem()
}
vals := []interface{}{}
for j := 0; j < m.NumField(); j++ {
ft := m.Type().Field(j)
if col := ft.Tag.Get("col"); len(col) > 0 {
f := m.Field(j)
vals = append(vals, f.Interface())
}
}
rows = append(rows, vals)
}
// ...
}