Especialização em Backend com Go & Gin • Demonstrações Práticas Compiláveis


💻 Código de Exemplo 01 — Has Many com Preload (sem N+1)

package main

import (
	"log"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type Cliente struct {
	gorm.Model
	Nome    string
	Pedidos []Pedido `gorm:"foreignKey:ClienteID"`
}

type Pedido struct {
	gorm.Model
	ClienteID uint
	Status    string
	Valor     float64
}

func main() {
	db, _ := gorm.Open(sqlite.Open("app.db"), &gorm.Config{})
	db.AutoMigrate(&Cliente{}, &Pedido{})

	c := Cliente{Nome: "Ana Souza"}
	db.Create(&c)
	db.Create(&Pedido{ClienteID: c.ID, Status: "PAGO", Valor: 150.0})
	db.Create(&Pedido{ClienteID: c.ID, Status: "PENDENTE", Valor: 80.0})

	// Duas queries no total (clientes + pedidos via IN), não uma por cliente.
	var clientes []Cliente
	db.Preload("Pedidos").Find(&clientes)
	for _, cli := range clientes {
		log.Printf("cliente=%s pedidos=%d", cli.Nome, len(cli.Pedidos))
	}
}

💻 Código de Exemplo 02 — Preload Condicional e Many-to-Many

package main

import "gorm.io/gorm"

type Usuario struct {
	gorm.Model
	Nome   string
	Regras []Regra `gorm:"many2many:usuario_regras;"`
}

type Regra struct {
	gorm.Model
	Nome string
}

// BuscarPedidosPagos usa Preload com condição: só traz pedidos pagos,
// mas mantém o cliente no resultado mesmo sem nenhum pedido pago.
func BuscarPedidosPagos(db *gorm.DB, clienteID uint) (*Cliente, error) {
	var cliente Cliente
	err := db.Preload("Pedidos", "status = ?", "PAGO").First(&cliente, clienteID).Error
	return &cliente, err
}

// AtribuirRegra usa a API de Association para gerenciar a tabela de
// junção many2many sem escrever SQL manual de INSERT na tabela pivot.
func AtribuirRegra(db *gorm.DB, usuario *Usuario, regra *Regra) error {
	return db.Model(usuario).Association("Regras").Append(regra)
}

💻 Código de Exemplo 03 — Preload Aninhado com Dot Notation

package main

import "gorm.io/gorm"

type ItemPedido struct {
	gorm.Model
	PedidoID uint
	Produto  string
	Qtde     int
}

// BuscarHistoricoCompleto carrega três níveis em uma única chamada:
// Cliente -> Pedidos -> Itens, evitando N+1 em cascata.
func BuscarHistoricoCompleto(db *gorm.DB, clienteID uint) (*Cliente, error) {
	var cliente Cliente
	err := db.Preload("Pedidos.Itens").First(&cliente, clienteID).Error
	return &cliente, err
}