Для работы с PostgreSQL в Go мы можем применять различные драйверы, но в данном случае мы будем использовать Pure Go Postgres driver.
Поэтому начале перейдем в командную строку/терминал и установим данный драйвер с помощью команды
go get github.com/lib/pq
Пусть на сервере PostgreSQL будет база данных productdb, в которой есть таблица Products, описываемая следующим скриптом:
CREATE TABLE Products ( id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, model varchar(30) NOT NULL, company varchar(30) NOT NULL, price integer NOT NULL );
То есть в таблице будет четыре столбца: id, model, company, price.
Для открытия соединения с базой данных в функцию sql.Open() передается имя драйвера "postgres" и строка подключения:
connStr := "user=postgres password=mypass dbname=productdb sslmode=disable" db, err := sql.Open("postgres", connStr)
Строка подключения инкапсулирует следующие параметры: user
(логин на сервере PostgreSQL), password
(пароль этого пользователя),
dbname
(имя базы данных), sslmode
(режим работы с SSL). В моем случае логин - postgres (логин по умолчанию на сервере),
пароль - mypass, имя базы данных - productdb (которая была создана выше) и ssl отключен (значение disable).
В результате установки подлючения метод db.Open возвратить объект *DB
, через который можно будет взаимодействовать в базой данных.
Это не все возможные параметры. Полный список параметров для строки подключения и их значения можно посмотреть в документации.
Для добавления используется метод Exec():
package main import ( "database/sql" "fmt" _ "github.com/lib/pq" ) func main() { connStr := "user=postgres password=mypass dbname=productdb sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { panic(err) } defer db.Close() result, err := db.Exec("insert into Products (model, company, price) values ('iPhone X', $1, $2)", "Apple", 72000) if err != nil{ panic(err) } fmt.Println(result.LastInsertId()) // не поддерживается fmt.Println(result.RowsAffected()) // количество добавленных строк }
Выполняемое sql-выражение может получать значения через дополнительные параметры метода db.Exec()
. В самом sql-выражении такие
значения представлены плейсхолдерами $1, $2 и так далее, вместо которых вставляются значения дополнительных параметров метода db.Exec.
Стоит обратить внимание, что этот драйвер не поддерживает метод result.LastInsertId()
, который возвращает id последнего добавленного объекта:
Если нам обязательно нужно получить id добавленного объекта, то мы можем использовать метод db.QueryRow(), который выполняет запрос и возвращает определенный объект:
var id int db.QueryRow("insert into Products (model, company, price) values ('Mate 10 Pro', $1, $2) returning id", "Huawei", 35000).Scan(&id) fmt.Println(id)
В само sql выражение вводится подвыражение "returning id". И с помощью метода Scan()
полученное значение считывается в переменную id.
Для получения данных применяется метод db.Query()
, который возващает набор строк, либо db.QueryRow()
, который возвращает
одну строку:
package main import ( "database/sql" "fmt" _ "github.com/lib/pq" ) type product struct{ id int model string company string price int } func main() { connStr := "user=postgres password=mypass dbname=productdb sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { panic(err) } defer db.Close() rows, err := db.Query("select * from Products") if err != nil { panic(err) } defer rows.Close() products := []product{} for rows.Next(){ p := product{} err := rows.Scan(&p.id, &p.model, &p.company, &p.price) if err != nil{ fmt.Println(err) continue } products = append(products, p) } for _, p := range products{ fmt.Println(p.id, p.model, p.company, p.price) } }
Для работы с данными здесь определена структура product, которая соответствует данным в таблице Products.
Для получения данных вызывается метод Query():
rows, err := db.Query("select * from Products")
Этот метод в качестве параметра принимает sql-выражение SELECT на получение всех данных из таблицы Products. Результат выборки попадает в переменную rows, которая представляет указатель на структуру Rows. И с помощью метода rows.Next() мы можем последовательно перебрать все строки в полученном наборе:
for rows.Next(){ p := product{} err := rows.Scan(&p.id, &p.model, &p.company, &p.price) if err != nil{ fmt.Println(err) continue } products = append(products, p) }
Тип Rows определяет метод Scan, с помощью которого можно считать все полученные данные в переменные. Например, здесь считываем данные в структуру Product и затем добавляем ее в срез. Поскольку мы получаем все данные - все четыре столбца, то соответственно в Scan передается адреса четырех переменных.
После прочтения данных в срез мы можем делать с ними все что угодно, например, вывести на консоль:
В методе Query мы можем указывать дополнительные параметры. Например, получим товары, у которых цена больше 70000:
rows, err := db.Query("select * from Products where price > $1", 70000)
Если надо получить только одну строку, то можно использовать метод QueryRow():
row := db.QueryRow("select * from Products where id = $1", 2) prod := product{} err = row.Scan(&prod.id, &prod.model, &prod.company, &prod.price) if err != nil{ panic(err) } fmt.Println(prod.id, prod.model, prod.company, prod.price)
Для обновления данных применяется метод Exec:
package main import ( "database/sql" "fmt" _ "github.com/lib/pq" ) func main() { connStr := "user=postgres password=mypass dbname=productdb sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { panic(err) } defer db.Close() // обновляем строку с id=1 result, err := db.Exec("update Products set price = $1 where id = $2", 69000, 1) if err != nil{ panic(err) } fmt.Println(result.RowsAffected()) // количество обновленных строк }
Для удаления также применяется метод Exec:
package main import ( "database/sql" "fmt" _ "github.com/lib/pq" ) func main() { connStr := "user=postgres password=mypass dbname=productdb sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { panic(err) } defer db.Close() // удаляем строку с id=2 result, err := db.Exec("delete from Products where id = $1", 2) if err != nil{ panic(err) } fmt.Println(result.RowsAffected()) // количество удаленных строк }