Сохранение перечислимых значений в базу данных

Я новичок в Go, и я пытаюсь написать небольшую программу для сохранения перечисляемых значений в базе данных. Я объявляю свои значения следующим образом:

type FileType int64
const (
    movie FileType = iota
    music
    book
    etc
)

Я использую эти значения в своей структуре следующим образом:

type File struct {
    Name     string
    Type     FileType
    Size     int64
}

Я использую gorp для моего материала базы данных, но я предполагаю, что использование gorp не имеет отношения к моей проблеме. Я помещал вещи в свою БД следующим образом:

dbmap.Insert(&File{"MyBook.pdf",movie,1000})

но когда я пытаюсь найти материал...

dbmap.Select(&dbFiles, "select * from Files")

Я получаю следующую ошибку:

panic: reflect.Set: value of type int64 is not assignable to type main.FileType

Когда я использую int64 как тип для const(...) и для поля File.Type, все работает отлично, но я новичок в Go и хочу понять проблему. Как я вижу это, у меня две проблемы:

  • Почему нельзя конвертировать этот материал успешно? Я посмотрел исходный код пакетов Go и sql, и есть методы для такого преобразования, но они, похоже, терпят неудачу. Это ошибка? В чем проблема?
  • Я понял, что можно реализовать интерфейс sql.Scanner, выполнив следующий метод:

    Scan(src interface{}) error
    

    Я попытался реализовать этот метод, и даже смог получить правильное значение из src и преобразовать его в FileType, но я был сбит с толку, если бы я должен реализовать метод для (f *FileType) или (f FileType). В любом случае метод вызывается, однако я не могу перезаписать f (или, по крайней мере, обновление будет потеряно позже), а экземпляры File, прочитанные из БД, всегда имели значение "0" в качестве значения для File.Type.

Есть ли у вас какие-либо идеи по этим двум пунктам?

Ответ 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 }

Ответ 2

Немного не по теме, но может быть полезным для других, поскольку я продолжал пересматривать этот вопрос/ответ при решении подобной проблемы при работе с полями enge 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
 }

Ответ 3

  • Go должен быть конкретным с типами, которые иногда могут быть больны.
  • (f FileType) дешевле, чем (f *FileType) для "родных" типов, в значительной степени, если у вас нет сложного типа, почти всегда лучше не использовать указатель.
  • Что вы имеете в виду, это не перезаписывает? вы сохранили структуру после того, как вы ее модифицировали?