SpikeMe
← Comparativos
Go

GORM vs sqlx

atualizado em 14 de julho de 2026

GORM vs sqlx

GORM e sqlx comparados como ORM completo (associações, migrações, hooks) vs extensão fina sobre database/sql (SQL explícito, scan em struct), pesando controle contra conveniência, overhead, risco de N+1 e curva de aprendizado em Go.

FatoGORMsqlx
Versão atual1.31.21.4.0
LicençaMITMIT
CritérioGORMsqlx
AbstraçãoORM completo com API fluente sobre o bancoextensão fina sobre database/sql, SQL na mão
Como você escreve queriesmétodos encadeados (Where, Preload, Joins)SQL literal com scan automático em struct
Associaçõeshas one/has many/many2many declarados por structsem associações — você monta os JOINs manualmente
MigraçõesAutoMigrate cria e altera tabelas a partir das structssem migrações — use SQL ou uma ferramenta dedicada
Hooks e callbacksBeforeCreate/AfterUpdate e afins no ciclo de vidanenhum — a lógica fica explícita no seu código
Risco de N+1possível se esquecer Preload; mitigável com eager loadingimprovável — cada query é visível e intencional
Curva e overheadcurva maior, reflexão e abstração custam algum overheadcurva curta se você já sabe SQL, overhead mínimo

Contexto

GORM e sqlx ocupam extremos opostos do espectro de acesso a dados em Go, e escolher entre eles é escolher quanta abstração você quer entre o seu código e o SQL que roda no banco. GORM é um ORM completo: você descreve suas tabelas como structs Go, declara associações com tags, e a biblioteca gera o SQL, cuida das migrações, dispara hooks de ciclo de vida e resolve o carregamento de relações por você. sqlx faz o oposto por design — é uma extensão fina sobre a database/sql da biblioteca padrão. Ela não esconde o SQL; ela só remove o trabalho chato de mapear colunas em campos de struct. Você escreve a query, ela faz o scan do resultado direto no seu tipo.

A tensão central é controle versus conveniência. Com GORM, um db.Create(&user) pode inserir o usuário e suas associações numa cadeia de callbacks que você não vê — ótimo para velocidade, menos óbvio quando precisa depurar performance. Com sqlx, toda query que atinge o banco está escrita no seu código, visível e intencional; o custo é que tudo é responsabilidade sua, das migrações aos JOINs. Nenhum dos dois é "melhor": eles otimizam variáveis diferentes. GORM otimiza a velocidade de escrever CRUD e navegar relações. sqlx otimiza a transparência e o overhead mínimo sobre a stack padrão do Go.

Quando escolher GORM

GORM é a escolha certa quando o volume de CRUD é alto e as relações entre entidades são o coração do domínio. Declarar associações uma vez na struct e depois carregá-las com Preload economiza um trabalho real de SQL repetitivo, e AutoMigrate mantém o schema em sincronia com os modelos sem você escrever DDL à mão. Hooks como BeforeCreate centralizam regras de ciclo de vida (gerar um UUID, normalizar um campo) no modelo, em vez de espalhá-las por cada ponto de inserção.

package main

