代码之家  ›  专栏  ›  技术社区  ›  Fred Hors

从特定函数(重构)创建常规dbbulkinsert

  •  0
  • Fred Hors  · 技术社区  · 7 年前

    我用的是 createUsers func来填充我的伪数据库,只是为了测试。

    我正在使用的批量导入功能 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)
    }
    

    此代码中的所有内容都工作正常。

    需要:

    我现在需要的是使它“通用”,而不仅仅是用户模型。

    我想我需要这样的东西:

    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", "username", "age" 在里面 DBBulkInsert(users, "users", "username", "age") 但是怎么做呢?反思?

    0 回复  |  直到 7 年前
        1
  •  2
  •   mkopriva    7 年前

    你的 rows 类型需要为 [][]interface{} ,即每行是列值列表的行列表。然后用那种类型的每一个 row 可以“打开”到 Exec 呼叫使用 ... .

    即:

    for _, row := range rows {
        _, err = stmt.Exec(row...)
    }
    

    []model.User []model.Whatever []接口 你需要使用反射。如果需要,还可以使用反射来获取列名和表名。

    假设您的模型类型如下:

    type User struct {
        _        struct{} `rel:"users"`
        Username string   `col:"username"`
        Age      int      `col:"age"`
    }
    

    现在可以使用反射从字段的结构标记中获取表名和列列表。(注意使用 _ (空白)字段只是如何指定表名的一个选项,它有其优点和缺点,因此由您来选择,这里我只是演示如何利用反射包)。

    下面是一个更完整的示例,说明如何从标记收集“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)
        }
    
        // ...
    }
    

    Run it on playground

    推荐文章