I have the following two models:
type Dock struct {
BaseModel
// ... other fields redacted
Groups []Group; `gorm:"many2many:dock_group;json:"groups"`
}
type Group struct {
BaseModel
// ... other fields redacted
Docks []Dock; `gorm:"many2many:dock_group;json:"docks"`
}
Where in base model I set the primary key to UUID instead of uint:
type BaseModel struct {
ID *datatypes.UUID `gorm:"type:binary(16);primary_key;" json:"id"`
CreatedAt time.Time `sql:"index" json:"createdAt"`
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
}
The custom datatypes.UUID field is defined as follows, and I define a scanner and valuer for it:
import uuid "github.com/satori/go.uuid"
type UUID struct {
UUID uuid.UUID
}
// Value satisfies the Valuer interace and is responsible for writing data to the database
func (u *UUID) Value() (driver.Value, error) {
if u == nil {
return nil, nil
}
var v driver.Value
v = u.UUID.Bytes()
return v, nil
}
// Scan satisfies the Scanner interace and is responsible for reading data from the database
func (u *UUID) Scan(src interface{}) error {
if src == nil {
return nil
}
b, ok := src.([]byte)
if !ok {
return fmt.Errorf("did not scan: expected []byte but was %T", src)
}
var err error
u.UUID, err = uuid.FromBytes(b)
return err
}
Group is like tag, and has a many-to-many relationship Dock. This is a REST service generating JSON. When user pulls up the dock JSON object, I intend it to show a field which has an array of Group object. And when user pulls up the group JSON object, it should should show a field which has an array of dock object. So I want both to preload to one level down.
I think Gorm is attempting to pull out data from the link table, but it didn't work as there was 0 row pulled:
[2020-05-21 11:18:47] [14.81ms] SELECT * FROM `group` INNER JOIN `dock_group` ON `dock_group`.`group_id` = `group`.`id` WHERE `group`.`deleted_at` IS NULL AND ((`dock_group`.`dock_id` IN ('<binary>',') �n3�HH�s�[�O�')))
[0 rows affected or returned ]
I execute the following query and it works just fine in the MySQL client:
mysql> SELECT * FROM `group` INNER JOIN `dock_group` ON `dock_group`.`group_id` = `group`.`id` WHERE `group`.`deleted_at` IS NULL AND ((`dock_group`.`dock_id` IN (UUID_TO_BIN("2920e86e-33b1-4848-a773-e68e5bde4fc0")))) \G
*************************** 1. row ***************************
id: 2����5Cw������4x
created_at: 2020-05-21 11:07:26
updated_at: 2020-05-21 11:07:26
deleted_at: NULL
name: group name
dock_id: ) �n3�HH�s�[�O�
group_id: 2����5Cw������4x
1 row in set (0.01 sec)
I don't know if preloading works for many-to-many in Gorm. But the query above by Gorm should not return 0 rows. I think it may has to do with how Gorm is querying the link table and it doesn't work with I using the UUID field as a primary key. Any way to make it work?
EDIT:
Regarding to Billy Yuan's reply about ) �n3�HH�s�[�O� not actually 2920e86e-33b1-4848-a773-e68e5bde4fc0, I think it is because I can see it below:
mysql> select id from dock;
+------------------+
| id |
+------------------+
| '{�L��Gd��h@� �| |
| ) �n3�HH�s�[�O� |
+------------------+
2 rows in set (0.01 sec)
mysql> select BIN_TO_UUID(id) from dock;
+--------------------------------------+
| BIN_TO_UUID(id) |
+--------------------------------------+
| 277b894c-c2f0-4764-9994-6840c700d47c |
| 2920e86e-33b1-4848-a773-e68e5bde4fc0 |
+--------------------------------------+
2 rows in set (0.00 sec)
mysql> select UUID_TO_BIN("2920e86e-33b1-4848-a773-e68e5bde4fc0");
+-----------------------------------------------------+
| UUID_TO_BIN("2920e86e-33b1-4848-a773-e68e5bde4fc0") |
+-----------------------------------------------------+
| ) �n3�HH�s�[�O� |
+-----------------------------------------------------+