GORM: Guía Esencial para ORM en Go

Introducción a GORM

GORM es un ORM (Object-Relational Mapping) completo para Go que simplifica la interacción con bases de datos. Ofrece características avanzadas como:

  • Soporte completo para asociaciones (Has One, Has Many, Belongs To, Many To Many, polimorfismo, herencia de tablas simples).
  • Métodos de gancho (hooks) en operaciones Create, Save, Update, Delete y Find.
  • Precarga con Preload y Joins.
  • Transacciones, transacciones anidadas, Save Points y Rollback.
  • Contexto, modo de precompilación y modo DryRun.
  • Inserción por lotes, FindInBatches, uso de Mapas para operaciones CRUD.
  • Constructor de SQL, Upsert, bloqueo de base de datos, hints de optimizador/índice/comentarios, parámetros con nombre, subconsultas.
  • Claves primarias compuestas, índices y restricciones.
  • Migración de datos e instanciación de tablas.
  • Logger personalizable.
  • API de plugins extensible: Database Resolver (bases de datos múltiples, separación lectura/escritura), Prometheus.
  • Compatibilidad con MySQL, PostgreSQL, SQLite, SQL Server y TiDB.

Instalación y Configuración

Para instalar GORM y un controlador de base de datos, use los siguientes comandos:

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql  // Para MySQL
go get -u gorm.io/driver/postgres  // Para PostgreSQL
go get -u gorm.io/driver/sqlite  // Para SQLite

Ejemplo básico de conexión y operaciones CRUD:

package main

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/schema"
)

type Item struct {
    gorm.Model
    Code  string
    Price uint
}

func main() {
    connectionString := "usuario:contraseña@tcp(host:puerto)/base_datos?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(connectionString), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{
            SingularTable: true,
        },
    })
    if err != nil {
        panic("Error al conectar a la base de datos")
    }

    db.AutoMigrate(&Item{})

    // Insertar un registro
    result := db.Create(&Item{Code: "A01", Price: 150})
    fmt.Printf("Filas afectadas: %d\n", result.RowsAffected)

    // Consultar por clave primaria
    var item Item
    result = db.First(&item, 1)
    fmt.Printf("Consulta: %v, Filas: %d\n", item, result.RowsAffected)

    // Actualizar un campo
    result = db.Model(&item).Update("Price", 250)
    fmt.Printf("Actualización - Filas: %d\n", result.RowsAffected)

    // Eliminación lógica (requiere campo DeletedAt)
    result = db.Delete(&item, 1)
    fmt.Printf("Eliminación - Filas: %d\n", result.RowsAffected)
}

Operaciones CRUD Avanzadas

Inserción de Datos

Se pueden insertar registros individuales o por lotes, especificando campos o usando mapas. Ejemplo:

type Usuario struct {
    ID       uint64    `gorm:"primaryKey;autoIncrement"`
    Nombre   string    `gorm:"size:50;not null"`
    Edad     uint8     `gorm:"not null"`
    CreatedAt time.Time
    UpdatedAt time.Time
}

// Inserción simple
nuevoUsuario := Usuario{Nombre: "Elena", Edad: 28}
db.Create(&nuevoUsuario)

// Inserción por lotes
usuarios := []Usuario{{Nombre: "Luis"}, {Nombre: "Sofía"}}
db.Create(&usuarios)

// Uso de mapa para inserción
db.Model(&Usuario{}).Create(map[string]interface{}{
    "Nombre": "Carlos", "Edad": 35,
})

Para inserciones con control de conflictos, se puede usar clause.OnConflict.

Actualización de Datos

Se pueden actualizar uno o múltiples campos, con condiciones específicas. Ejemplo:

var user Usuario
db.First(&user, 10)

// Actualizar un solo campo
db.Model(&user).Update("Nombre", "María")

// Actualizar múltiples campos usando mapa
db.Model(&user).Updates(map[string]interface{}{
    "Nombre": "Ana", "Edad": 30,
})

// Actualización por lotes con condición
db.Model(Usuario{}).Where("Edad > ?", 20).Update("Nombre", "Actualizado")

Se pueden usar expresiones SQL para cálculos dinámicos en actualizaciones.

Eliminación de Datos

Se soporta eliminación física y lógica (con campo DeletedAt). Ejemplo:

// Eliminación por ID
db.Delete(&Usuario{}, 5)

// Eliminación por condición
db.Where("Nombre = ?", "Pedro").Delete(&Usuario{})

// Eliminación con retorno de datos (si la base de datos lo soporta)
var eliminados []Usuario
db.Clauses(clause.Returning{}).Where("Edad < ?", 18).Delete(&eliminados)

Consultas Básicas y Avanzadas

GORM ofrece métodos flexibles para consultas, incluyendo filtrado, ordenamiento, paginación y subconsultas. Ejemplo:

// Consulta con condiciones
var resultado Usuario
db.Where("Nombre = ?", "Elena").First(&resultado)

// Consulta con múltiples condiciones usando mapa
db.Where(map[string]interface{}{"Nombre": "Luis", "Edad": 25}).Find(&resultado)

// Subconsulta
subQuery := db.Model(&Usuario{}).Select("AVG(Edad)")
db.Where("Edad > (?)", subQuery).Find(&resultado)

// Consulta con bloqueo
db.Clauses(clause.Locking{Strength: "UPDATE"}).Where("ID = ?", 1).First(&resultado)

Se pueden usar scopes para reutilizar consultas comunes.

Definición de Modelos y Asociaciones

Convenciones y Etiquetas de Campo

GORM sigue convenciones como usar ID como clave primaria y nombres de tablas en plural. Las etiquetas permiten personalizar el mapeo:

  • column: Especifica el nombre de la columna.
  • type: Define el tipo de dato.
  • primaryKey: Marca la clave primaria.
  • unique: Establece restricción de unicidad.
  • default: Valor por defecto.
  • autoCreateTime y autoUpdateTime: Para campos de timesatmp.

Asociaciones entre Modelos

GORM soporta varios tipos de relaciones:

  • Belongs To: Relación uno-a-uno inversa.
  • Has One: Relación uno-a-uno directa.
  • Has Many: Relación uno-a-muchos.
  • Many To Many: Relación muchos-a-muchos con tabla intermedia.

Ejemplo de relación Belongs To:

type Empresa struct {
    ID     uint64
    Nombre string
}

type Empleado struct {
    ID         uint64
    Nombre     string
    EmpresaID  uint64
    Empresa    Empresa `gorm:"foreignKey:EmpresaID"`
}

Para consultas con ascoiaciones, se puede usar Preload o Joins para cargar datos relacionados.

Consultas SQL Nativas y Características Avanzadas

GORM permite ejecutar SQL nativo directamente. Ejemplo:

// Ejecutar SQL nativo
var usuarios []Usuario
db.Raw("SELECT * FROM usuarios WHERE edad > ?", 25).Scan(&usuarios)

// Modo DryRun para previsualizar SQL
stmt := db.Session(&gorm.Session{DryRun: true}).First(&Usuario{}, 1).Statement
fmt.Println("SQL generado:", stmt.SQL.String())

Características adicionales incluyen:

  • Scopes: Para componer consultas reutilizables.
  • Hooks: Para ejecutar lógica antes o después de operaciones.
  • Transacciones: Con soporte para anidamiento y puntos de guardado.

Para más detalles, consulte la documentación oficial de GORM.

Etiquetas: GORM Go ORM MySQL PostgreSQL

Publicado el 6-18 22:05