Работа с базами данных в Go: PostgreSQL и MongoDB для начинающих
Go (Golang) — современный компилируемый язык, который завоевал популярность благодаря высокой производительности, простоте и встроенной поддержке конкурентности. При разработке веб-сервисов и приложений практически всегда возникает необходимость взаимодействия с базами данных. В этой статье мы рассмотрим, как работать с двумя популярными СУБД: реляционной PostgreSQL и документо-ориентированной MongoDB. Материал ориентирован на начинающих разработчиков, которые уже знакомы с основами Go.
1. Подготовка окружения и установка драйверов
Прежде чем начать, убедитесь, что у вас установлен Go (версия 1.16+) и запущены соответствующие СУБД. Для работы с PostgreSQL потребуется драйвер lib/pq, а для MongoDB — официальный драйвер mongo-go-driver.
go get github.com/lib/pq
go get go.mongodb.org/mongo-driver/mongo
Создайте новый модуль:
go mod init example.com/db-demo
2. Работа с PostgreSQL в Go
PostgreSQL — мощная реляционная база данных с поддержкой SQL. В Go мы используем стандартный пакет database/sql вместе с драйвером lib/pq.
2.1 Подключение к базе данных
Для подключения необходимо указать строку соединения (DSN) в формате: host=... port=... user=... password=... dbname=... sslmode=disable.
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
connStr := "host=localhost port=5432 user=postgres password=secret dbname=testdb sslmode=disable"
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatal("Не удалось подключиться:", err)
}
fmt.Println("Подключение к PostgreSQL установлено!")
}
2.2 Создание таблицы и CRUD-операции
Рассмотрим создание таблицы users и выполнение базовых операций.
// Создание таблицы
createTable := `
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
age INT
);`
_, err = db.Exec(createTable)
if err != nil {
log.Fatal(err)
}
// Вставка данных
sqlInsert := `INSERT INTO users (name, email, age) VALUES ($1, $2, $3) RETURNING id`
var userID int
err = db.QueryRow(sqlInsert, "Иван Петров", "ivan@example.com", 30).Scan(&userID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Добавлен пользователь с ID: %d\n", userID)
// Запрос данных
rows, err := db.Query("SELECT id, name, email, age FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var u struct {
ID int
Name string
Email string
Age int
}
err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.Age)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Имя: %s, Email: %s, Возраст: %d\n", u.ID, u.Name, u.Email, u.Age)
}
2.3 Использование подготовленных запросов
Подготовленные запросы (prepared statements) повышают безопасность и производительность, особенно при повторяющихся операциях.
stmt, err := db.Prepare("UPDATE users SET age = $1 WHERE id = $2")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec(35, 1)
if err != nil {
log.Fatal(err)
}
fmt.Println("Возраст пользователя обновлён")
3. Работа с MongoDB в Go
MongoDB — документо-ориентированная база данных NoSQL. В Go работа с ней осуществляется через официальный драйвер mongo-go-driver, который использует контексты для управления временем выполнения операций.
3.1 Подключение к MongoDB
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(context.TODO())
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Не удалось подключиться:", err)
}
fmt.Println("Подключение к MongoDB установлено!")
}
3.2 CRUD-операции с коллекциями
В MongoDB данные хранятся в коллекциях, которые содержат документы (структуры, похожие на JSON).
collection := client.Database("testdb").Collection("users")
// Вставка одного документа
type User struct {
Name string `bson:"name"`
Email string `bson:"email"`
Age int `bson:"age"`
}
user := User{Name: "Мария Смирнова", Email: "maria@example.com", Age: 28}
insertResult, err := collection.InsertOne(context.TODO(), user)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Добавлен документ с ID: %v\n", insertResult.InsertedID)
// Поиск всех документов
cur, err := collection.Find(context.TODO(), bson.D{})
if err != nil {
log.Fatal(err)
}
defer cur.Close(context.TODO())
for cur.Next(context.TODO()) {
var result User
err := cur.Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Имя: %s, Email: %s, Возраст: %d\n", result.Name, result.Email, result.Age)
}
// Обновление документа
filter := bson.D{{"name", "Мария Смирнова"}}
update := bson.D{{"$set", bson.D{{"age", 29}}}}
_, err = collection.UpdateOne(context.TODO(), filter, update)
if err != nil {
log.Fatal(err)
}
// Удаление документа
_, err = collection.DeleteOne(context.TODO(), filter)
if err != nil {
log.Fatal(err)
}
3.3 Индексы и агрегация
MongoDB поддерживает создание индексов для ускорения запросов:
indexModel := mongo.IndexModel{
Keys: bson.D{{"email", 1}}, //