#sql #types #enums #go
#sql #типы #перечисления #Вперед
Вопрос:
Я новичок в Go, и я пытаюсь написать небольшую программу для сохранения перечисленных значений в базе данных. Я объявляю свои значения следующим образом:
type FileType int64
const (
movie FileType = iota
music
book
etc
)
Я использую эти значения в своей структуре следующим образом:
type File struct {
Name string
Type FileType
Size int64
}
Я использую gorp для работы с базой данных, но, полагаю, использование gorp не имеет отношения к моей проблеме. Я помещаю данные в свою базу данных следующим образом:
dbmap.Insert(amp;File{"MyBook.pdf",movie,1000})
но когда я пытаюсь извлечь материал…
dbmap.Select(amp;dbFiles, "select * from Files")
Я получаю следующую ошибку:
panic: reflect.Set: value of type int64 is not assignable to type main.FileType
Когда я использую int64
в качестве типа для const(...)
и для File.Type
поля, все работает нормально, но я новичок в Go и хочу понять проблему.
На мой взгляд, у меня две проблемы:
- Почему не удается успешно преобразовать этот материал? Я просмотрел исходный код пакетов Go reflection и sql, и там есть методы для такого преобразования, но они, похоже, терпят неудачу. Это ошибка? В чем проблема?
-
Я понял, что можно реализовать
sql.Scanner
интерфейс, реализовав следующий метод:Scan(src interface{}) error
Я попытался реализовать этот метод, и мне даже удалось получить правильное значение из
src
и преобразовать его в aFileType
, но я был смущен, если я должен реализовать метод для «(f *FileType)
или(f FileType)
. В любом случае метод вызывается, однако я не могу перезаписатьf
(или, по крайней мере, обновление будет потеряно позже), иFile
экземпляры, считываемые из базы данных, всегда имели значение «0» в качестве значенияFile.Type
.
У вас есть какие-либо идеи по этим двум пунктам?
Комментарии:
1. Вам действительно не следует использовать значение iota за пределами Go, например, в базе данных. Если вы когда-либо измените порядок констант или добавите новую в середине, значения iota изменятся, что приведет к несоответствию существующих записей в вашей базе данных.
Ответ №1:
Недавно у меня была такая же потребность, и решение заключается в реализации двух интерфейсов:
Вот рабочий пример:
type FileType int64
func (u *FileType) Scan(value interface{}) error { *u = FileType(value.(int64)); return nil }
func (u FileType) Value() (driver.Value, error) { return int64(u), nil }
Комментарии:
1. Это правильный ответ, и его следует принять. Отличная работа.
2. Просто из любопытства, я новичок в go. Есть ли веская причина сделать это int64? Похоже, вам нужен только int16 или даже int8, поскольку у вас, вероятно, не будет миллиардов возможных значений.
3. @bigblind это то, что использовалось в исходном вопросе. Обычно я бы не стал слишком беспокоиться о размерах значений, пока это не станет проблемой или проблемное пространство не оправдает этого.
Ответ №2:
Немного не по теме, но может быть полезно другим, поскольку я продолжал пересматривать этот вопрос / ответ при решении аналогичной проблемы при работе с полями перечисления postgres в golang (которые возвращаются в виде байтов).
// Status values
const (
incomplete Status = "incomplete"
complete Status = "complete"
reject Status = "reject"
)
type Status string
func (s *Status) Scan(value interface{}) error {
asBytes, ok := value.([]byte)
if !ok {
return errors.New("Scan source is not []byte")
}
*s = Status(string(asBytes))
return nil
}
func (s SubjectStatus) Value() (driver.Value, error) {
// validation would go here
return string(s), nil
}
Комментарии:
1. В Go нет
enum
s. Ваши константы имеют типstring
. Если вы хотите, чтобы они были типаStatus
(вы делаете), тогда вам следует использоватьIncomplete Status = "incomplete"
,Complete Status = "complete"
, и т.д. (кроме того, идентификаторы с заглавными буквами не являются идиоматическими Go).2. @DaveC спасибо, обновлено! Просто для ясности, я имею в виду фактический
enum
тип столбца postgres, но с хорошими исправлениями.
Ответ №3:
- Go должен быть специфичным для типов, что иногда может быть проблемой.
(f FileType)
дешевле, чем(f *FileType)
для «собственных» типов, в значительной степени, если у вас нет сложного типа, почти всегда лучше не использовать указатель.- Что вы имеете в виду, это не перезаписывает его? вы повторно сохранили структуру после ее изменения?
Комментарии:
1. что касается 2: хорошо, но как мне изменить «f» в случае «(f FileType)», когда я пишу что-то вроде «f = book» в методе сканирования, это не имеет никакого эффекта.
2. Вы можете изменить свое значение, только если оно работает с получателем указателя. Взгляните на реализацию
sql.NullInt64
в stdlib .