GORM (Go ORM) es una librería que sirve para el mapeo objeto relacional.
Características principales:
- ORM con todas las funciones Asociaciones (tiene uno, tiene muchos, pertenece a, muchos a muchos, polimorfismo, herencia de tabla única).
- Ganchos (antes/después de crear/guardar/actualizar/eliminar/buscar).
- Carga diligente con precarga, uniones.
- Transacciones, transacciones anidadas, punto de guardado, revertir al punto guardado.
- Contexto, modo de sentencia preparada, modo de ejecución en seco.
- Inserción por lotes, búsqueda en lotes, búsqueda/creación con mapa, CRUD con SQL Expr y valorador de contexto.
- Constructor de SQL, Upsert, bloqueo, sugerencias de optimizador/índice/comentario, argumento con nombre, subconsulta.
- Clave primaria compuesta, índices, restricciones.
- Migraciones automáticas.
- Registrador API de complemento extensible y flexible: Resolvedor de bases de datos (múltiples bases de datos, división de lectura/escritura) / Prometheus…
- Cada función incluye pruebas.
- Fácil de usar para desarrolladores.
Digamos que GORM es el Hibernate de los programadores Go.
Cómo empezar con GORM
Crearemos una API REST en Go que:
- Use GORM como ORM.
- Se conecte a MariaDB usando el driver de MySQL (compatible).
- Trabaje contra tu tabla persons, ya existente.
- Exporte endpoints:
-
POST /persons -
GET /persons -
GET /persons/:id -
PUT /persons/:id -
DELETE /persons/:id
-
1. Crearemos una carpeta y nos ubicaremos en ella.
$ mkdir gorm-mariadb-crud $ cd gorm-mariadb-crud
2. Inicializamos el módulo:
$ go mod init github.com/hironakamura/gorm-mariadb-crud
El directorio del proyecto debería quedar más o menos así:
gorm-mariadb-crud/ go.mod
3. Instalamos gorm y las dependencias necesarias:
$ go get -u gorm.io/gorm $ go get -u gorm.io/driver/mysql $ go get -u github.com/gin-gonic/gin
Con esto hemos instalado GORM, el driver de MySQL (compatible con MariaDB) y Gin framework. Herramientas necesarias para el proyecto.
Ahora el directorio del proyecto lucirá así:
gorm-mariadb-crud/ go.mod go.sum
Para el ejemplo usaremos una BD de MariaDB; GORM permite distintos SGBD para ello. Tenemos la tabla llamada ``persons`` con los siguientes campos:
+-----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+-------+ | PersonID | int(11) | YES | | NULL | | | LastName | varchar(255) | YES | | NULL | | | FirstName | varchar(255) | YES | | NULL | | | Address | varchar(255) | YES | | NULL | | | City | varchar(255) | YES | | NULL
GORM necesita una primary key. Vamos a asumir que el campo PersonID lo es.
4. Crearemos una carpeta para nuestro modelo y dentro de esta un archivo llamado person.go
models/person.go
package models type Person struct { PersonID uint `gorm:"primaryKey;column:PersonID"` LastName string `gorm:"column:LastName"` FirstName string `gorm:"column:FirstName"` Address string `gorm:"column:Address"` City string `gorm:"column:City"` } // TableName fuerza a GORM a usar el nombre exacto de la tabla func (Person) TableName() string { return "persons" }
Nuestra conexión a la BD de MariaDB será esta:
root:password@tcp(host:3306)/cursomariadb?charset=utf8mb4&parseTime=True&loc=Local
5. Creamos módulo de conexión:
database/database.go
package database import ( "log" "github.com/hironakamura/gorm-mariadb-crud/models" "gorm.io/driver/mysql" "gorm.io/gorm" ) var DB *gorm.DB func Connect() { // Ajusta estos valores a tu entorno dsn := "root:password@tcp(127.0.0.1:3306)/cursomariadb?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Error al conectar a MariaDB: ", err) } DB = db // Migración automática (opcional si ya tienes la tabla) if err := DB.AutoMigrate(&models.Person{}); err != nil { log.Fatal("Error en AutoMigrate: ", err) } }
5. En este punto usaremos Gin, específiamente el concepto de handlers. Crearemos una nueva carpeta y archivo:
handlers/person_handlers.go
package handlers import ( "net/http" "github.com/gin-gonic/gin" "github.com/hironakamura/gorm-mariadb-crud/database" "github.com/hironakamura/gorm-mariadb-crud/models" )
Dentro de este archivo tendremos los métodos de POST, GET, PUT y DELETE; métodos básicos de petición HTTP.
POST/persons
func CreatePerson(c *gin.Context) { var person models.Person if err := c.ShouldBindJSON(&person); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := database.DB.Create(&person).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al crear persona"}) return } c.JSON(http.StatusOK, person) }
GET/persons
func GetPersons(c *gin.Context) { var persons []models.Person if err := database.DB.Find(&persons).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al obtener personas"}) return } c.JSON(http.StatusOK, persons) }
GET/persons/:id
func GetPerson(c *gin.Context) { id := c.Param("id") var person models.Person if err := database.DB.First(&person, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Persona no encontrada"}) return } c.JSON(http.StatusOK, person) }
PUT/persons/:id
func UpdatePerson(c *gin.Context) { id := c.Param("id") var person models.Person // Buscar registro existente if err := database.DB.First(&person, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Persona no encontrada"}) return } // Leer datos nuevos var input models.Person if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Actualizar solo campos enviados if err := database.DB.Model(&person).Updates(input).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al actualizar persona"}) return } c.JSON(http.StatusOK, person) }
DELETE/persons/:id
func DeletePerson(c *gin.Context) { id := c.Param("id") var person models.Person if err := database.DB.First(&person, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Persona no encontrada"}) return } if err := database.DB.Delete(&person).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al eliminar persona"}) return } c.JSON(http.StatusOK, gin.H{"message": "Persona eliminada"}) }
Handler completo:
package handlers import ( "net/http" "github.com/gin-gonic/gin" "github.com/hironakamura/gorm-mariadb-crud/database" "github.com/hironakamura/gorm-mariadb-crud/models" ) func CreatePerson(c *gin.Context) { var person models.Person if err := c.ShouldBindJSON(&person); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := database.DB.Create(&person).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al crear persona"}) return } c.JSON(http.StatusOK, person) } func GetPersons(c *gin.Context) { var persons []models.Person if err := database.DB.Find(&persons).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al obtener personas"}) return } c.JSON(http.StatusOK, persons) } func GetPerson(c *gin.Context) { id := c.Param("id") var person models.Person if err := database.DB.First(&person, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Persona no encontrada"}) return } c.JSON(http.StatusOK, person) } func UpdatePerson(c *gin.Context) { id := c.Param("id") var person models.Person // Buscar registro existente if err := database.DB.First(&person, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Persona no encontrada"}) return } // Leer datos nuevos var input models.Person if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Actualizar solo campos enviados if err := database.DB.Model(&person).Updates(input).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al actualizar persona"}) return } c.JSON(http.StatusOK, person) } func DeletePerson(c *gin.Context) { id := c.Param("id") var person models.Person if err := database.DB.First(&person, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Persona no encontrada"}) return } if err := database.DB.Delete(&person).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al eliminar persona"}) return } c.JSON(http.StatusOK, gin.H{"message": "Persona eliminada"}) }
6. Creamos el archivo principal del proyecto.
main.go
package main import ( "github.com/gin-gonic/gin" "github.com/hironakamura/gorm-mariadb-crud/database" "github.com/hironakamura/gorm-mariadb-crud/handlers" ) func main() { // 1. Conectar a la base de datos database.Connect() // 2. Configurar router r := gin.Default() // 3. Definir rutas r.POST("/persons", handlers.CreatePerson) r.GET("/persons", handlers.GetPersons) r.GET("/persons/:id", handlers.GetPerson) r.PUT("/persons/:id", handlers.UpdatePerson) r.DELETE("/persons/:id", handlers.DeletePerson) // 4. Levantar servidor r.Run(":8080") }
7. Ejecutamos el proyecto:
$ go run ./...
Si todo va bien veremos algo como esto en la terminal:
2025/12/14 18:41:11 ←[32mC:/Users/HP/Documents/pruebasGo/gorm-mariadb-crud/database/database.go:25 ←[33mSLOW SQL >= 200ms ←[0m←[31;1m[202.655ms] ←[33m[rows:-]←[35m SELECT * FROM `persons` LIMIT 1←[0m 2025/12/14 18:41:13 ←[32mC:/Users/HP/Documents/pruebasGo/gorm-mariadb-crud/database/database.go:25 ←[33mSLOW SQL >= 200ms ←[0m←[31;1m[1795.554ms] ←[33m[rows:4]←[35m ALTER TABLE `persons` MODIFY COLUMN `LastName` longtext←[0m 2025/12/14 18:41:13 ←[32mC:/Users/HP/Documents/pruebasGo/gorm-mariadb-crud/database/database.go:25 ←[33mSLOW SQL >= 200ms ←[0m←[31;1m[906.920ms] ←[33m[rows:4]←[35m ALTER TABLE `persons` MODIFY COLUMN `FirstName` longtext←[0m 2025/12/14 18:41:14 ←[32mC:/Users/HP/Documents/pruebasGo/gorm-mariadb-crud/database/database.go:25 ←[33mSLOW SQL >= 200ms ←[0m←[31;1m[641.563ms] ←[33m[rows:4]←[35m ALTER TABLE `persons` MODIFY COLUMN `Address` longtext←[0m 2025/12/14 18:41:15 ←[32mC:/Users/HP/Documents/pruebasGo/gorm-mariadb-crud/database/database.go:25 ←[33mSLOW SQL >= 200ms ←[0m←[31;1m[547.782ms] ←[33m[rows:4]←[35m ALTER TABLE `persons` MODIFY COLUMN `City` longtext←[0m [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] POST /persons --> github.com/hironakamura/gorm-mariadb-crud/handlers.CreatePerson (3 handlers) [GIN-debug] GET /persons --> github.com/hironakamura/gorm-mariadb-crud/handlers.GetPersons (3 handlers) [GIN-debug] GET /persons/:id --> github.com/hironakamura/gorm-mariadb-crud/handlers.GetPerson (3 handlers) [GIN-debug] PUT /persons/:id --> github.com/hironakamura/gorm-mariadb-crud/handlers.UpdatePerson (3 handlers) [GIN-debug] DELETE /persons/:id --> github.com/hironakamura/gorm-mariadb-crud/handlers.DeletePerson (3 handlers) [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details. [GIN-debug] Listening and serving HTTP on :8080
Para probarlo podemos crear scripts con Powershell:
create_person.ps1
$body = @{ LastName = "Juan" FirstName = "Perez" Address = "123 Main St" City = "CDMX" } | ConvertTo-Json Invoke-RestMethod -Uri "http://localhost:8080/persons" ` -Method Post ` -Body $body ` -ContentType "application/json"
Ejecución:
$ .\create_person.ps1
get_all.ps1
Invoke-RestMethod -Uri "http://localhost:8080/persons" -Method Get
Ejecución:
$ .\get_all.ps1
get_one.ps1
Invoke-RestMethod -Uri "http://localhost:8080/persons/1" -Method Get
Ejecución:
$ .\get_one.ps1
update_person.ps1
$update = @{ LastName = "DoeUpdated" FirstName = "Juan" Address = "Nueva dirección" City = "Monterrey" } | ConvertTo-Json Invoke-RestMethod -Uri "http://localhost:8080/persons/1" ` -Method Put ` -Body $update ` -ContentType "application/json"
Ejecución:
$ .\update_person.ps1
delete_person.ps1
Invoke-RestMethod -Uri "http://localhost:8080/persons/1" -Method Delete
Ejecución:
$ .\delete_person.ps1
Estos 4 scripts de Powershell nos ayudan a probar nuestra aplicación API Rest con GORM.
¡Hemos aprendido a usar GORM!
GORM es una herramienta bastante útil a la hora de trabajar con BD y Go.
Enlaces:
https://gorm.io/https://medium.com/@feldyjudahk/database-connections-with-gorm-in-golang-opening-closing-and-connection-pooling-277043e1d568
https://medium.com/@itskenzylimon/getting-started-on-golang-gorm-af49381caf3f

Comentarios
Publicar un comentario