Создать общую функцию из определенной функции (рефакторинг) - PullRequest
1 голос
/ 17 марта 2019

Я использую функцию 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"), но как? Отражение?

1 Ответ

2 голосов
/ 17 марта 2019

Ваш тип 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)
    }

    // ...
}

Запустите его на детской площадке

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...