我知道使用自定义类型是一个常见的问题,但请原谅我...
我想定义一个自定义类型‘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
}
我怎么能这样做呢?
My progress thus far:个
我定义了一个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)
}
但当然,我得到了以下错误:
unsupported data type: <myproject>/models.ConnectionInfo
WORKING ANSWER
多亏了Shahriar Ahmed,我有了一个惯用的解决方案,我只是想额外增加一点关于我是如何(try )来确保在Models包外部处理ConnectionInfo类型时的类型安全的.
我的ConnectionInfo字段现在如下所示:
type DataSource struct {
gorm.Model
ConnectionInfo connectionInfo `gorm:"type:jsonb;not null"`
}
它的类型是Shahriar建议的,并包括还实现Scanner和Valuer接口的每个子类型/变量:
type connectionInfo struct {
Postgres *PostgresConnectionInfo `gorm:"-" json:"postgres,omitempty"`
MySQL *MySQLConnectionInfo `gorm:"-" json:"mysql,omitempty"`
}
func (c *connectionInfo) Scan(src any) error {
switch src := src.(type) {
case nil:
return nil
case []byte:
var res connectionInfo
err := json.Unmarshal(src, &res)
*c = res
return err
default:
return fmt.Errorf("unable to scan type %T into connectionInfo", src)
}
}
func (c connectionInfo) Value() (driver.Value, error) {
return json.Marshal(c)
}
但是,我没有导出‘ConnectionInfo’类型(通过使用小写的‘c’),并且我已经创建了一个导出的‘ConnectionInfo’接口,‘PostgresConnectionInfo’和‘MySQLConnectionInfo’类型实现该接口:
type ConnectionInfo interface {
IsConnectionInfoType() bool
}
type PostgresConnectionInfo struct {
Host string `json:"host" binding:"required"`
Port int `json:"port" binding:"required"`
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
DBName string `json:"dbName" binding:"required"`
}
func (PostgresConnectionInfo) IsConnectionInfoType() bool {
return true
}
当想要引用抽象类型时,我将使用ConnectionInfo,然后将其传递给我的Models包,该包将使用下面的代码获取具体类型并实例化一个‘ConnectionInfo’类型:
func getConcreteConnectionInfo(connInfo ConnectionInfo) connectionInfo {
switch v := connInfo.(type) {
case *PostgresConnectionInfo:
return connectionInfo{Postgres: v}
case *MySQLConnectionInfo:
return connectionInfo{MySQL: v}
default:
panic(fmt.Sprintf("Unknown connection info type: %T", connInfo))
}
}