Работа с базами данных в Go: PostgreSQL и MongoDB
Go (Golang) — это современный компилируемый язык, который идеально подходит для создания высоконагруженных бэкенд-сервисов. Одной из ключевых задач любого бэкенда является работа с базами данных. В этой статье мы детально разберем, как подключаться и выполнять CRUD-операции с двумя самыми популярными СУБД: реляционной PostgreSQL и документо-ориентированной MongoDB. Вы получите готовые примеры кода, best practices и советы по оптимизации запросов.
1. Подготовка среды и установка драйверов
Перед началом работы убедитесь, что у вас установлен Go версии 1.19 или выше. Для работы с базами данных нам понадобятся два основных пакета:
- database/sql — стандартный пакет Go для работы с SQL-базами (общий интерфейс);
- lib/pq — драйвер для PostgreSQL;
- go.mongodb.org/mongo-driver — официальный драйвер MongoDB.
Установка драйверов через go get:
go get github.com/lib/pq
go get go.mongodb.org/mongo-driver/mongo
Создайте новый модуль вашего проекта:
mkdir go-db-example
cd go-db-example
go mod init go-db-example
2. Работа с PostgreSQL в Go
PostgreSQL — это мощная объектно-реляционная СУБД. В Go работа с ней строится через интерфейс database/sql.
2.1 Подключение к базе
Создадим файл main.go и напишем подключение к локальной базе данных Postgres:
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
// Строка подключения: user password dbname sslmode
connStr := "user=postgres password=mysecretpassword 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 Создание таблицы и вставка данных
После подключения создадим таблицу 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)
}
// Вставка данных
insert := `INSERT INTO users (name, email, age) VALUES ($1, $2, $3)`
_, err = db.Exec(insert, "Анна Петрова", "anna@example.com", 28)
if err != nil {
log.Fatal("Ошибка вставки:", err)
}
fmt.Println("Данные добавлены")
2.3 Чтение данных (SELECT)
Используем QueryRow для одной записи и Query для множества:
// Получение одной записи
var id int
var name, email string
var age sql.NullInt64
row := db.QueryRow("SELECT id, name, email, age FROM users WHERE email = $1", "anna@example.com")
err = row.Scan(&id, &name, &email, &age)
if err != nil {
log.Fatal("Ошибка сканирования:", err)
}
fmt.Printf("ID: %d, Имя: %s, Email: %s, Возраст: %v\n", id, name, email, age)
// Получение всех записей
rows, err := db.Query("SELECT id, name, email FROM users ORDER BY id")
if err != nil {
log.Fatal("Ошибка запроса:", err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&id, &name, &email)
if err != nil {
log.Fatal("Ошибка чтения строки:", err)
}
fmt.Printf("User: %d | %s | %s\n", id, name, email)
}
Важно: Всегда обрабатывайте sql.NullInt64 для nullable-полей, чтобы избежать паники при NULL-значениях.
3. Работа с MongoDB в Go
MongoDB — NoSQL база данных, хранящая данные в формате BSON (бинарный JSON). Официальный драйвер предоставляет удобный API для работы с коллекциями.
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() {
// Контекст с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Подключение к локальному инстансу MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal("Ошибка подключения:", err)
}
defer client.Disconnect(ctx)
// Проверка соединения
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal("Не удалось подключиться к MongoDB:", err)
}
fmt.Println("Успешно подключено к MongoDB!")
// Выбираем базу данных и коллекцию
collection := client.Database("testdb").Collection("users")
_ = collection // используем далее
}
3.2 CRUD-операции с MongoDB
Определим структуру пользователя и выполним базовые операции:
import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"name"`
Email string `bson:"email"`
Age int `bson:"age"`
}
// --- CREATE ---
user := User{Name: "Иван Иванов", Email: "ivan@example.com", Age: 30}
result, err := collection.InsertOne(ctx, user)
if err != nil {
log.Fatal("Ошибка вставки:", err)
}
fmt.Println("Вставлен ID:", result.InsertedID)
// --- READ (FindOne) ---
var foundUser User
err = collection.FindOne(ctx, bson.M{"email": "ivan@example.com"}).Decode(&foundUser)
if err != nil {
log.Fatal("Пользователь