英文:
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 (
"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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论