Reputation: 1416
let say I have a struct Profile and sql table profiles.
type Profile struct {
ID int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Age int `json:"age"`
}
And, I get a record from profiles table, assign into the Profile struct.
These fields are allow null, so that I used sql.NullString, sql.NullInt64, pq.NullTime to check valid value before assign to struct.
// ... DB.Query(...)
// ... rows.Next()
// ... rows.Scan()
// ...
if firstName.Valid {
p.FirstName = firstName.String
}
if lastName.Valid {
p.LastName = lastName.String
}
if age.Valid {
p.Age = age.Int64
}
// ...
If I have more than 10 tables, each tables has more than 30 columns, I have to check all variables again and again. That make ugly code, someone have any suggestion to make it pretty?
Upvotes: 1
Views: 4943
Reputation: 79546
You shouldn't be checking like that at all. The nullable types are intended for use directly in structs:
type Profile struct {
ID sql.NullInt64 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
FirstName sql.NullString `json:"first_name"`
LastName sql.NullString `json:"last_name"`
Age sql.NullInt64 `json:"age"`
}
Then access the values using, for example, product.FirstName.String()
. There is no reason for checking product.FirstName.Valid
except where you actually care about the distinction between nil
and an empty value (in the examples in your question, you clearly don't care about this distinction).
An alternative, which may be more suitable in some situations, is to just use a pointer:
type Profile struct {
ID *int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
FirstName *string `json:"first_name"`
LastName *string `json:"last_name"`
Age *int `json:"age"`
}
Of course then any code that uses these variables must dereference them, and check for nil
, so this may not always be the most desirable, depending on how you use the values.
A third option, if you never care about the distinction between a NULL
value and and empty value, is to create your own type which treats these the same. Example:
type myString string
func (s *myString) Scan(src interface{}) error {
if src == nil {
return nil
}
if value, ok := src.(string); ok {
*s = value
return nil
}
return fmt.Errorf("Unsupported type for myString: %T", src)
}
Upvotes: 5