import (
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type User struct {
	ID    uint   `gorm:"primaryKey"`
	Name  string
	Email string `gorm:"uniqueIndex"`
	Posts []Post // has many
}

type Post struct {
	ID     uint `gorm:"primaryKey"`
	Title  string
	UserID uint
}

func main() {
	db, err := gorm.Open(postgres.Open("postgres://localhost/app"), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	db.AutoMigrate(&User{}, &Post{}) // cria/altera as tabelas a partir das structs

	db.Create(&User{Name: "Ada", Email: "ada@example.com"})

	var users []User
	// Preload evita o N+1: uma query para users, outra para todos os posts
	db.Preload("Posts").Where("name = ?", "Ada").Find(&users)
}

O exemplo mostra o ganho e o risco no mesmo lugar. O ganho: associação declarada, migração automática e carregamento das relações em duas queries com um único Preload. O risco: se você iterar users e acessar user.Posts sem o Preload, GORM pode disparar uma query por usuário — o clássico problema N+1, que fica escondido justamente porque a abstração torna a busca de relações barata de escrever. GORM tem as ferramentas para evitá-lo (Preload, Joins), mas você precisa lembrar de usá-las. Some a isso o overhead de reflexão e a curva mais longa para dominar convenções e a API fluente, e o perfil fica claro: GORM troca um pouco de transparência e performance por muita velocidade de desenvolvimento em domínios ricos em relações.

Quando escolher sqlx

sqlx é a escolha certa quando você quer que o SQL seja explícito e o overhead seja mínimo. Se o time já pensa em SQL, sqlx quase não adiciona conceitos novos: você usa a database/sql de sempre, com o bônus de Get e Select fazerem o scan de linhas direto em structs via tags db. Cada query que toca o banco está no seu código, então revisar performance é ler o que está escrito — não existe query implícita para caçar. Isso torna o N+1 improvável por construção: relações só carregam quando você escreve o JOIN ou a segunda consulta.

package main

import (
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
)

type User struct {
	ID    int    `db:"id"`
	Name  string `db:"name"`
	Email string `db:"email"`
}

func main() {
	db, err := sqlx.Connect("postgres", "postgres://localhost/app")
	if err != nil {
		panic(err)
	}

	_, err = db.Exec(
		`INSERT INTO users (name, email) VALUES ($1, $2)`,
		"Ada", "ada@example.com",
	)
	if err != nil {
		panic(err)
	}

	var users []User
	// SQL explícito; Select faz o scan de cada linha em User via tags db
	err = db.Select(&users, `SELECT id, name, email FROM users WHERE name = $1`, "Ada")
	if err != nil {
		panic(err)
	}
}

A diferença de filosofia salta aos olhos: não há AutoMigrate (a tabela users precisa existir, criada por SQL ou por uma ferramenta de migração à parte), não há associações e não há hooks. Em troca, você tem previsibilidade total — o SQL que você lê é o SQL que roda, o overhead sobre a database/sql é desprezível, e o modelo mental é curto se você já conhece SQL. O custo aparece quando o domínio tem muitas relações: montar JOINs, mapear resultados aninhados e versionar migrações à mão vira trabalho repetitivo que um ORM automatizaria. sqlx assume que esse trabalho explícito é um preço justo pela transparência.

Veredito

A decisão gira em torno de uma pergunta sobre a forma do seu domínio e a cultura do seu time. Se a aplicação é rica em relações, o CRUD é volumoso e você quer velocidade de desenvolvimento com associações declarativas, migrações automáticas e hooks, GORM se paga — desde que a equipe internalize o hábito de usar Preload/Joins para não cair em N+1 e aceite algum overhead de abstração. É o caminho mais produtivo para domínios densos em entidades relacionadas.

Se o time pensa naturalmente em SQL, valoriza previsibilidade e quer o menor overhead possível sobre a biblioteca padrão, sqlx é a base mais honesta. Cada query é visível, o modelo mental é curto e não há mágica para depurar quando a performance importa — ao custo de escrever migrações, JOINs e mapeamentos à mão. Uma regra prática: quanto mais o gargalo do seu produto for a evolução do schema e a navegação de relações, mais GORM ajuda; quanto mais for o controle fino sobre queries e latência, mais sqlx recompensa. Escolha pela natureza do trabalho, não pela reputação de ORMs.

GORM

Escolha GORM se…

Escolha GORM quando produtividade em CRUD, associações declarativas, migrações automáticas e hooks de ciclo de vida valem mais que controle total sobre cada SQL.

sqlx

Escolha sqlx se…

Escolha sqlx quando quer SQL explícito e previsível, overhead mínimo sobre database/sql e o scan automático em struct sem esconder nenhuma query.

Este comparativo é genérico: a resposta certa depende das versões, do time e do que já existe no seu projeto.

Gere o spike para a SUA stack

Compartilhar

XLinkedIn