💭

Go言語で堅牢なDBアクセスを実現するためのsqlc活用法

に公開

Go言語でデータベース(DB)を扱う際、ORM(Object-Relational Mapping)を利用する選択肢もありますが、内部で発行されるSQLを完全に把握するのが難しいことがあります。これが原因でパフォーマンスの最適化が困難になったり、予期しないバグが発生する可能性があります。

その点、sqlc を使えば、事前に定義したSQLから型安全なGoコードを自動生成でき、より堅牢なシステム構築が可能になります。本記事では、sqlcの概要から具体的な導入方法、実践的な活用方法までを解説します。

sqlcとは?

sqlcは、SQLクエリから型安全なGoコードを生成するツール です。従来のORMのように抽象化するのではなく、明示的に書かれたSQLを解析し、それに対応するGoの型を自動生成するため、以下のようなメリットがあります。

  • SQLの最適化を自分でコントロールできる
  • 型安全なGoコードを生成し、バグを未然に防ぐ
  • ランタイムではなくビルド時にエラーを検出できる
  • 余計な抽象化がなく、パフォーマンスが最適化される

sqlcの導入方法

sqlcをプロジェクトに導入するには、以下の手順を実施します。

1. sqlcのインストール

# 最新版のsqlcをインストール
go install github.com/kyleconroy/sqlc/cmd/sqlc@latest

2. 設定ファイルの作成

sqlcの設定ファイル sqlc.yaml をプロジェクトのルートディレクトリに作成します。

version: "2"
sql:
  - engine: "postgresql"
    schema: "schema.sql"
    queries: "queries.sql"
    gen:
      go:
        package: "db"
        out: "internal/db"

この設定では、PostgreSQLをターゲットとし、schema.sql にスキーマ定義を、queries.sql にSQLクエリを記述する構成になっています。

3. スキーマとクエリの定義

スキーマ定義(schema.sql)

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
);

クエリ定義(queries.sql)

-- name: GetUser :one
SELECT * FROM users WHERE id = $1;

-- name: CreateUser :one
INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *;

4. Goコードの生成

以下のコマンドを実行すると、型安全なGoコードが自動生成されます。

sqlc generate

これにより、 internal/db ディレクトリ内に queries.sql で定義したSQLに対応するGoの型と関数が生成されます。

sqlcを使ったDBアクセス

生成されたGoコードを使って、データベースの操作を行うサンプルを見てみましょう。

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
    "_ "github.com/lib/pq" // PostgreSQLドライバ
    "myapp/internal/db"    // 生成されたパッケージ
)

func main() {
    connStr := "postgres://user:password@localhost:5432/mydb?sslmode=disable"
    conn, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    queries := db.New(conn)

    // ユーザーを作成
    newUser, err := queries.CreateUser(context.Background(), db.CreateUserParams{
        Name:  "Alice",
        Email: "alice@example.com",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created user: %+v\n", newUser)

    // ユーザーを取得
    user, err := queries.GetUser(context.Background(), newUser.ID)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Fetched user: %+v\n", user)
}

まとめ

sqlcを使うことで、最適化されたSQLを元に型安全なGoのコードを自動生成 し、予期しないエラーを未然に防ぎつつ、堅牢なDBアクセスを実現できます。

従来のORMのように抽象化しすぎることなく、SQLを明示的に書きつつも、Goの型システムによる安全性を確保できる のが大きなメリットです。パフォーマンスと保守性の両方を重視するプロジェクトでは、sqlcの導入を検討してみる価値があるでしょう。

次のステップとして、実際にプロジェクトに組み込んで試してみてください!

Discussion