多级一对一关系表使用Joins查询出错

  • 使用 #3282 问题的回复处理报错, 源码内好像只有对主表的关联关系有判断
  • 加连表sql又查询不到结果, 我看源码对字段解析好像只有一级表关系使用"__"做处理.

这是我的表接口以及具体查询

type User struct {
    ID   int64  `gorm:"primary_key;autoIncrement" json:"id"`
    Name string `gorm:"column:name;" json:"name"`
}

type Order struct {
    ID       int64  `gorm:"primary_key;autoIncrement" json:"id"`
    SerialNo string `gorm:"not null; column:serialNo" json:"serialNo"`
    UserId   int64  `gorm:"not null; column:userId;" json:"userId"`
    User     *User  `gorm:"foreignKey:userId"`
}

type OrderShipping struct {
    ID      int64  `gorm:"primary_key;autoIncrement" json:"id"`
    Note    string `gorm:"column: note;" json:"note"`
    OrderId int64  `gorm:"not null; column:orderId;" json:"orderId"`
    Order   *Order `gorm:"foreignKey:orderId"`
}

func (op *OrderShipping) FindAll() {

    // 直接join
    tx := DB.Debug().Model(op).Joins("Order").Joins("User").Find(&op)
    if tx.Error != nil {
        fmt.Printf("tx err is: %+v\n", tx.Error)
    }
        // 打印出的错误: 
       // tx err is: Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'User' at line 1

    // 加关联
    tx2 := DB.Debug().Model(op).
        Joins("Order"). //.Joins("User").Find(&op)
        Joins("LEFT JOIN `users` AS `User` ON `Order`.`userId` = `User`.`id`").
        Select("`order_shippings`.*, `Order`.*, `User`.*").
        Find(&op)
    if tx2.Error != nil {
        fmt.Printf("tx2 err is: %+v\n", tx2.Error)
    }
    fmt.Printf("op: %+v\n", op.Order)
    fmt.Printf("op.Order.User: %+v\n", op.Order.User)
        // op.Order.User 为 nil
}

sql

INSERT INTO users (id, name) VALUES (1, 'tom');
INSERT INTO orders (id, serialNo, userId) VALUES (1, '123456', 1);
INSERT INTO order_shippings (id, ` note`, orderId) VALUES (1, 'notes....', 1);

Expected answer

需要怎样才能通过Joins的方式正常的查询到User表中的数据?

Comment From: wxy2077

你的结构体 column写的有问题,MySQL字段命名习惯是下划线。 对于你这个需求,直接用预加载Preload就可以解决。

以下是我的例子 gorm_demo_test.go

package test

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "testing"
)

type User struct {
    ID   int64  `gorm:"primaryKey;autoIncrement;column:id;type:INT;" json:"id"`
    Name string `gorm:"column:name;" json:"name"`
}

func (u *User) TableName() string {
    return "users"
}

type Order struct {
    ID       int64  `gorm:"primaryKey;autoIncrement;column:id;type:INT;" json:"id"`
    SerialNo string `gorm:"not null; column:serial_no" json:"serial_no"`
    UserId   int64  `gorm:"not null; column:user_id;" json:"user_id"`
    User     *User  `gorm:"foreignKey:UserId;references:ID" json:"user"`
}

func (o *Order) TableName() string {
    return "orders"
}

type OrderShipping struct {
    ID      int64  `gorm:"primaryKey;autoIncrement;column:id;type:INT;" json:"id"`
    Note    string `gorm:"column:note;" json:"note"`
    OrderId int64  `gorm:"column:order_id;" json:"order_id"`
    Order *Order `gorm:"foreignKey:OrderId;references:ID" json:"order"`
}

func (o *OrderShipping) TableName() string {
    return "order_shippings"
}

func (o *OrderShipping) FindAll(db *gorm.DB) (list []*OrderShipping) {

    list = make([]*OrderShipping, 0)
    db.Debug().Model(&OrderShipping{}).Preload("Order", func(tx *gorm.DB) *gorm.DB {
        return tx.Preload("User")
    }).Find(&list)

    return list
}

