使用GORM golang保留自定义集合数据类型 [英] Persisting custom set data type using GORM golang

查看:247
本文介绍了使用GORM golang保留自定义集合数据类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在go中创建了一个自定义的Set数据类型,该数据类型用于定义一对多关系.例如,在我的架构中,我具有以下结构定义

I have created a custom Set Data type in go, which i am using to define one to many relationships. For example in my Schema, i have the following struct definition

type Doctor struct {
  firstName string
  lastName string
  capabilities commons.Set
 }

这里capabilities是具有以下值chat, audio, video的一组字符串,通过这种设置,我试图使用GORM库将上述结构持久化为MySQL,但是当我这样做时,我得到了以下错误

Here capabilities is a set of strings which have the following values chat, audio, video, with this setup i am trying to persist the above struct into MySQL using the GORM library, but when i do this i get the following error

panic: invalid sql type Set (interface) for mysql

goroutine 6 [running]:
catalog/vendor/github.com/jinzhu/gorm.(*mysql).DataTypeOf(0xc00027e8a0, 0xc00024d680, 0x8, 0x8)
    /home/kumard/go/src/catalog/vendor/github.com/jinzhu/gorm/dialect_mysql.go:123 +0xce9
catalog/vendor/github.com/jinzhu/gorm.(*Scope).createTable(0xc000169400, 0xc14e60)

我知道我必须实现某些方法才能实现此目的,但是我无法在此处确定要实现哪种方法/回调.

I know that i have to implement certain methods in order to achieve this, but i am not able to figure out which method/callback to implement here.

ThreadUnsafeSet定义:

ThreadUnsafeSet Definition:

type threadUnsafeSet map[interface{}]struct{}    

type OrderedPair struct {
    First interface{}
    Second interface{}
}    

func newThreadUnsafeSet() threadUnsafeSet {
    return make(threadUnsafeSet)
}    

func (pair *OrderedPair) Equal(other OrderedPair) bool {
    return pair.First == other.First && pair.Second == other.Second
}    

func (set *threadUnsafeSet) Add(i interface{}) bool {
    _, found := (*set)[i]
    if found {
        return false
    }
    (*set)[i] = struct{}{}
    return true
}    

func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
    for _, val := range i {
        if _, ok := (*set)[val]; !ok {
            return false
        }
    }
    return true
}    

func (set *threadUnsafeSet) Cardinality() int {
    return len(*set)
}    

func (set *threadUnsafeSet) Equal(other Set) bool {
    _ = other.(*threadUnsafeSet)
    if set.Cardinality() != other.Cardinality() {
        return false
    }
    for elem := range *set {
        if !other.Contains(elem){
            return false
        }
    }
    return true
}    

func (set *threadUnsafeSet) IsSubSet(other Set) bool {
    _ = other.(*threadUnsafeSet)
    if set.Cardinality() > other.Cardinality() {
        return false
    }
    for elem := range *set {
        if !other.Contains(elem) {
            return false
        }
    }
    return true
}    

func (set *threadUnsafeSet) IsProperSubSet(other Set) bool {
    return set.IsSubSet(other) && !set.Equal(other)
}    

func (set *threadUnsafeSet) IsSuperSet(other Set) bool {
    return other.IsSubSet(set)
}    

func (set *threadUnsafeSet) IsProperSuperSet(other Set) bool {
    return set.IsSuperSet(other) && !set.Equal(other)
}    

func (set *threadUnsafeSet) Union(other Set) Set {
    o := other.(*threadUnsafeSet)
    result := newThreadUnsafeSet()
    for elem := range *set {
        result.Add(elem)
    }
    for elem := range *o {
        result.Add(elem)
    }
    return &result
}    


func (set *threadUnsafeSet) Intersect(other Set) Set {
    o := other.(*threadUnsafeSet)    

    intersection := newThreadUnsafeSet()    

    if set.Cardinality() < other.Cardinality() {
        for elem := range *set {
            if other.Contains(elem) {
                intersection.Add(elem)
            }
        }
    } else {
        for elem := range *o {
            if set.Contains(elem) {
                intersection.Add(elem)
            }
        }
    }
    return &intersection
}    

