💻 Exemplos: Capítulo 07: Associações no GORM (Has Many, Belongs To) e Preloading
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
}