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.autoCreateTimeyautoUpdateTime: 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.