I have troubles while using gorm with custom types that have integer properties. Gorm tends to convert the integer values into strings for me. There are two separate problems:
Problem 1:
I have a custom type defined as follows:
type ClientType uint8
const (
USER ClientType = iota
SERVER
)
func (t ClientType) String() string {
return [...]string{"User", "Service"}[t]
And then I have a ORM struct for Client
defined like this:
type Client struct {
ClientID uint64 `gorm:"primaryKey"`
UserID uint64
ClientType ClientType
CreatedAt time.Time
}
When calling db.Create(&client)
, gorm automatically invokes the String()
method on ClientType
, and causes a data type mismatch in MySQL where I intend to store ClientType
in a TINYINT
column called client_type
.
Problem 2:
So I thought if I can't figure out how to override the automatic calling of the String()
method, I simply renamed the method to ToString()
and call it when I need it. Now gorm can't invoke it any more, but instead, it casts the integer values into numeric strings instead. So USER
which is 0
, will now become '0'
in the resulting SQL statement, and SERVER
will become '1'
, although MySQL is able to cast the strings back to integers.
I still wonder what I did wrong to make gorm think I wanted these conversions.
String()
, so I can still name the method conventionally?uint8
that made gorm converts the int to strings, since the other numeric values (ClientID and UserID) using uint64 were not affected by the issue? Is it some sort of caching of the DB schema that made gorm remember the old schema where client_type used to be an ENUM('User', 'Service')
column?As per gorm for custom types Value and Scan need to be implemented.
You can try specifying type in struct (replace <TYPE> with column type) too.
type Client struct {
ClientID uint64 `gorm:"primaryKey"`
UserID uint8
ClientType ClientType `gorm:"type:<TYPE>"`
CreatedAt time.Time
}
String() does not seems to be issue as it is not called during creates, but only during finds.
With db.LogMode(true)
, it is called during create also, but that seems to be for sql query logging and not for sending to DB.
With Log Mode off
db.LogMode(false)
fmt.Println("Creating")
db.Create(&Client{ClientID: 9, UserID: 8, ClientType: USER, CreatedAt: time.Now()})
fmt.Println("Created")
fmt.Println("Finding")
db.First(&client)
fmt.Printf("Found %v\n", client)
output
Creating
Created
Finding
String() called with 0
Found {9 8 User 2021-01-28 11:41:51.355633057 +0530 +0530}
With Log Mode on
db.LogMode(true)
fmt.Println("Creating")
db.Create(&Client{ClientID: 9, UserID: 8, ClientType: USER, CreatedAt: time.Now()})
fmt.Println("Created")
fmt.Println("Finding")
db.First(&client)
fmt.Printf("Found %v\n", client)
Output:
Creating
String() called with 0
[2021-01-28 11:39:30] [0.95ms] INSERT INTO "clients" ("client_id","user_id","client_type","created_at") VALUES (9,8,'User','2021-01-28 11:39:30')
[1 rows affected or returned ]
Created
Finding
[2021-01-28 11:39:30] [1.49ms] SELECT * FROM "clients" LIMIT 1
[1 rows affected or returned ]
String() called with 0
Found {9 8 User 2021-01-28 11:42:31.245628274 +0530 +0530}