Quando desenvolvemos programas que vão trabalhar com dados, sem sombra de dúvida uma das coisas mais importantes é armazenar esses dados com segurança e robustez.
Nesse post, vou mostrar como conectar e executar um CRUD utilizando um banco de dados PostgreSQL.
Antes de começar a escrever código, vamos fazer download do drive do postgres para Go com o comando go get github.com/lib/pq.
Ok, agora podemos começar.
Para ficar um pouco mais próximo da realidade, vou separar todas as operações em funções, incluindo a conexão com o banco.
E por falar em conexão com o banco, vamos começar justamente por ela.
import (
"database/sql"
_ "github.com/lib/pq"
)
func OpenConn() (*sql.DB, error) {
db, err := sql.Open("postgres", "host=localhost port=5432 user=aprenda password=golang dbname=blog sslmode=disable")
if err != nil {
panic(err)
}
err = db.Ping()
return db, err
}
Essa função é bem simples, e sua única função é abrir uma conexão com o banco de dados e retorna-lá para que as outras funções do CRUD possam realizar suas transações.
Um ponto que vale a pena se atentar aqui é o underline antes do import do drive do postgres. Como nós não utilizamos o package de forma direta, esse underline é necessário para que o compilador do Go não gere um erro ao tentar fazer o build da aplicação.
Dando continuidade, vamos criar as funções para CRUD usando como base uma struct de categoria.
type Categoria struct {
ID int64
Nome string
}
Para começar gerir os dados, vamos iniciar com a função responsável por adicionar um novo registro no banco de dados.
func Insert(c Categoria) (id int64, err error) {
conn, err := db.OpenConn()
if err != nil {
return
}
defer conn.Close()
sql := `INSERT INTO categorias (nome) VALUES ($1) RETURNING id`
err = conn.QueryRow(sql, c.Nome).Scan(&id)
return id
}
Base para todas as funções que vamos escrever, iniciamos a função abrindo uma nova conexão com o banco de dados, e caso não haja nenhum erro, colocamos um defer para o fechamento da conexão.
Na sequência, preparamos o SQL que queremos executar com as variáveis que serão substituídas na função QueryRow. Utilizamos a função QueryRow pois nosso SQL irá retornar o ID que foi gerado após o insert.
Se a execução da nossa query não fosse retornar nada, o ideal seria utilizar a função Exec. Caso ela fosse retornar mais do que 1 linha, o melhor seria a função Query.
Agora que já conseguimos adicionar itens no nosso banco de dados, vamos criar duas funções, sendo a primeira para obter um registro especifico e a segundo para obter todos os registros da tabela.
// categoria especifica
func Get(id int64) (c Categoria, err error) {
conn, err := db.OpenConn()
if err != nil {
return
}
defer conn.Close()
row := conn.QueryRow(`SELECT * FROM categorias WHERE id=$1`, id)
err = row.Scan(&c.ID, &c.Nome)
return
}
// todas as categorias
func GetAll() (sc []Categoria, err error) {
conn, err := db.OpenConn()
if err != nil {
return
}
defer conn.Close()
rows, err := db.Query(`SELECT * FROM categorias`)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
var c Categoria
err = row.Scan(&c.ID, &c.Nome)
if err != nil {
continue
}
sc = append(sc, c)
}
return
}
Como explicado antes, podemos ver como acima a clara diferença entre as funções Query e QueryRow.
Seguindo para o “U” do nosso CRUD, vamos implementar a função responsável por atualizar os registros em nossa tabela.
Como não queremos que a execução nos retorne algum registro do banco de dados, para realizar o update nós vamos utilizar a função Exec.
func Update(id int64, c Categoria) (int64, error) {
conn, err := db.OpenConn()
if err != nil {
return
}
defer conn.Close()
res, err := db.Exec(`UPDATE categorias SET nome=$2 WHERE id=$1`, id, c.Nome)
if err != nil {
return err
}
return res.RowsAffected()
}
Se você der uma olhada com atenção, vai notar que no nosso SQL, o nome recebe o segundo parâmetro e o id o primeiro.
A função RowsAffected, chamada no final, irá retornar o número de registros alterados e se houve algum erro.
É importante retornar essas informações para que possamos validar, além do erro, se o número de registros alterados condiz com a quantidade que era esperada. Essa simples validação pode revelar uma inconsistência em nossa tabela.
Para finalizar, vamos implementar a função delete.
func Delete(id int64) (int64, error) {
conn, err := db.OpenConn()
if err != nil {
return
}
defer conn.Close()
res, err := db.Exec(`DELETE FROM users WHERE id=$1`, id)
if err != nil {
return err
}
return res.RowsAffected()
}
Aqui, retornar o RowsAffected tem o mesmo propósito que no update, validar se o número de registros removidos é igual o esperado.
Como removemos com base em ID, receber a informação que 2 registros foram removidos mostra claramente que existe algum problema em nossa tabela.
Com isso fechamos nosso CRUD e nosso post.
Espero que tenha sido útil para você. Qualquer dúvida é só deixar ai nos comentário.
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.




