Gorm 预加载 m2m 关系

Gorm preload m2m relation

我想预加载 M2Mgorm 的关系,它不会使用 Preload 函数填充切片。

这是 sql 架构

create table project (
  id int generated by default as identity,
  name varchar(64) not null,
  slug varchar(64) not null,
  image_url text,
  constraint project_pk primary key (id),
  constraint project_unq unique (name, slug)
);

create table donation (
  id int generated by default as identity,
  user_id varchar(36) not null,
  total_amount numeric(7, 2) not null,
  currency varchar(3) not null,
  constraint donation_pk primary key (id)
);

create table donation_detail (
  donation_id int not null,
  project_id int not null,
  amount numeric(7, 2) not null,
  primary key (donation_id, project_id)
);

这些是我的 gorm 模型

type Donation struct {
    ID              uint64            `json:"id" gorm:"primarykey"`
    UserID          string            `json:"user_id"`
    PaypalOrderID   string            `json:"paypal_order_id"`
    TotalAmount     float64           `json:"total_amount"`
    Currency        string            `json:"currency"`
    DonationDetails []*DonationDetail `json:"donation_details" gorm:"many2many:donation_detail;"`
}

type Project struct {
    ID          uint64  `json:"id" gorm:"primarykey"`
    Name        string  `json:"name"`
    Slug        string  `json:"slug"`
    ImageURL    string  `json:"image_url"`
}

type DonationDetail struct {
    DonationID uint64  `json:"donation_id" gorm:"primaryKey"`
    ProjectID  uint64  `json:"project_id" gorm:"primaryKey"`
    Amount     float64 `json:"amount"`
    Project    Project
}

我想要 return 捐赠及其包含项目信息的详细信息。像这样:

{
  "id": 1,
  "user_id": "e14a98d1-0c4a-45c3-b748-5ac6ba733b99",
  "paypal_order_id": "6SR91505YA360210R",
  "total_amount": 10000,
  "currency": "EUR",
  "donation_details": [
    {
      "donation_id": 1,
      "project_id": 1,
      "amount": 3000,
      "project": {
        "project_id": 1,
        "name": "First Project",
        "slug": "first_project",
        "image_url": "img1.jpg"
      }
    },
    {
      "donation_id": 2,
      "project_id": 2,
      "amount": 7000,
      "project": {
        "project_id": 2,
        "name": "Second Project",
        "slug": "second_project",
        "image_url": "img2.jpg"
      }
    }
  ]
}

我正在尝试使用此代码预加载 DonationDetails 切片,但它最终变成了空切片:

func List(donationID string) (*Donation, error) {
  var d *Donation

  if err := db.Debug().Preload("DonationDetails").First(&d, donationID).Error; err != nil {
    return nil, fmt.Errorf("could not list donation details: %w", err)
  }

  return d, nil
}

sql 调试的输出显然打印了这 3 个查询:

[rows:3] SELECT * FROM "donation_detail" WHERE "donation_detail"."donation_id" = 1
[rows:0] SELECT * FROM "donation_detail" WHERE ("donation_detail"."donation_id","donation_detail"."project_id") IN (NULL)
[rows:1] SELECT * FROM "donation" WHERE "donation"."id" = '1' ORDER BY "donation"."id" LIMIT 1

注意:我在 .sql 文件中手动执行迁移,而不是使用 Gorm Automigrate 功能。 也许我遗漏了一些结构标签注释,或者我完全误解了它是如何工作的。

PS:我只是想做这样的事情

SELECT * FROM donation d 
 LEFT JOIN donation_detail dd on d.id = dd.donation_id 
 LEFT JOIN project p on p.id = dd.project_id

使用 gorm 执行此操作的理想方法是什么?有人必须知道。我只是想做一个愚蠢的加入,这有多难。

有几件事需要尝试和解决:

您可能不需要 many2many 属性来加载 DonationDetail 切片,因为它们只能使用 DonationID 加载。如果你有foreign key,你可以这样添加:

type Donation struct {
    ID              uint64            `json:"id" gorm:"primarykey"`
    UserID          string            `json:"user_id"`
    PaypalOrderID   string            `json:"paypal_order_id"`
    TotalAmount     float64           `json:"total_amount"`
    Currency        string            `json:"currency"`
    DonationDetails []*DonationDetail `json:"donation_details" gorm:"foreignKey:DonationID"`
} 

您不需要 First 方法中的 &d,因为 d 已经是一个指针。但是,要加载 Project 数据,您还需要预加载。

func List(donationID string) (*Donation, error) {
  var d *Donation

  if err := db.Debug().Preload("DonationDetails.Project").First(d, donationID).Error; err != nil {
    return nil, fmt.Errorf("could not list donation details: %w", err)
  }

  return d, nil
}