💻 Exemplos: Capítulo 08: Migrações de Banco de Dados e Versionamento
Especialização em Backend com Go & Gin • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 01 — AutoMigrate de Múltiplos Modelos
package main
import (
"log"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Produto struct {
gorm.Model
Nome string
SKU string `gorm:"uniqueIndex"`
Preco float64
}
type Cliente struct {
gorm.Model
Nome string
}
type Pedido struct {
gorm.Model
ClienteID uint
Valor float64
}
func main() {
db, err := gorm.Open(sqlite.Open("app.db"), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// AutoMigrate é aditivo: cria tabelas/colunas/índices que faltam,
// mas nunca altera tipo nem remove coluna existente.
if err := db.AutoMigrate(&Produto{}, &Cliente{}, &Pedido{}); err != nil {
log.Fatal(err)
}
log.Println("AutoMigrate sincronizou 3 tabelas.")
}
💻 Código de Exemplo 02 — Migração Versionada com golang-migrate
package main
import (
"database/sql"
"log"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/sqlite3"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
// ExecutarMigracoesSQL aplica, em ordem, apenas os arquivos .up.sql
// ainda não registrados na tabela de controle schema_migrations.
func ExecutarMigracoesSQL(dbSQL *sql.DB) error {
driver, err := sqlite3.WithInstance(dbSQL, &sqlite3.Config{})
if err != nil {
return err
}
m, err := migrate.NewWithDatabaseInstance("file://migrations", "sqlite3", driver)
if err != nil {
return err
}
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
return err
}
log.Println("migrações aplicadas com sucesso")
return nil
}
// migrations/000001_criar_tabela_usuarios.up.sql
// CREATE TABLE usuarios (id SERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL);
//
// migrations/000001_criar_tabela_usuarios.down.sql
// DROP TABLE usuarios;
💻 Código de Exemplo 03 — Seed Idempotente e Verificação de Drift
package main
import (
"log"
"gorm.io/gorm"
)
type Usuario struct {
gorm.Model
Email string `gorm:"uniqueIndex"`
Papel string
}
// SeedDadosIniciais só insere o admin padrão se a tabela estiver vazia,
// podendo rodar em todo boot sem risco de duplicar registros.
func SeedDadosIniciais(db *gorm.DB) error {
var count int64
db.Model(&Usuario{}).Count(&count)
if count > 0 {
return nil
}
admin := Usuario{Email: "admin@app.com", Papel: "ADMIN"}
return db.Create(&admin).Error
}
// VerificarSchemaVersion compara a versão esperada (fixada no código
// versionado) com a aplicada no banco, usada como gate em pipelines CI/CD.
func VerificarSchemaVersion(versaoEsperada, versaoAtual uint) bool {
if versaoAtual != versaoEsperada {
log.Printf("ERRO: drift de schema! esperado=%d atual=%d", versaoEsperada, versaoAtual)
return false
}
return true
}