Ответ
Да, активно работал с SQL в различных проектах. В Go для взаимодействия с реляционными базами данных используется стандартный пакет database/sql
в сочетании с конкретными драйверами для СУБД (например, github.com/lib/pq
для PostgreSQL, github.com/go-sql-driver/mysql
для MySQL, github.com/mattn/go-sqlite3
для SQLite).
Основные практики и подходы:
-
Подключение к базе данных:
import ( "database/sql" _ "github.com/lib/pq" // Импорт драйвера PostgreSQL "log" ) func connectDB() *sql.DB { connStr := "postgres://user:password@localhost:5432/dbname?sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { log.Fatalf("Не удалось подключиться к базе данных: %v", err) } // Проверка соединения if err = db.Ping(); err != nil { log.Fatalf("Не удалось установить соединение с базой данных: %v", err) } log.Println("Успешное подключение к базе данных!") return db } // В main или другом месте: db := connectDB(); defer db.Close()
-
Выполнение запросов:
db.Query()
: Для выполнения запросовSELECT
, возвращающих строки.rows, err := db.Query("SELECT id, name FROM users WHERE age > $1", 25) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string if err := rows.Scan(&id, &name); err != nil { log.Fatal(err) } fmt.Printf("ID: %d, Name: %sn", id, name) } if err = rows.Err(); err != nil { log.Fatal(err) } // Проверка ошибок после цикла
db.Exec()
: Для выполнения запросовINSERT
,UPDATE
,DELETE
, не возвращающих строки.result, err := db.Exec("INSERT INTO users(name, email) VALUES($1, $2)", "Alice", "alice@example.com") if err != nil { log.Fatal(err) } rowsAffected, err := result.RowsAffected() if err != nil { log.Fatal(err) } fmt.Printf("Добавлено строк: %dn", rowsAffected)
-
Подготовленные выражения (
Prepare()
): Для повторяющихся запросов, повышают производительность и безопасность (защита от SQL-инъекций).stmt, err := db.Prepare("UPDATE products SET price = $1 WHERE id = $2") if err != nil { log.Fatal(err) } defer stmt.Close() _, err = stmt.Exec(100.00, 1) if err != nil { log.Fatal(err) } _, err = stmt.Exec(150.50, 2) if err != nil { log.Fatal(err) }
-
Транзакции: Для обеспечения атомарности операций.
tx, err := db.Begin() if err != nil { log.Fatal(err) } _, err = tx.Exec("INSERT INTO accounts(user_id, balance) VALUES($1, $2)", 1, 1000) if err != nil { tx.Rollback() // Откат при ошибке log.Fatal(err) } _, err = tx.Exec("UPDATE users SET has_account = TRUE WHERE id = $1", 1) if err != nil { tx.Rollback() log.Fatal(err) } err = tx.Commit() // Подтверждение транзакции if err != nil { log.Fatal(err) } fmt.Println("Транзакция успешно завершена.")
Мой подход:
В сложных проектах я использую ORM, такие как GORM, для быстрой разработки, упрощения работы с миграциями и базовыми CRUD-операциями. Однако для критичных к производительности участков, сложных аналитических запросов или специфических операций, где ORM может быть неэффективен или генерировать неоптимальный SQL, я предпочитаю писать чистый SQL с использованием database/sql
. Это позволяет получить полный контроль над запросами и оптимизировать их под конкретные задачи.