func (set *threadUnsafeSet) Difference(other Set) Set {
    _ = other.(*threadUnsafeSet)
    difference := newThreadUnsafeSet()
    for elem := range *set {
        if !other.Contains(elem) {
            difference.Add(elem)
        }
    }
    return &difference
}    

func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
    _ = other.(*threadUnsafeSet)
    aDiff := set.Difference(other)
    bDiff := other.Difference(set)
    return aDiff.Difference(bDiff)
}    

func (set *threadUnsafeSet) Clear(){
    *set = newThreadUnsafeSet()
}    

func (set *threadUnsafeSet) Remove(i interface{}) {
    delete(*set, i)
}    

func (set *threadUnsafeSet) Each(cb func(interface{}) bool) {
    for elem := range *set {
        if cb(elem) {
            break
        }
    }
}    

func (set *threadUnsafeSet) Iter() <-chan interface{} {
    ch := make(chan interface{})
    go func() {
        for elem := range *set {
            ch <- elem
        }
        close(ch)
    }()
    return ch
}    

func (set *threadUnsafeSet) Iterator() *commons.Iterator {
    iterator, ch, stopCh := commons.NewIterator()
    go func (){
        L:
            for elem := range *set {
                select {
                    case <-stopCh: {
                        break L
                    }
                    case ch <- elem:
                }
                close(ch)
            }
    }()
    return iterator
}    

func (set *threadUnsafeSet) Clone() Set {
    clonedSet := newThreadUnsafeSet()
    for elem := range *set {
        clonedSet.Add(elem)
    }
    return &clonedSet
}    

func (set *threadUnsafeSet) String() string {
    items := make([]string, 0, len(*set))    

    for elem := range *set {
        items = append(items, fmt.Sprintf("%v", elem))
    }
    return fmt.Sprintf("Set{%s}", strings.Join(items, ","))
}    

func (pair OrderedPair) String() string {
    return fmt.Sprintf("(%v, %v)", pair.First, pair.Second)
}    

func (set *threadUnsafeSet) Pop() interface{} {
    for item := range *set {
        delete (*set, item)
        return item
    }
    return nil
}    

func (set *threadUnsafeSet) PowerSet() Set {
    powSet := NewThreadUnsafeSet()
    nullSet := newThreadUnsafeSet()
    powSet.Add(&nullSet)    

    for _, v := range i {
        switch t := v.(type) {
        case []interface{}, map[string]interface{}:
            continue
        default:
            set.Add(t)
        }
    }
    return nil
}    

func (set *threadUnsafeSet) FindAny() fi.Optional {
    for elem := range *set {
        return fi.MakeNullable(elem)
    }
    return fi.MakeNullable(nil)
}    

func (set *threadUnsafeSet) FindFirst(predicate func(interface{}) bool) fi.Optional {
    for elem := range *set {
        if predicate(elem) {
            return fi.MakeNullable(elem)
        }
    }
    return fi.MakeNullable(nil)
}    

func (set *threadUnsafeSet) RemoveAll(elementsToRemove ... interface{}) {
    for _, elem := range elementsToRemove {
        if set.Contains(elem) {
            set.Remove(elem)
        }
    }
}
    for es := range *set {
        u := newThreadUnsafeSet()
        j := powSet.Iter()
        for err := range j {
            p := newThreadUnsafeSet()
            if reflect.TypeOf(err).Name() == "" {
                k := err.(*threadUnsafeSet)
                for ek := range *(k){
                    p.Add(ek)
                }
            }else {
                p.Add(err)
            }
            p.Add(es)
            u.Add(&p)
        }
        powSet = powSet.Union(&u)
    }
    return powSet
}    

func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
    o := other.(*threadUnsafeSet)
    cartProduct := NewThreadUnsafeSet()
    for i := range *set {
        for j := range *o {
            elem := OrderedPair{First: i, Second: j}
            cartProduct.Add(elem)
        }
    }
    return cartProduct
}    

