{"id":21577941,"url":"https://github.com/wesleysbmartins/go_pgsql","last_synced_at":"2026-05-07T10:37:46.325Z","repository":{"id":251563959,"uuid":"835396294","full_name":"wesleysbmartins/go_pgsql","owner":"wesleysbmartins","description":"Este repositório didático explora as possibilidades existentes em uma aplicação Golang integrada a um banco de dados relacional PostgreSQL, intermediada pelo driver ou lib database/sql do próprio Go.","archived":false,"fork":false,"pushed_at":"2024-08-04T02:32:53.000Z","size":12,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-18T07:28:22.498Z","etag":null,"topics":["crud","database","go","golang","postgres","postgresql","prepared-statements","preparedstatement","query","sql","transactions"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wesleysbmartins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-29T18:44:33.000Z","updated_at":"2024-08-04T02:34:09.000Z","dependencies_parsed_at":"2024-08-04T03:45:20.568Z","dependency_job_id":"2a60de8e-e29a-4285-8c7e-dae1d0b4d9bd","html_url":"https://github.com/wesleysbmartins/go_pgsql","commit_stats":null,"previous_names":["wesleysbmartins/go_pgsql"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wesleysbmartins/go_pgsql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysbmartins%2Fgo_pgsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysbmartins%2Fgo_pgsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysbmartins%2Fgo_pgsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysbmartins%2Fgo_pgsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wesleysbmartins","download_url":"https://codeload.github.com/wesleysbmartins/go_pgsql/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wesleysbmartins%2Fgo_pgsql/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268308589,"owners_count":24230072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crud","database","go","golang","postgres","postgresql","prepared-statements","preparedstatement","query","sql","transactions"],"created_at":"2024-11-24T13:09:02.370Z","updated_at":"2026-05-07T10:37:46.295Z","avatar_url":"https://github.com/wesleysbmartins.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Golang PostgreSQL\n[![My Skills](https://skillicons.dev/icons?i=golang,postgres)](https://skillicons.dev)\n\nEste repositório didático explora as possibilidades existentes em uma aplicação Golang integrada a um banco de dados relacional PostgreSQL, intermediada pelo driver ou lib **database/sql** do próprio Go.\nO pacote uma interface genérica para trabalhar com bancos de dados SQL. Ele permite que você execute operações de banco de dados de forma consistente, independentemente do banco de dados específico que você está usando (como PostgreSQL, MySQL, SQLite, etc.). A biblioteca abstrai os detalhes específicos do banco de dados e oferece um conjunto de funções e tipos para executar consultas, gerenciar transações e manipular conexões.\n\n\n\u003cdetails\u003e\n    \u003csummary\u003eCriação de Client (Conexão).\u003c/summary\u003e\n\n## Client\nUm client ou uma conexão refere-se a uma instância que gerencia a comunicação com o banco de dados e a execução de operações SQL.\nNa abordagem utilizado criamos um Singleton do client, onde será centralizado apenas uma conexão com o banco de dados, e a partir desta conexão poderemos executar diversas operações.\n```go\npackage postgres\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\n\t_ \"github.com/lib/pq\"\n)\n\ntype Postgres struct {\n\thost     string\n\tport     int\n\tuser     string\n\tpassword string\n\tdatabase string\n}\n\ntype IPostgres interface {\n\tConnect()\n}\n\nvar Client *sql.DB\n\nfunc (p *Postgres) Connect() {\n\n\tcredentials := Postgres{\n\t\thost:     \"localhost\",\n\t\tport:     5432,\n\t\tuser:     \"postgres\",\n\t\tpassword: \"1234\",\n\t\tdatabase: \"teste\",\n\t}\n\n\tconnStr := fmt.Sprintf(\"host=%s port=%v user=%s password=%s dbname=%s sslmode=disable\", credentials.host, credentials.port, credentials.user, credentials.password, credentials.database)\n\n\tdb, err := sql.Open(\"postgres\", connStr)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = db.Ping()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"POSTGRES CONNECTION SUCCESS!\")\n\n\tClient = db\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eEntidade\u003c/summary\u003e\n\n## Users\nA nossa aplicação realiza operações com base na tabela Users no banco de dados, que tem suas colunas equivalentes a nossa struct User.\n```go\npackage entities\n\nimport \"time\"\n\ntype User struct {\n\tId        int\n\tName      string\n\tUsername  string\n\tEmail     string\n\tPassword  string\n\tToken     string\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n\tDeletedAt *time.Time\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eCRUD (Create, Read, Update, Delete).\u003c/summary\u003e\n\n## Leituras com Query e QueryRow\nA leitura de linhas em tabelas do banco pode ser abordada de duas formas, leituras de apenas uma linha e de mais de uma, o pacote sql permite efetuar duas operações como o método Query para trazer diversas linhas e o QueryRow para apenas uma.\n\n### Query\nO método query trás como retorno as rows ou linhas de resultado da consulta e um error, caso não tenha ocorrido nenhum erro o valor será **nil**.\nEm caso de sucesso, será necessário iterar as linhas retornadas e converter os valores de cada coluna da linha nos valores da nossa struct User.\n```go\nfunc FindUsers() ([]entities.User, error) {\n    users := []entities.User{}\n\n\trows, err := postgres.Client.Query(\"SELECT * FROM \u003cnome do banco\u003e.users\")\n    defer rows.Close()\n\n\tif err != nil {\n\t\tfmt.Println(\"ERROR\\n\", err)\n\t} else {\n        for rows.Next() {\n            user := entities.User{}\n\t\t    err = rows.Scan(\u0026user.Id, \u0026user.Name, \u0026user.Username, \u0026user.Email, \u0026user.Password, \u0026user.Token, \u0026user.CreatedAt, \u0026user.UpdatedAt, \u0026user.DeletedAt)\n\n            if err != nil {\n                fmt.Println(\"SCAN ERROR\\n\", err)\n            }\n\n            users = append(users, user)\n        }\n    }\n\n\treturn users, err\n}\n```\nE se você precisar adicionar filtros a sua consulta?\nO método Query aceita argumentos como parametros, sendo que no seu where voce deve substituir o valor real por um cifrão e o indice do parametro, por exemplo:\n\n```go\nid := 1\nrows, err := postgres.Client.Query(\"SELECT * FROM \u003cnome do banco\u003e.users WHERE id = $1\", id)\n```\n```go\nids := []int{1,2}\nrows, err := postgres.Client.Query(\"SELECT * FROM \u003cnome do banco\u003e.users WHERE id IN ($1,$2)\", ids...)\n```\n\n### QueryRow\nO método **QueryRow** trás como retorno a row ou a linha de resultado da consulta.\nE como na operação anterior será necessário iterar a linha retornada e converter os valores de cada coluna nos valores da nossa struct User.\nQueryRow também aceita argumentas como a consulta anterior.\n```go\nfunc FindUserById(id int) (entities.User, error) {\n\trow := postgres.Client.QueryRow(\"SELECT * FROM \u003cnome do banco\u003e.users WHERE id = $1\", id)\n\t\n    user := entities.User{}\n\n    err = row.Scan(\u0026user.Id, \u0026user.Name, \u0026user.Username, \u0026user.Email, \u0026user.Password, \u0026user.Token, \u0026user.CreatedAt, \u0026user.UpdatedAt, \u0026user.DeletedAt)\n    if err != nil {\n        fmt.Println(\"SCAN ERROR\\n\", err)\n    }\n\n\treturn user, err\n}\n```\n\n## Escrevendo ou criando, atualizando e removendo com Exec\nA escrita de linhas em tabelas do banco pode ser abordada de duas formas, inserção, e update, o pacote sql permite efetuar duas operações como o método Query para trazer diversas linhas e o QueryRow para apenas uma.\n\n### Exec\nO método **Exec** trás como retorno o result que contém a informação de quantas linhas foram afetadas e um error, caso não tenha ocorrido nenhum erro o valor será **nil**.\n\n### Insert\n```go\nfunc CreateUser(user entities.User) error {\n\t\n\tresult, err := postgres.Client.Exec(\"INSERT INTO \u003cnome do banco\u003e.users (name, username, email, password, createdat) VALUES ($1, $2, $3, $4, $5)\", user.Name, user.Username, user.Email, user.Password, time.Now())\n    if err != nil {\n\t\tfmt.Println(\"INSERT ERROR\\n\", err)\n\t\treturn err\n\t} else {\n\t\trows, _ := result.RowsAffected()\n\t\tfmt.Println(\"ROWS AFFECTED: \", rows)\n\t}\n\n    return nil\n}\n```\n\n### Update\n```go\nfunc UpdateUser(id int, name string, email string) error {\n\t\n\tresult, err := postgres.Client.Exec(\"UPDATE \u003cnome do banco de dados\u003e.users SET name = $1, email = $2 WHERE id = $3\", name, email, id)\n    if err != nil {\n\t\tfmt.Println(\"UPDATE ERROR\\n\", err)\n\t\treturn err\n\t} else {\n\t\trows, _ := result.RowsAffected()\n\t\tfmt.Println(\"ROWS AFFECTED: \", rows)\n\t}\n\n    return nil\n}\n```\n\n### Delete\n```go\nfunc DeleteUser(id int) error {\n\t\n\tresult, err := postgres.Client.Exec(\"DELETE FROM \u003cnome do banco de dados\u003e.users WHERE id = $1\", id)\n    if err != nil {\n\t\tfmt.Println(\"DELETE ERROR\\n\", err)\n\t\treturn err\n\t} else {\n\t\trows, _ := result.RowsAffected()\n\t\tfmt.Println(\"ROWS AFFECTED: \", rows)\n\t}\n\n    return nil\n}\n```\n\n### Escrevendo com QueryRow\nE se eu quiser escrever e ter o retorno da linha que eu escrevi?\n\nVoce pode usar o QueryRow e adicionar ao final de sua operação o comando \"RETURNING *\" para retornar a linha\n\nIsso servirá para operações de **INSERT** e **UPDATE**.\n```go\nfunc CreateUser(user entities.User) (entities.User, error) {\n\n\trow := postgres.Client.QueryRow(\"INSERT INTO teste.users (name, username, email, password, createdat) VALUES ($1, $2, $3, $4, $5) RETURNING *\", user.Name, user.Username, user.Email, user.Password, time.Now())\n\n\tnewUser := entities.User{}\n\n\terr := row.Scan(\u0026newUser.Id, \u0026newUser.Name, \u0026newUser.Username, \u0026newUser.Email, \u0026newUser.Password, \u0026newUser.Token, \u0026newUser.CreatedAt, \u0026newUser.UpdatedAt, \u0026newUser.DeletedAt)\n\n\tif err != nil {\n\t\tfmt.Println(\"SCAN ERROR\\n\", err)\n\t}\n\n\treturn newUser, err\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003ePrepared Statements\u003c/summary\u003e\n\n## Prepared Statements\nConsultas preparadas, ou prepared statements, são uma forma eficiente e segura de executar consultas repetidas em um banco de dados. Ao usar consultas preparadas, você pode separar a compilação da consulta SQL da execução dos dados, o que pode melhorar o desempenho e a segurança.\n\nAs consultas preparadas são compiladas uma vez pelo servidor de banco de dados e podem ser executadas várias vezes com diferentes parâmetros sem precisar ser recompiladas.\n\nUsar consultas preparadas ajuda a prevenir injeções de SQL, já que os parâmetros são tratados separadamente do comando SQL.\n\nSegue um exemplo onde para criar um usuário antes é feito uma busca de usuários pelo nome, caso não exista deverá ser criado, então é feito a busca novamente para retornar o valor do usuários criado, senão é retornado um usuário vazio e uma mensagem de erro.\n```go\nfunc CreateAndFindUser(user entities.User) (entities.User, error) {\n\tname := user.Name\n\texists := false\n\n\tstmt, err := postgres.Client.Prepare(\"SELECT * FROM teste.users WHERE name = $1\")\n\tif err != nil {\n\t\tfmt.Println(\"PREPARE STATEMENT ERROR\\n\", err)\n\t}\n\n\tdefer stmt.Close()\n\n\trows, err := stmt.Query(name)\n\n\tif err != nil {\n\t\tfmt.Println(\"STATEMENT READ ERROR\\n\", err)\n\t} else {\n\t\tdefer rows.Close()\n\n\t\tfor rows.Next() {\n\t\t\tcurrentUser := entities.User{}\n\t\t\terr = rows.Scan(\u0026currentUser.Id, \u0026currentUser.Name, \u0026currentUser.Username, \u0026currentUser.Email, \u0026currentUser.Password, \u0026currentUser.Token, \u0026currentUser.CreatedAt, \u0026currentUser.UpdatedAt, \u0026currentUser.DeletedAt)\n\n\t\t\tif err != nil {\n\t\t\t\tfmt.Println(\"SCAN ERROR\\n\", err)\n\t\t\t}\n\n\t\t\tif currentUser.Name == name {\n\t\t\t\texists = true\n\t\t\t}\n\t\t}\n\t}\n\n\tnewUser := entities.User{}\n\n\tif !exists {\n\t\tCreateUser(user)\n\t} else {\n\t\terr := fmt.Errorf(\"USER EXISTS\")\n\t\treturn newUser, err\n\t}\n\n\trows, err = stmt.Query(name)\n\n\tif err != nil {\n\t\tfmt.Println(\"STATEMENT READ ERROR\\n\", err)\n\t} else {\n\t\tdefer rows.Close()\n\n\t\tfor rows.Next() {\n\t\t\tcurrentUser := entities.User{}\n\t\t\terr = rows.Scan(\u0026currentUser.Id, \u0026currentUser.Name, \u0026currentUser.Username, \u0026currentUser.Email, \u0026currentUser.Password, \u0026currentUser.Token, \u0026currentUser.CreatedAt, \u0026currentUser.UpdatedAt, \u0026currentUser.DeletedAt)\n\n\t\t\tif err != nil {\n\t\t\t\tfmt.Println(\"SCAN ERROR\\n\", err)\n\t\t\t}\n\n\t\t\tif currentUser.Name == name {\n\t\t\t\tnewUser = currentUser\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newUser, err\n}\n```\nNeste exemplo, a consulta é reutilizada, os argumentos passados tem os mesmos valores porém, se fossem diferentes, a consulta seria executada novamente com sucesso.\nA execução da query de um Prepared Statement pode ser tanto um **Query**, **QueryRow**, ou **Exec**.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eTransactions\u003c/summary\u003e\n\n## Transactions\nTransações são um conceito fundamental em bancos de dados, incluindo PostgreSQL, que permitem agrupar uma ou mais operações SQL em uma única unidade de trabalho. As transações garantem que todas as operações dentro da transação sejam concluídas com sucesso ou nenhuma delas seja aplicada, mantendo a consistência e integridade dos dados.\n\nAs transações seguem as propriedades **ACID**:\n\n**Atomicidade:** Todas as operações dentro da transação são completadas com sucesso ou nenhuma delas é aplicada.\n\n**Consistência:** As transações levam o banco de dados de um estado consistente a outro estado consistente.\n\n**Isolamento:** As transações são isoladas umas das outras, garantindo que os resultados de uma transação não sejam visíveis para outras transações até que sejam finalizadas.\n\n**Durabilidade:** Uma vez que uma transação é confirmada (committed), seus efeitos persistem no banco de dados mesmo que haja uma falha no sistema.\n\n**Locks:** Durante a utilização de transactions é necessário ter cuidado, pois elas geram locks e podem comprometer a performance das operações no seu banco de dados, bloqueando linhas ou até mesmo tabelas inteiras de serem lidas ou escritas até que sua execução termine.\n\nPara usar uma transação em uma ou mais operações você deve xriar a transação e a partir dela executar suas querys, caso uma das operações de erro você deve fazer o rollback desta operação, ou seja, se voce estiver inserindo, atualizando ou deletando alguma linha, esta operação não será efetuada e a sua tabela permanecerá no estado anterior as operações, e em caso de sucesso, você deve efetuar o commit da transação para que as operações sejam efetuadas, assim alterando seu banco de dados definitivamente.\n```go\nfunc CreateUser(user entities.User) error {\n\ttx, err := postgres.Client.Begin()\n\tif err != nil {\n\t\tfmt.Println(\"TRANSACTION INTANCE ERROR\\n\", err)\n\t}\n\n\tdefer tx.Rollback()\n\tdefer tx.Commit()\n\n\tresult, err := tx.Exec(\"INSERT INTO \u003cnome do banco\u003e.users (name, username, email, password, createdat) VALUES ($1, $2, $3, $4, $5)\", user.Name, user.Username, user.Email, user.Password, time.Now())\n    if err != nil {\n\t\tfmt.Println(\"INSERT ERROR\\n\", err)\n\t\treturn err\n\t} else {\n\t\trows, _ := result.RowsAffected()\n\t\tfmt.Println(\"ROWS AFFECTED: \", rows)\n\t}\n\n    return nil\n}\n```\n\nTambém é possível utilizar Prepared Statements com transações, por exemplo:\n```go\nfunc CreateUser(user entities.User) error {\n\ttx, err := postgres.Client.Begin()\n\tif err != nil {\n\t\tfmt.Println(\"TRANSACTION INTANCE ERROR\\n\", err)\n\t}\n\n\tdefer tx.Rollback()\n\tdefer tx.Commit()\n\n\tstmt, err := tx.Prepare(\"INSERT INTO \u003cnome do banco\u003e.users (name, username, email, password, createdat) VALUES ($1, $2, $3, $4, $5)\")\n\tif err != nil {\n\t\tfmt.Println(\"PREPARE STATEMENT ERROR\\n\", err)\n\t}\n\n\tdefer stmt.Close()\n\n\tresult, err := stmt.Exec(user.Name, user.Username, user.Email, user.Password, time.Now())\n    if err != nil {\n\t\tfmt.Println(\"INSERT ERROR\\n\", err)\n\t\treturn err\n\t} else {\n\t\trows, _ := result.RowsAffected()\n\t\tfmt.Println(\"ROWS AFFECTED: \", rows)\n\t}\n\n    return nil\n}\n```\n\u003c/details\u003e\n\nNeste repositórios eu exercitei algumas ideias e maneiras de utilizar todos estes recursos, fique a vontade para explorar.\n\nPontos que ainda podem ser aprofundados seriam os tipos de Lock e Isolamentos das transações, pretendo abordar em breve.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwesleysbmartins%2Fgo_pgsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwesleysbmartins%2Fgo_pgsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwesleysbmartins%2Fgo_pgsql/lists"}