英文:
how to define custom sql type enum field
问题
我想定义一个自定义的 SQL 类型 enum
,代码如下:
package db
import (
"database/sql/driver"
"github.com/spf13/cast"
)
type Enum int64
func (e Enum) Value() (driver.Value, error) {
return int64(e), nil
}
func (e *Enum) Scan(v any) error {
*e = Enum(cast.ToInt64(v))
return nil
}
我想要使用以下代码:
package db_test
import (
"db"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"testing"
)
type StatusEnum = db.Enum
const (
StatusEnumEnable StatusEnum = iota
StatusEnumDisable
)
var StatusEnumMap = map[StatusEnum]string{
StatusEnumEnable: "Enable",
StatusEnumDisable: "Disable",
}
type module struct {
Status StatusEnum
}
func TestEnum(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"))
db.AutoMigrate(&module{})
db.Model(&module{}).Create(&module{Status: StatusEnumDisable})
var out *module
db.Debug().Model(&module{}).Where("status=?", StatusEnumDisable).First(&out)
t.Log(out)
t.Log(StatusEnumMap[out.Status])
}
现在我在需要字符串格式时使用 StatusEnumMap[EnumValue]
。我认为 value.String()
更加优雅简洁,而且 fmt
格式化也可以打印字符串而不是数字。
你有什么更好的实现方法吗?谢谢你的建议。
上面的问题已经由 @Zeke Lu 回答了。
另一个小问题是,如果我不使用 Valuer
并删除 String
方法,那么 SQL 条件 status=1
将变成 status="1"
,为什么会这样?如果 MySQL 中存在类型不一致,查询性能会受到影响吗?
第二个问题只是 GORM 的调试信息。实际执行的 SQL 是 status=1
。请参考 gorm-issue。
英文:
I want to define custom sql type enum
like this:
package db
import (
"database/sql/driver"
"github.com/spf13/cast"
)
type Enum int64
func (e Enum) Value() (driver.Value, error) {
return int64(e), nil
}
func (e *Enum) Scan(v any) error {
*e = Enum(cast.ToInt64(v))
return nil
}
And I want use the following:
package db_test
import (
"db"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"testing"
)
type StatusEnum = db.Enum
const (
StatusEnumEnable StatusEnum = iota
StatusEnumDisable
)
// this is error because this type alias can not define new method
//func (e *StatusEnum) String() string{
// if v,ok := StatusEnumMap[*e];ok{
// return v
// }
// return "unknown"
//}
var StatusEnumMap = map[StatusEnum]string{
StatusEnumEnable: "Enable",
StatusEnumDisable: "Disable",
}
type module struct {
Status StatusEnum
}
func TestEnum(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"))
db.AutoMigrate(&module{})
db.Model(&module{}).Create(&module{Status: StatusEnumDisable})
var out *module
// SELECT * FROM `modules` WHERE status=1 ORDER BY `modules`.`status` LIMIT 1
db.Debug().Model(&module{}).Where("status=?", StatusEnumDisable).First(&out)
t.Log(out)
t.Log(StatusEnumMap[out.Status])
//t.Log(out.Status.String())
}
Now I'm using it like this StatusEnumMap[EnumValue]
when I want the string format. I think value.String()
is more elegant and simple. fmt
formatting can also print strings instead of numbers.
How can I better implement this feature? Thank you for your suggestions.
The above question has been answered by @Zeke Lu.
Another little problem, If I did not used Valuer
and delete the String
method, the sql condition status=1
will be status="1"
, why? Whether the query performance of sql will be affected if the type inconsistency in mysql.
The second problem:just is gorm debug info. Actual execute sql is status=1
. See gorm-issue
答案1
得分: 1
似乎如果底层类型是int64
,则不需要实现Valuer
和Scanner
接口。如果是这样的话,Enum
类型就不需要了。下面的示例在没有Enum
的情况下工作:
注意:我稍微修改了你的示例,将StatusEnum
类型上的Stringer
接口实现改为了*StatusEnum
。
package db_test
import (
"testing"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type StatusEnum int64
const (
StatusEnumEnable StatusEnum = iota
StatusEnumDisable
)
var StatusEnumMap = map[StatusEnum]string{
StatusEnumEnable: "Enable",
StatusEnumDisable: "Disable",
}
func (e StatusEnum) String() string {
if v, ok := StatusEnumMap[e]; ok {
return v
}
return "unknown"
}
type module struct {
Status StatusEnum
}
func TestEnum(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"))
db.AutoMigrate(&module{})
db.Model(&module{}).Create(&module{Status: StatusEnumDisable})
var out *module
// SELECT * FROM `modules` WHERE status=1 ORDER BY `modules`.`status` LIMIT 1
db.Debug().Model(&module{}).Where("status=?", StatusEnumDisable).First(&out)
t.Log(out)
t.Logf("status value: %d, status string: %s", out.Status, out.Status)
}
输出结果为:
&{Disable}
status value: 1, status string: Disable
请注意,第一个输出现在是&{Disable}
,之前是&{1}
。
英文:
It seems that you don't need to implement the Valuer
and Scanner
interfaces if the underlying type is int64
. If that's true, the type Enum
is not needed. The following example works without Enum
:
Note: I modified your demo slightly to implement the Stringer
interface on type StatusEnum
instead of *StatusEnum
.
package db_test
import (
"testing"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type StatusEnum int64
const (
StatusEnumEnable StatusEnum = iota
StatusEnumDisable
)
var StatusEnumMap = map[StatusEnum]string{
StatusEnumEnable: "Enable",
StatusEnumDisable: "Disable",
}
func (e StatusEnum) String() string {
if v, ok := StatusEnumMap[e]; ok {
return v
}
return "unknown"
}
type module struct {
Status StatusEnum
}
func TestEnum(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"))
db.AutoMigrate(&module{})
db.Model(&module{}).Create(&module{Status: StatusEnumDisable})
var out *module
// SELECT * FROM `modules` WHERE status=1 ORDER BY `modules`.`status` LIMIT 1
db.Debug().Model(&module{}).Where("status=?", StatusEnumDisable).First(&out)
t.Log(out)
t.Logf("status value: %d, status string: %s", out.Status, out.Status)
}
The output is:
&{Disable}
status value: 1, status string: Disable
Please note that the first one is &{Disable}
now. It's &{1}
before.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论