func (set *threadUnsafeSet) ToSlice() []interface{}{
    keys := make([]interface{}, 0, set.Cardinality())
    for elem := range *set {
        keys = append(keys, elem)
    }
    return keys
}    

func (set *threadUnsafeSet) MarshalJSON() ([]byte, error) {
    items := make([]string, 0, set.Cardinality())    

    for elem := range *set {
        b, err := json.Marshal(elem)
        if err != nil {
            return nil, err
        }
        items = append(items, string(b))
    }
    return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
}    

func (set *threadUnsafeSet) UnMarshalJSON(b []byte) error {
    var i []interface{}    

    d := json.NewDecoder(bytes.NewReader(b))
    d.UseNumber()
    err := d.Decode(&i)
    if err != nil {
        return err
    }

设置接口定义

type Set interface {
    Add(i interface{}) bool    

    Cardinality() int    

    Clear()    

    Clone() Set    

    Contains(i ...interface{}) bool    

    Difference(other Set) Set    

    Equal(other Set) bool    

    Intersect(other Set) Set    

    IsProperSubSet(other Set) bool    

    IsSubSet(other Set) bool    

    IsSuperSet(other Set) bool    

    Each(func(interface{}) bool)    

    Iter() <-chan interface{}    

    Iterator() *commons.Iterator    

    Remove(i interface{})    

    String() string    

    SymmetricDifference(other Set) Set    

    Union(other Set) Set    

    Pop() interface{}    

    PowerSet() Set    

    CartesianProduct(other Set) Set    

    ToSlice() []interface{}    

    FindAny() fi.Optional    

    FindFirst(predicate func(interface{}) bool) fi.Optional    

    RemoveAll(...interface{})
}    

// NewSet creates and returns a reference to an empty set.  Operations
// on the resulting set are thread-safe.
func NewSet(s ...interface{}) Set {
    set := newThreadSafeSet()
    for _, item := range s {
        set.Add(item)
    }
    return &set
}    

// NewSetWith creates and returns a new set with the given elements.
// Operations on the resulting set are thread-safe.
func NewSetWith(elts ...interface{}) Set {
    return NewSetFromSlice(elts)
}    

// NewSetFromSlice creates and returns a reference to a set from an
// existing slice.  Operations on the resulting set are thread-safe.
func NewSetFromSlice(s []interface{}) Set {
    a := NewSet(s...)
    return a
}    

// NewThreadUnsafeSet creates and returns a reference to an empty set.
// Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSet() Set {
    set := newThreadUnsafeSet()
    return &set
}    

// NewThreadUnsafeSetFromSlice creates and returns a reference to a
// set from an existing slice.  Operations on the resulting set are
// not thread-safe.
func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
    a := NewThreadUnsafeSet()
    for _, item := range s {
        a.Add(item)
    }
    return a
}

推荐答案

您需要实现扫描器&自定义类型的驱动程序 Valuer 接口 然后数据库驱动程序就会知道如何将数据存储在数据库中以及如何从数据库中获取数据.

You need to implement Scanner & Driver Valuer interface for the custom type then database driver could know how to store the data in the database and how to get the data from the database.

func (data *CustomType) Value() (driver.Value, error) {
    ...
}
func (data *CustomType) Scan(value interface{}) error {
    ...
}

示例: 假设UserAccess是map[interface{}]struct{}类型.

Example: Suppose UserAccess is map[interface{}]struct{} type.

type UserAccess map[interface{}]struct{}

func (data *UserAccess) Value() (driver.Value, error) {
    return data.ConvertJSONToString(), nil
}
func (data *UserAccess) Scan(value interface{}) error {
    *data = data.ConvertStringToJson(valueString)
}

此处ConvertStringToJsonConvertJSONToString用于将自定义数据类型值转换为与数据库兼容的类型,例如json-string.

Here ConvertStringToJson and ConvertJSONToString used to convert custom datatype value into database compatible type like json-string.

这篇关于使用GORM golang保留自定义集合数据类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