func Test_CRUD(t *testing.T) {
    db, err := gorm.Open(mysql.New(mysql.Config{
        DSN:                       "root:123456@tcp(127.0.0.1:3306)/gozero?charset=utf8mb4&parseTime=True&loc=Local", // DSN data source name
        DefaultStringSize:         256,                                                                               // string 类型字段的默认长度
        DisableDatetimePrecision:  true,                                                                              // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
        DontSupportRenameIndex:    true,                                                                              // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
        DontSupportRenameColumn:   true,                                                                              // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
        SkipInitializeWithVersion: false,                                                                             // 根据当前 MySQL 版本自动配置
    }), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
    if err != nil {
        panic(err)
    }

    list := new(OrderShipping).FindAll(db)


    t.Log(list[0]) //  &{1 notes.... 1 0xc000406cf0}
    t.Log(list[0].Order) // &{1 123456 1 0xc0001e9680}
    t.Log(list[0].Order.User.Name) // tom

    // INSERT INTO users (id, name) VALUES (1, 'tom');
    // INSERT INTO orders (id, serial_no, user_id) VALUES (1, '123456', 1);
    // INSERT INTO order_shippings (id, note, order_id) VALUES (1, 'notes....', 1);

}

Comment From: jinzhu

joins doesn't works with multiple levels

Comment From: runningzyp

@sankforever

Comment From: DarckRM

你的结构体 column写的有问题,MySQL字段命名习惯是下划线。 对于你这个需求,直接用预加载Preload就可以解决。

以下是我的例子 gorm_demo_test.go

```go package test

import ( "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" "testing" )

type User struct { ID int64 gorm:"primaryKey;autoIncrement;column:id;type:INT;" json:"id" Name string gorm:"column:name;" json:"name" }

func (u *User) TableName() string { return "users" }

type Order struct { ID int64 gorm:"primaryKey;autoIncrement;column:id;type:INT;" json:"id" SerialNo string gorm:"not null; column:serial_no" json:"serial_no" UserId int64 gorm:"not null; column:user_id;" json:"user_id" User *User gorm:"foreignKey:UserId;references:ID" json:"user" }

func (o *Order) TableName() string { return "orders" }

type OrderShipping struct { ID int64 gorm:"primaryKey;autoIncrement;column:id;type:INT;" json:"id" Note string gorm:"column:note;" json:"note" OrderId int64 gorm:"column:order_id;" json:"order_id" Order *Order gorm:"foreignKey:OrderId;references:ID" json:"company_user" }

func (o *OrderShipping) TableName() string { return "order_shippings" }

func (o OrderShipping) FindAll(db gorm.DB) (list []*OrderShipping) {

list = make([]OrderShipping, 0) db.Debug().Model(&OrderShipping{}).Preload("Order", func(tx gorm.DB) *gorm.DB { return tx.Preload("User") }).Find(&list)

return list }

func Test_CRUD(t *testing.T) { db, err := gorm.Open(mysql.New(mysql.Config{ DSN: "root:123456@tcp(127.0.0.1:3306)/gozero?charset=utf8mb4&parseTime=True&loc=Local", // DSN data source name DefaultStringSize: 256, // string 类型字段的默认长度 DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 DontSupportRenameColumn: true, // 用 change 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置 }), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}) if err != nil { panic(err) }

list := new(OrderShipping).FindAll(db)

t.Log(list[0]) // &{1 notes.... 1 0xc000406cf0} t.Log(list[0].Order) // &{1 123456 1 0xc0001e9680} t.Log(list[0].Order.User.Name) // tom

// INSERT INTO users (id, name) VALUES (1, 'tom'); // INSERT INTO orders (id, serial_no, user_id) VALUES (1, '123456', 1); // INSERT INTO order_shippings (id, note, order_id) VALUES (1, 'notes....', 1);

} ```

感谢,你的代码解决我的问题了

Comment From: wxy2077

感谢,你的代码解决我的问题了

哈哈哈,那挺好的,gorm基础使用我写了一些例子,仓库在这里,你可以参考参考。

https://github.com/wxy2077/gorm-example