go - 如何在 Gorm 中模仿联合类型?

标签 go go-gorm

我知道使用自定义类型是一个常见问题,但请耐心等待...

我想定义一个自定义类型“ConnectionInfo”(见下文):

type DataSource struct {
    gorm.Model

    Name           string
    Type           DataSourceType `sql:"type:ENUM('POSTGRES')" gorm:"column:data_source_type"`
    ConnectionInfo ConnectionInfo `gorm:"embedded"`
}

我想将 ConnectionInfo 限制为有限数量的类型之一,即:

type ConnectionInfo interface {
    PostgresConnectionInfo | MySQLConnectionInfo
}

我该怎么做?

到目前为止我的进展:

我定义了一个 ConnectionInfo 接口(interface)(我现在知道这在 GORM 中是无效的,但我如何解决它?)

type ConnectionInfo interface {
    IsConnectionInfoType() bool
}

然后我用两种类型实现了这个接口(interface)(并实现了扫描仪和评估器接口(interface)),如下所示:

type PostgresConnectionInfo struct {
    Host     string
    Port     int
    Username string
    Password string
    DBName   string
}

func (PostgresConnectionInfo) IsConnectionInfoType() bool {
    return true
}

func (p *PostgresConnectionInfo) Scan(value interface{}) error {
    bytes, ok := value.([]byte)
    if !ok {
        return fmt.Errorf("failed to unmarshal the following to a PostgresConnectionInfo value: %v", value)
    }

    result := PostgresConnectionInfo{}
    if err := json.Unmarshal(bytes, &result); err != nil {
        return err
    }
    *p = result

    return nil
}

func (p PostgresConnectionInfo) Value() (driver.Value, error) {
    return json.Marshal(p)
}

但是我当然收到以下错误:

unsupported data type: /models.ConnectionInfo

最佳答案

您可以采用这种方式来代替使用此联合 GITHUB LINK 。您可以克隆这些存储库并运行代码。这是有效的。

package storage

import (
    "database/sql/driver"
    "encoding/json"
    "fmt"
    "log"

    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

type DataSourceType string

const (
    POSTGRES DataSourceType = "POSTGRES"
    MYSQL    DataSourceType = "MYSQL"
)

type PostgresConnectionInfo struct {
    Host     string
    Port     int
    Username string
    Password string
    DBName   string
}

type MySQLConnectionInfo struct {
    Host     string
    Port     int
    Username string
    Password string
    DBName   string
}

type ConnectionInfo struct {
    Postgres *PostgresConnectionInfo `gorm:"-" json:"postgres,omitempty"`
    Mysql    *MySQLConnectionInfo    `gorm:"-" json:"mysql,omitempty"`
}
type DataSource struct {
    gorm.Model
    Name           string
    Type           DataSourceType `sql:"type:ENUM('POSTGRES')" gorm:"column:data_source_type"`
    ConnectionInfo ConnectionInfo `gorm:"type:json" `
}

func (a *ConnectionInfo) Scan(src any) error {
    switch src := src.(type) {
    case nil:
        return nil
    case []byte:
        var res ConnectionInfo
        err := json.Unmarshal(src, &res)
        *a = res
        return err

    default:
        return fmt.Errorf("scan: unable to scan type %T into struct", src)
    }

}

func (a ConnectionInfo) Value() (driver.Value, error) {
    ba, err := json.Marshal(a)
    return ba, err
}

func GormTest2() {
    db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })
    if err != nil {
        log.Fatal("could not open database")
    }
    err = db.AutoMigrate(&DataSource{})
    if err != nil {
        log.Fatal("could not migrate database")
    }
    createTestData1(db)
    fetchData1(db)
}

func createTestData1(db *gorm.DB) {
    ds := []DataSource{
        {
            Name: "Postgres",
            Type: POSTGRES,
            ConnectionInfo: ConnectionInfo{
                Postgres: &PostgresConnectionInfo{
                    Host:     "localhost",
                    Port:     333,
                    Username: "sdlfj",
                    Password: "sdfs",
                    DBName:   "sdfsd",
                },
            },
        },
        {
            Name: "Mysql",
            Type: MYSQL,
            ConnectionInfo: ConnectionInfo{
                Mysql: &MySQLConnectionInfo{
                    Host:     "localhost",
                    Port:     333,
                    Username: "sdlfj",
                    Password: "sdfs",
                    DBName:   "sdfsd",
                },
            },
        },
    }
    err := db.Create(&ds).Error
    if err != nil {
        log.Println("failed to create data")
    }
}

func fetchData1(db *gorm.DB) {
    var dsList []DataSource
    if err := db.Find(&dsList).Error; err != nil {
        log.Println("failed to load data")
    }
    log.Println(dsList)
}

关于go - 如何在 Gorm 中模仿联合类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74549098/

相关文章:

mysql - 如何在gorm中串连?

json - Golang Godog REST API 测试失败

sqlite - 在 Go GORM 中显示 Foreign Keys 的 Foreign Keys

reflection - 使用 gorm 库进行反射(reflect)

time - Go 中 time.Time 的 `zero` 值是多少?

go - 如何使用 Go Gorm 创建 TEXT 列

go - 如何使用 GORM 创建或更新记录?

go - 如何在保留引号的同时将数组连接到字符串中?

xml - 如何将字符串转换为函数名

go - 在命令行解析中引发自定义错误