如何在Gorm中模拟联合类型?

huangapple go评论88阅读模式
英文:

How to mimic a union type in 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接口(我现在知道在GORM中这是无效的,但我该如何解决?)

type ConnectionInfo interface {
    IsConnectionInfoType() bool
}

然后,我用两种类型实现了这个接口(并实现了scanner和valuer接口),如下所示:

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)
}

但是我当然会得到以下错误:

不支持的数据类型:/models.ConnectionInfo

英文:

I know that using custom types is a common question, but bear with me...

I would like to define a custom type 'ConnectionInfo' (see below):

type DataSource struct {
	gorm.Model

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

I would like to restrict ConnectionInfo to be one of a limited number of types, i.e.:

type ConnectionInfo interface {
    PostgresConnectionInfo | MySQLConnectionInfo
}

How can I do this?

My progress thus far:

I defined a ConnectionInfo interface (I now know this is invalid in GORM, but how do I get around it?)

type ConnectionInfo interface {
	IsConnectionInfoType() bool
}

I've then implemented this interface with two types (and implemented the scanner and valuer interfaces) like so:

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)
}

But of course I get the following error:

> unsupported data type: <myproject>/models.ConnectionInfo

答案1

得分: 2

不使用这个union,你可以使用这种方式来处理 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)
}

希望对你有帮助!

英文:

Instead of using this union, you can approach this way GITHUB LINK. You can clone these repo and run the code. This is working.

package storage
import (
&quot;database/sql/driver&quot;
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;log&quot;
&quot;gorm.io/driver/sqlite&quot;
&quot;gorm.io/gorm&quot;
&quot;gorm.io/gorm/logger&quot;
)
type DataSourceType string
const (
POSTGRES DataSourceType = &quot;POSTGRES&quot;
MYSQL    DataSourceType = &quot;MYSQL&quot;
)
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:&quot;-&quot; json:&quot;postgres,omitempty&quot;`
Mysql    *MySQLConnectionInfo    `gorm:&quot;-&quot; json:&quot;mysql,omitempty&quot;`
}
type DataSource struct {
gorm.Model
Name           string
Type           DataSourceType `sql:&quot;type:ENUM(&#39;POSTGRES&#39;)&quot; gorm:&quot;column:data_source_type&quot;`
ConnectionInfo ConnectionInfo `gorm:&quot;type:json&quot; `
}
func (a *ConnectionInfo) Scan(src any) error {
switch src := src.(type) {
case nil:
return nil
case []byte:
var res ConnectionInfo
err := json.Unmarshal(src, &amp;res)
*a = res
return err
default:
return fmt.Errorf(&quot;scan: unable to scan type %T into struct&quot;, src)
}
}
func (a ConnectionInfo) Value() (driver.Value, error) {
ba, err := json.Marshal(a)
return ba, err
}
func GormTest2() {
db, err := gorm.Open(sqlite.Open(&quot;gorm.db&quot;), &amp;gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatal(&quot;could not open database&quot;)
}
err = db.AutoMigrate(&amp;DataSource{})
if err != nil {
log.Fatal(&quot;could not migrate database&quot;)
}
createTestData1(db)
fetchData1(db)
}
func createTestData1(db *gorm.DB) {
ds := []DataSource{
{
Name: &quot;Postgres&quot;,
Type: POSTGRES,
ConnectionInfo: ConnectionInfo{
Postgres: &amp;PostgresConnectionInfo{
Host:     &quot;localhost&quot;,
Port:     333,
Username: &quot;sdlfj&quot;,
Password: &quot;sdfs&quot;,
DBName:   &quot;sdfsd&quot;,
},
},
},
{
Name: &quot;Mysql&quot;,
Type: MYSQL,
ConnectionInfo: ConnectionInfo{
Mysql: &amp;MySQLConnectionInfo{
Host:     &quot;localhost&quot;,
Port:     333,
Username: &quot;sdlfj&quot;,
Password: &quot;sdfs&quot;,
DBName:   &quot;sdfsd&quot;,
},
},
},
}
err := db.Create(&amp;ds).Error
if err != nil {
log.Println(&quot;failed to create data&quot;)
}
}
func fetchData1(db *gorm.DB) {
var dsList []DataSource
if err := db.Find(&amp;dsList).Error; err != nil {
log.Println(&quot;failed to load data&quot;)
}
log.Println(dsList)
}

huangapple
  • 本文由 发表于 2022年11月23日 23:16:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/74549098.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定