postgresql - 从特定函数创建通用函数(重构)

标签 postgresql go refactoring bulkinsert pq

我正在使用 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)
}

此代码中的所有内容都运行良好。

需求:

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

我想我需要这样的东西:

DBBulkInsert(users, "users", "username", "age")

具有功能 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用我的用户数组。

更好:

也许我也可以删除参数 "users", "username", "age"DBBulkInsert(users, "users", "username", "age") ,但如何?反射(reflection)?

最佳答案

您的rows 类型需要是[][]interface{},即一个行列表,其中每一行都是一个列值列表。然后使用该类型,可以使用 ... 将每个 row“解包”到 Exec 调用中。

即:

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

要从 []model.User[]model.Whatever[][]interface{},您需要使用反射。如果需要,您还可以使用反射来获取列名和表名。

假设您的模型类型如下:

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

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

以下是一个更完整的示例,说明如何从标签收集“元”数据以及如何从结构字段聚合列值。

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

关于postgresql - 从特定函数创建通用函数(重构),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55208819/

相关文章:

java - 重构代码查找二叉树是否是 BST

postgresql - B+树或B树

php - 带有数组字段的准备语句 PostgreSQL&PHP

amazon-web-services - 使用AWS STS获取临时凭证:Web身份 token 在哪里?

go - falcore 热重启不会重新加载主代码

java - 重构一系列方法

java - 单进程与分布式程序中的重构代码有什么区别?

postgresql - libgdal.so.1 : undefined symbol: sqlite3_column_table_name

sql - Postgres 一次更新多行

ruby - 类似Ruby的golang中的语法糖方法