Я пытаюсь отвлечь мое использование моей базы данных 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 {}, не так ли?
В дополнение к данному коду вы можете передать срез как 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: ""}
}
Интерфейсы могут содержать указатели. Существует редко причина сделать указатель на интерфейс.
Если вы измените свой тип на [] интерфейс {}, он должен работать.
Если вам действительно нужны указатели на интерфейсы, по какой-то причине вам нужно сначала сохранить это поле в интерфейсе, а затем получить указатель на это.
то есть:
var i interface{} = &_product.Name
append(dest, &i)
struct { ANY val, Type t }
хотя он может содержать значение или указатель на значение. Таким образом, если вы храните указатель в обычном старом интерфейсе, получатель (пакет sql) сможет хранить данные по этому указателю. И, поскольку интерфейсы содержат тип, получатель может даже выделить память для хранения значения. так что вы можете передать пустой указатель (хотя в вашем примере объект был выделен)