Подключение к SQLite в Go: Полное руководство

Основные команды по управлению базой данных SQLite на языке программирования Go.

Установка драйвера SQLite для Go

Перед началом работы установите драйвер SQLite:

go get github.com/mattn/go-sqlite3

Базовое подключение к базе данных

Простое подключение

package main

import (
	"database/sql"
	"fmt"
	"log"
	
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	// Открытие соединения с базой данных
	db, err := sql.Open("sqlite3", "./test.db")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Проверка соединения
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Successfully connected to SQLite database")
}

Подключение с параметрами

db, err := sql.Open("sqlite3", "file:test.db?cache=shared&mode=memory")
if err != nil {
	log.Fatal(err)
}

Выполнение запросов

Создание таблицы

func createTable(db *sql.DB) error {
	query := `
	CREATE TABLE IF NOT EXISTS users (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		name TEXT NOT NULL,
		email TEXT NOT NULL UNIQUE,
		created_at DATETIME DEFAULT CURRENT_TIMESTAMP
	)`
	
	_, err := db.Exec(query)
	return err
}

Вставка данных

func insertUser(db *sql.DB, name, email string) (int64, error) {
	res, err := db.Exec("INSERT INTO users(name, email) VALUES(?, ?)", name, email)
	if err != nil {
		return 0, err
	}
	
	return res.LastInsertId()
}

Выборка данных

type User struct {
	ID        int
	Name      string
	Email     string
	CreatedAt string
}

func getUsers(db *sql.DB) ([]User, error) {
	rows, err := db.Query("SELECT id, name, email, created_at FROM users")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []User
	for rows.Next() {
		var u User
		err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.CreatedAt)
		if err != nil {
			return nil, err
		}
		users = append(users, u)
	}

	return users, nil
}

Транзакции

func transferMoney(db *sql.DB, from, to int, amount float64) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	// Откат транзакции при ошибке
	defer func() {
		if err != nil {
			tx.Rollback()
		}
	}()

	// Снимаем деньги с первого аккаунта
	_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, from)
	if err != nil {
		return err
	}

	// Добавляем деньги на второй аккаунт
	_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, to)
	if err != nil {
		return err
	}

	// Фиксируем транзакцию
	return tx.Commit()
}

Подготовленные выражения (Prepared Statements)

func getUserByEmail(db *sql.DB, email string) (*User, error) {
	stmt, err := db.Prepare("SELECT id, name, email FROM users WHERE email = ?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	var u User
	err = stmt.QueryRow(email).Scan(&u.ID, &u.Name, &u.Email)
	if err != nil {
		return nil, err
	}

	return &u, nil
}

Практические советы

  1. Пулинг соединений: SQLite не поддерживает множественные соединения на запись, но вы можете использовать пулинг для чтения:
db.SetMaxOpenConns(1)  // Важно для SQLite
  1. Режим WAL: Для лучшей производительности:
_, err = db.Exec("PRAGMA journal_mode=WAL")
if err != nil {
	log.Fatal(err)
}
  1. Обработка ошибок: Всегда проверяйте ошибки при закрытии ресурсов:
defer func() {
	if err := rows.Close(); err != nil {
		log.Printf("failed to close rows: %v", err)
	}
}()

Полный пример приложения

package main

import (
	"database/sql"
	"fmt"
	"log"
	
	_ "github.com/mattn/go-sqlite3"
)

type User struct {
	ID    int
	Name  string
	Email string
}

func main() {
	// Подключение к базе данных
	db, err := sql.Open("sqlite3", "./app.db")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Настройка базы данных
	if err := initDB(db); err != nil {
		log.Fatal(err)
	}

	// Добавление пользователя
	id, err := addUser(db, "John Doe", "john@example.com")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Added user with ID: %d\n", id)

	// Получение списка пользователей
	users, err := getUsers(db)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Users:")
	for _, u := range users {
		fmt.Printf("%d: %s (%s)\n", u.ID, u.Name, u.Email)
	}
}

func initDB(db *sql.DB) error {
	_, err := db.Exec(`
		CREATE TABLE IF NOT EXISTS users (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			name TEXT NOT NULL,
			email TEXT NOT NULL UNIQUE
		)
	`)
	return err
}

func addUser(db *sql.DB, name, email string) (int64, error) {
	res, err := db.Exec("INSERT INTO users(name, email) VALUES(?, ?)", name, email)
	if err != nil {
		return 0, err
	}
	return res.LastInsertId()
}

func getUsers(db *sql.DB) ([]User, error) {
	rows, err := db.Query("SELECT id, name, email FROM users")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var users []User
	for rows.Next() {
		var u User
		if err := rows.Scan(&u.ID, &u.Name, &u.Email); err != nil {
			return nil, err
		}
		users = append(users, u)
	}
	return users, nil
}

Заключение

Работа с SQLite в Go проста и эффективна благодаря пакету database/sql и драйверу go-sqlite3. Основные принципы:

  • Всегда закрывайте ресурсы (деферы для Close())
  • Используйте подготовленные выражения для защиты от SQL-инъекций
  • Для операций записи используйте транзакции
  • Настраивайте параметры SQLite для оптимальной производительности