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!