Часть указателей на интерфейс для абстракций запросов MySQL

0

Я пытаюсь отвлечь мое использование моей базы данных MySQL, и я застрял на ошибке.

Есть объект, который я возьму в качестве примера:

package models

// Product : The Product model
type Product struct {
    ID         int
    Name       string
    Price      int
    PictureURL string
}

Я попытаюсь получить id = 1 продукта id = 1 в моей базе данных. Для этого предположим, что у меня уже есть соединение с моей базой данных, которая представлена следующей переменной:

var databaseMySQL *sql.DB

Чтобы запросить мою базу данных, я использую эту функцию:

// QueryMySQL query our MySQL database
func QueryMySQL(sqlquery model.SQLQuery) model.Status {

    // prepare the query
    stmtOut, err := databaseMySQL.Prepare(sqlquery.Query)
    if err != nil {
        return model.Status{Code: http.StatusInternalServerError, Error: err}
    }
    defer stmtOut.Close()

    // Run the query
    err = stmtOut.QueryRow(sqlquery.Args).Scan(sqlquery.Dest)
    if err != nil {
        return model.Status{Code: http.StatusInternalServerError, Error: err}
    } else {
        return model.Status{Code: http.StatusOK, Error: nil}
    }
}

2 модели, используемые здесь, это SQLQuery & Status.

package models

type SQLQuery struct {
    Query string
    Args  []interface{}
    Dest  []*interface{}
}

Status просто содержит error и int.

Таким образом, поскольку Scan() как следующий прототип Scan func(dest...interface{}) error, я могу передать параметр []*interface{} качестве параметра.

Если я прав, то я должен иметь возможность получить элементы Dest, заполненные элементом T type, а затем преобразовать их в те типы, которые мне нужны?

func GetProductByID(ID int) (model.Product, model.Status) {

    // "SELECT ID, Name, Price, PictureURL FROM Products WHERE ID = ?"
    var _product model.Product

    // HERE IS THE PROBLEM
    var dest []*interface{}
    append(dest, &_product.Name)
    append(dest, &_product.Price)
    append(dest, &_product.PictureURL)
    // HERE IS THE PROBLEM

    status := QueryMySQL(model.SQLQuery{
        Query: "SELECT Name, Price, PictureURL FROM Products WHERE ID = ?",
        Args:  []interface{}{ID},
        Dest:  dest})

    return _product, model.Status{Code: http.StatusOK, Error: nil}
}

PS: эта функция является всего лишь базовым тестом, чтобы попробовать мою логику

Однако, я получаю сообщение об ошибке:

не может использовать & _product.Name(type * string) как type * interface {} в append: * interface {} является указателем на интерфейс, а не интерфейс

Для меня есть две ошибки:

  • cannot use &_product.Name(type *string) as type *interface {}
  • *interface {} is pointer to interface, not interface

Во-первых, почему я не могу использовать интерфейс для хранения моей строки?

Во-вторых, поскольку я передаю указатель на строку, что с interface{}? это должен быть * interface {}, не так ли?


Правильный код, благодаря Дэвиду Budworth за его ответ

В дополнение к данному коду вы можете передать срез как varargs, как я сделал, добавив ... после вашего имени переменной среза

mysqlproduct.go

// GetProductByID returns a Product based on a given ID
func GetProductByID(ID int) (model.Product, model.Status) {

    _product := model.Product{
        ID: ID}

    status := QueryMySQL(&model.SQLQuery{
        Query: "SELECT Name, Price, PictureURL FROM Products WHERE ID = ?",
        Args:  []interface{}{_product.ID},
        Dest:  []interface{}{&_product.Name, &_product.Price, &_product.PictureURL}})

    if status.Code != http.StatusOK {
        return _product, status
    }

    return _product, model.Status{Code: http.StatusOK, Error: ""}
}

mysql.go

// QueryMySQL query our MySQL database
func QueryMySQL(sqlquery *model.SQLQuery) model.Status {

    stmtOut, err := databaseMySQL.Prepare(sqlquery.Query)
    if err != nil {
        return model.Status{Code: http.StatusInternalServerError, Error: err.Error()}
    }
    defer stmtOut.Close()

    // Run the query
    err = stmtOut.QueryRow(sqlquery.Args...).Scan(sqlquery.Dest...)
    if err != nil {
        return model.Status{Code: http.StatusInternalServerError, Error: err.Error()}
    }
    defer stmtOut.Close()

    return model.Status{Code: http.StatusOK, Error: ""}
}
Теги:
pointers
go
interface
slice

1 ответ

1
Лучший ответ

Интерфейсы могут содержать указатели. Существует редко причина сделать указатель на интерфейс.

Если вы измените свой тип на [] интерфейс {}, он должен работать.

Если вам действительно нужны указатели на интерфейсы, по какой-то причине вам нужно сначала сохранить это поле в интерфейсе, а затем получить указатель на это.

то есть:

var i interface{} = &_product.Name
append(dest, &i)
  • 0
    Это интересно, я попробую, как только смогу. Я разработчик C, поэтому я понимаю указатели, но вижу интерфейс {} как void *, и, поскольку функция хранит результаты в указателях varargs, мне было интересно, смогу ли я сделать его универсальным, понимаете?
  • 0
    Интерфейс {} больше похож на struct { ANY val, Type t } хотя он может содержать значение или указатель на значение. Таким образом, если вы храните указатель в обычном старом интерфейсе, получатель (пакет sql) сможет хранить данные по этому указателю. И, поскольку интерфейсы содержат тип, получатель может даже выделить память для хранения значения. так что вы можете передать пустой указатель (хотя в вашем примере объект был выделен)
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню