Acceso a Datos en Go: database/sql como Base
Go mantiene el acceso a la base de datos explícito.
Busca en todas las páginas de la documentación
Go mantiene el acceso a la base de datos explícito.
El paquete database/sql de la librería estándar define cómo las aplicaciones hablan con las bases de datos SQL, y drivers separados (Postgres, MySQL, SQLite y otros) implementan el protocolo real.
Los ORM como GORM y las utilidades como sqlx se asientan sobre esa misma base; no reemplazan el pool, las transacciones o las APIs conscientes del contexto.
Conceptos Básicos de Bases de Datos recopila fragmentos ejecutables; los artículos hermanos cubren pooling, transacciones, migraciones, caché y convenciones de equipo.
database/sql es una API pequeña y agnóstica de drivers para pooling de conexiones, consultas, transacciones y sentencias preparadas. Tu código importa un driver con una importación vacía y llama a sql.Open.database/sql te permite ser portable entre drivers, testeable con fakes y alineado con cómo las pilas HTTP y gRPC de Go propagan context.Context a las llamadas de almacenamiento.sql.Null* son verbosos; el comportamiento del driver difiere para la cancelación y el caché de sentencias preparadas; los riesgos N+1 se mueven a las capas ORM si no tienes cuidado.Imagina tu servicio como capas: manejador HTTP, servicio de dominio, repositorio de almacenamiento, luego *sql.DB.
El manejador analiza la solicitud y pasa r.Context() hacia abajo.
El repositorio posee las cadenas SQL (o SQL generado) y mapea las filas a tipos de dominio.
*sql.DB es un pool de conexiones, no un solo socket.
sql.Open("postgres", dsn) registra el nombre del driver y devuelve un manejador de pool.
La primera conexión real a menudo aparece en Ping, Query o Exec.
Los drivers viven en módulos separados:
import (
"database/sql"
_ "github.com/jackc/pgx/v5/stdlib" // Postgres
)La importación vacía ejecuta la función init() del driver para que sql.Open pueda resolver "pgx".
Cambiar de base de datos significa cambiar la importación del driver y el DSN, no reescribir los tipos de la aplicación si los repositorios ocultan los detalles de SQL.
El camino de lectura idiomático:
func GetUser(ctx context.Context, db *sql.DB, id int64) (User, error) {
var u User
err := db.QueryRowContext(ctx,
`SELECT id, email FROM users WHERE id = $1`, id,
).Scan(&u.ID, &u.Email)
if errors.Is(err, sql.ErrNoRows) {
return User{}, ErrNotFound
}
return u, err
}QueryRowContext espera como máximo una fila.
Scan copia los valores de las columnas en variables Go e informa inmediatamente las discrepancias de tipo.
Las lecturas de múltiples filas usan QueryContext, iteran rows.Next(), y siempre rows.Close().
Las escrituras usan ExecContext; los trabajos por lotes pueden usar transacciones a través de BeginTx.
| Capa | Responsabilidad | Paquete Típico |
|---|---|---|
| Manejador | Autenticación, enlace, códigos de estado | chi, gin, echo |
| Servicio | Reglas de negocio, orquestación | internal/order |
| Repositorio | SQL, mapeo, transacciones | internal/storage |
| Pool | Conexiones, límites, salud | database/sql |
| Driver | Protocolo de red, cancelación | pgx, go-sql-driver/mysql |
Los frameworks (chi, gin, echo) no cambian el contrato de almacenamiento: los repositorios aún aceptan ctx y *sql.DB (o una interfaz que los envuelva).
Los servicios de google.golang.org/grpc pasan el contexto RPC a los métodos del repositorio de la misma manera que lo hacen los manejadores HTTP.
Los servicios de producción combinan la configuración del pool con el contexto por consulta:
SetMaxOpenConns limita las conexiones simultáneas a la base de datos.SetMaxIdleConns mantiene conexiones calientes para tráfico intermitente.SetConnMaxLifetime rota las conexiones detrás de los balanceadores de carga.QueryContext vincula el trabajo a la cancelación del cliente y a los plazos de servicio.Los ORM (GORM) generan SQL y gestionan asociaciones; sqlx añade utilidades de escaneo de estructuras manteniéndose cerca del SQL puro.
Los equipos a menudo eligen:
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
database/sql puro | Control total, cero magia | Más código repetitivo | SQL crítico para el rendimiento |
| sqlx | Escaneo ergonómico | Aún esquema manual | Servicios con SQL moderado |
| GORM | Migraciones, hooks, asociaciones | Consultas ocultas, etiquetas mágicas | APIs de administración con mucho CRUD |
| sqlc / generación de código | Verificaciones de consultas en tiempo de compilación | Requiere pipeline de compilación | Equipos grandes con esquemas estables |
La observabilidad pertenece al límite del repositorio: registra el nombre de la consulta, la latencia y el plazo restante del ctx; expone estadísticas del pool (db.Stats()) a las métricas.
Los reconciliadores de controller-runtime deben pasar el contexto de reconciliación a las llamadas al repositorio para que el trabajo de la base de datos se detenga cuando el gestor se apague.
sql.Open conecta inmediatamente" - Asigna un pool; los errores a menudo aparecen en el primer uso. Siempre ejecuta PingContext al inicio.*sql.DB global es un anti-patrón" - Un solo pool por proceso es normal; el anti-patrón es el cableado oculto de init() sin pruebas.sql.Rows" - Devuelve tipos de dominio o rebanadas; mantén Rows dentro del repositorio.Prepare de forma generalizada.Los drivers se registran a sí mismos en init().
Sin la importación, sql.Open falla con "unknown driver".
Mantén el SQL en los repositorios para que los manejadores permanezcan delgados y las pruebas puedan simular el almacenamiento.
Mapea a un centinela de dominio ErrNotFound.
No expongas sql.ErrNoRows a las capas HTTP sin traducción.
No, un *sql.DB por proceso (o por base de datos lógica) es lo estándar.
Las solicitudes toman prestadas conexiones del pool.
GORM moderno soporta WithContext(ctx).
Aún verifica el SQL generado y la configuración del pool en producción.
Usa interfaces en el límite del servicio, sqlmock, o contenedores efímeros para pruebas de integración.
Las pruebas unitarias no deberían necesitar Docker para las reglas de negocio.
Esta sección se enfoca en SQL a través de database/sql.
Los almacenes de documentos usan diferentes SDK de cliente con sus propios patrones de pooling.
Cada proceso de servicio posee su pool.
Compartir un pool entre servicios acopla los dominios de fallo.
pgx ofrece una API nativa y un driver database/sql a través de stdlib.
La mayoría de las aplicaciones usan la capa de compatibilidad de la librería estándar.
Cuando el cambio de esquema, las asociaciones y los hooks de borrado lógico dominan el tiempo del desarrollador más que el ajuste de consultas.
Reevalúa cuando el rendimiento se convierta en el cuello de botella.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado GC de Green Tea, go fix modernizers - verifica el parche en la compilación), chi (última - verifica en la compilación), gin (última - verifica en la compilación), echo (última - verifica en la compilación), google.golang.org/grpc (última - verifica en la compilación), sigs.k8s.io/controller-runtime (última - verifica en la compilación), kubebuilder (última - verifica en la compilación), tinygo (última - verifica objetivos de placa en la compilación), wazero (última - verifica en la compilación) y golangci-lint (última - verifica el conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 16 jul 2026