1
resposta

[Dúvida] Projeto usando GORM problema N + 1

Estou estudando GoLang, e para tal tenho implementado um projeto particular, sabemos que os projetos reais trazem problemas mais complexos principalmente no quesito relacionamento de bancos de dados. Gostaria de uma explicação sobre como evitar o famoso problema N + 1 quando temos relacionamentos um para muitos e muitos para muitos

Partindo do contexto abaixo gostaria de uma ajuda sobre como realizar as Querys para evitar o referido problema

O cliente solicitou a implementação de uma API para controlar as Arenas de BeachTennis aonde uma Arena pode ter vários atletas e os atletas podem jogar em várias arenas

type Arena struct {
  gorm.Model
  Quadras         int     `json:"quadras,default=1"`   // Default value included
  Nome             string  `json:"nome,default=Arena"` // Default value included
  Email            string  `json:"email"`              // Required field
  Telefone         string  `json:"telefone"`          // Required field
  Endereco         string  `json:"endereco"`          // Required field
  HorarioFuncionamento string  `json:"horario_funcionamento"` // Required field
  FuncionaFinalDeSemana bool   `json:"funciona_final_de_semana,default=false"` // Default value included
  Atletas          []*Atleta  `json:"atletas,omitempty"`               // Slice of Atleta pointers (omitempty for potential empty slice)
  Professores      []*Professor  `json:"professores,omitempty"`           // Slice of Professor pointers (omitempty for potential empty slice)
}

type Atleta struct {
  gorm.Model
  Nome      string  `json:"nome"` // Required field
  Email     string  `json:"email"` // Required field
  Telefone  string  `json:"telefone"` // Required field
  Endereco  string  `json:"endereco"` // Required field
}

type Professor struct {
  gorm.Model
  Nome      string  `json:"nome"` // Required field
  Email     string  `json:"email"` // Required field
  Telefone  string  `json:"telefone"` // Required field
  Endereco  string  `json:"endereco"` // Required field
  Especialidade string  `json:"especialidade"` // Required field
}

Preciso de um endpoint que retorne os dados da arena e também tragam os id´s dos atletas e dos professores, como ficaria a Query para evitar o problema de 1 + N

1 resposta

Oi Afrânio! Tudo bem?

O problema N + 1 ocorre quando você faz várias consultas ao banco de dados para buscar dados relacionados, o que pode ser ineficiente. No GORM, você pode evitar esse problema utilizando a função Preload para carregar os relacionamentos de uma só vez.

Aqui está um exemplo de como você pode fazer isso no seu caso:

package main

import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

type Arena struct {
    gorm.Model
    Quadras               int          `json:"quadras,default=1"`
    Nome                  string       `json:"nome,default=Arena"`
    Email                 string       `json:"email"`
    Telefone              string       `json:"telefone"`
    Endereco              string       `json:"endereco"`
    HorarioFuncionamento  string       `json:"horario_funcionamento"`
    FuncionaFinalDeSemana bool         `json:"funciona_final_de_semana,default=false"`
    Atletas               []*Atleta    `json:"atletas,omitempty"`
    Professores           []*Professor `json:"professores,omitempty"`
}

type Atleta struct {
    gorm.Model
    Nome     string `json:"nome"`
    Email    string `json:"email"`
    Telefone string `json:"telefone"`
    Endereco string `json:"endereco"`
}

type Professor struct {
    gorm.Model
    Nome          string `json:"nome"`
    Email         string `json:"email"`
    Telefone      string `json:"telefone"`
    Endereco      string `json:"endereco"`
    Especialidade string `json:"especialidade"`
}

func main() {
    // Inicializando o banco de dados (para fins de exemplo, usando SQLite)
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        fmt.Println("Erro ao conectar ao banco de dados:", err)
        return
    }

    // Migrando as tabelas
    db.AutoMigrate(&Arena{}, &Atleta{}, &Professor{})

    // Exemplo de como evitar o problema N + 1
    var arenas []Arena
    if err := db.Preload("Atletas").Preload("Professores").Find(&arenas).Error; err != nil {
        fmt.Println("Erro ao buscar arenas:", err)
        return
    }

    // Exibindo os dados para verificação
    for _, arena := range arenas {
        fmt.Printf("Arena: %s\n", arena.Nome)
        for _, atleta := range arena.Atletas {
            fmt.Printf("  Atleta: %s\n", atleta.Nome)
        }
        for _, professor := range arena.Professores {
            fmt.Printf("  Professor: %s\n", professor.Nome)
        }
    }
}

Neste exemplo, usamos Preload("Atletas") e Preload("Professores") para carregar os relacionamentos Atletas e Professores de uma só vez, evitando o problema N + 1. Assim, ao buscar as arenas, você já traz junto os dados dos atletas e professores associados.

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.