GOで使用するORM: GORM、sqlc、Ent、またはBunですか?
GORM vs sqlc vs Ent vs Bun
Goのエコシステムには、それぞれ独自の哲学を持つORM(オブジェクトリレーショナルマッピング)ツールとデータベースライブラリが多数存在します。ここでは、PostgreSQLをGoで使用するための4つの主要なソリューション(GORM、sqlc、Ent、Bun)についての包括的な比較を紹介します。ORM in GO and Postgresql。
それらをパフォーマンス、開発者体験、人気、機能性/拡張性の観点から評価し、User
モデルに対する基本的なCRUD操作を示すコード例を用いて比較します。中級および上級のGo開発者は、各ツールのトレードオフを理解し、自分に最も適したツールを選び出すことができます。
ORMの概要
-
GORM – 機能が豊富で、Active RecordスタイルのORMです。GORMでは、Goの構造体をモデルとして定義し、メソッドチェインを使用してデータをクエリおよび操作するための広範なAPIを提供します。何年も前から存在し、GoのORMの中で最も広く使用されているものの一つ(GitHubスターはほぼ39,000個)。GORMの魅力は、自動マイグレーション、リレーションの処理、急ぎ読み込み、トランザクション、フックなどの豊富な機能セットであり、これにより、SQLを最小限に使用してクリーンなGoコードベースを作成できます。しかし、独自のパターンを導入し、反射とインターフェースの使用によるランタイムオーバーヘッドが、大規模な操作ではパフォーマンスに影響を与える可能性があります。
-
sqlc – 伝統的なORMではなく、SQLコードジェネレータです。sqlcでは、
.sql
ファイルに通常のSQLクエリを記述し、そのクエリを実行するための型安全なGoコード(DAO関数)を生成します。これは、生成されたGo関数(例:CreateUser
、GetUser
)を呼び出して、行を手動でスキャンすることなく型安全な結果を取得することを意味します。sqlcは、クエリ構築やランタイム反射のレイヤーを避けて、準備されたSQLを使用してシンプルさとパフォーマンスを実現します。これは、database/sql
を直接使用するのと同じくらい高速です。トレードオフは、クエリのSQLステートメントを(維持する必要がある)記述しなければならない点であり、ORMがSQLを自動生成する場合に比べて動的なクエリロジックが直感的でない可能性があります。 -
Ent (Entgo) – コードファーストのORMで、コードジェネレーションを使用してデータモデルのための型安全なAPIを作成します。Entでは、フィールド、エッジ/リレーション、制約などのEntのDSLを使用してスキーマをGoで定義し、その後EntはモデルとクエリメソッドのためのGoパッケージを生成します。生成されたコードは、クエリを構築するためのフロントAPIを使用し、完全なコンパイル時型チェックが可能です。Entは比較的新しく、Linux Foundationによって後援され、Arigaによって開発されています。開発者体験と正確性に焦点を当てており、クエリは文字列ではなく、チェイン可能なメソッドを使用して構築されるため、組み合わせやすく、エラーの可能性が少ないです。Entのアプローチは、非常に最適化されたSQLクエリを生成し、クエリ結果をキャッシュしてデータベースへの複数回のアクセスを減らすことができます。スケーラビリティを考慮して設計されており、複雑なスキーマやリレーションを効率的に処理します。ただし、Entを使用するとコードジェネレーションのためのビルドステップが必要になり、そのエコシステムに依存することになります。現在ではPostgreSQL、MySQL、SQLite(他のデータベースの実験的なサポートも)をサポートしており、GORMはSQL ServerやClickHouseなど、より広範なデータベースをサポートしています。
-
Bun – “SQLファースト"のアプローチを持つ新しいORMです。BunはGoの
database/sql
と古いgo-pgライブラリからインスピレーションを受け、軽量で高速であり、PostgreSQLの機能に焦点を当てています。Active Recordパターンではなく、フロントクエリビルダーを提供します。クエリはdb.NewSelect()
/NewInsert()
/ などから開始し、SQLに非常に近いメソッドで構築されます(必要に応じて生のSQLスニペットを埋め込むこともできます)。GORMと同様の構造タグを使用してクエリ結果をGo構造体にマッピングします。Bunは明確さを重視し、必要に応じて生のSQLに簡単に戻ることができます。Bunは自動マイグレーションを実行しません(マイグレーションを自分で書くか、Bunのmigrateパッケージを使用して手動で実行する必要があります)が、これは制御のための利点と見なされることがあります。Bunは複雑なクエリを洗練された方法で処理し、配列、JSONなどの高度なPostgreSQLの型をデフォルトでサポートしています。実際には、GORMに比べてBunはランタイムオーバーヘッドをほとんど課しません(すべてのクエリで重い反射を実行しない)。これは、高負荷のシナリオでスループットが向上することを意味します。コミュニティは比較的小規模(約4,000スター)であり、機能セットは堅牢ですが、GORMほど広範ではありません。Bunは、効果的に使用するには少しSQLの知識が必要ですが、パフォーマンスと柔軟性を報いることができます。
パフォーマンスベンチマーク
パフォーマンス(クエリのレイテンシーとスループット)に関しては、これらのツールの動作に大きな違いがあり、特に負荷が増加するにつれて顕著になります。最近のベンチマークとユーザーの経験はいくつかの重要な点を強調しています:
-
GORM: GORMの便利さは、パフォーマンスのコストとして現れます。単純な操作や小さな結果セットでは、GORMは非常に速く、あるベンチマークでは非常に小さなクエリ(1または10レコードの取得)でGORMが最も速い実行時間を示しました。しかし、取得するレコード数が増えると、GORMのオーバーヘッドにより大きく遅くなります。15,000レコードを取得するテストでは、GORMはsqlcやraw
database/sql
よりも約2倍遅く(GORM: 59.3ms、sqlc: ~31.7ms、database/sql: 32.0ms)実行されました。反射に依存する設計とクエリ構築の抽象化はレイテンシーを増加させ、GORMは複雑な負荷(Preload
を使用してデフォルトで関連エンティティごとに1つのクエリを発行)で複数のクエリを発行する可能性があります。要約すると: GORMは中規模のワークロードでは問題ありませんが、高スループットのシナリオや大規模なバッチ操作では、そのオーバーヘッドがボトルネックになる可能性があります。 -
sqlc: sqlcの呼び出しは、実際には手書きのSQLであり、標準ライブラリの使用と同様のパフォーマンスを提供します。ベンチマークでは、sqlcが最も高速なオプションの一つとして一貫して評価されており、同等の操作ではraw
database/sql
よりもわずかに遅いまたはわずかに速いことが観測されています。例えば、10,000レコード以上の処理では、sqlcはrawdatabase/sql
よりもスループットでわずかに優れていた(これは、スキャンの boilerplate を避けて効率的なコードジェネレーションを使用しているためと考えられます)。sqlcでは、ランタイムクエリビルダーが存在しないため、クエリは準備されたステートメントとして実行され、最小限のオーバーヘッドが発生します。重要な点は、すでに最適なSQLクエリを書いているということです。sqlcは、あなたにその労力を省いてくれます。実際には、これは優れたスケーラビリティを意味します。sqlcは、raw SQLと同様に大規模なデータロードを処理でき、raw speedが重要な場合に最適な選択肢です。 -
Ent: Entのパフォーマンスは、raw-SQLアプローチと伝統的なORMの間のどこかに位置しています。Entは型安全なコードを生成するため、反射やランタイムクエリ構築のコストを回避します。生成されるSQLは非常に最適化されており(Ent APIを使用して効率的なクエリを書くことが可能です)、Entはいくつかのケースでクエリ実行計画/結果を再利用するための内部キャッシュレイヤーを備えています。これは、複雑なワークフローで繰り返されるデータベースアクセスを減らすことができます。Entと他のツールの具体的なベンチマークは異なりますが、多くの開発者がEntがGORMと同等の操作で優れていると報告しています、特に複雑性が増すにつれて。その理由の一つは、GORMが最適でないクエリを生成する可能性がある(例えば、join/preloadsを慎重に使用しないとN+1 selectsが発生する)が、Entは関係の明示的な急ぎ読み込みを奨励し、少ないクエリでデータを結合します。Entは、GORMよりも1操作あたりのメモリ割当が少ないので、Goのガベージコレクターにかかる圧力を減らし、スループットを向上させます。全体的に、Entは高パフォーマンスと大規模なスキーマを処理するため設計されており、オーバーヘッドは低く、複雑なクエリを効率的に処理できますが、手書きのSQL(sqlc)のすべてのシナリオでrawスループットに匹敵するとは限りません。SQLの手書きが最適な場合、Entは速度とORMレイヤーの安全性の両方を提供する強力な候補です。
-
Bun: Bunはパフォーマンスを考慮して設計されており、GORMの高速な代替としてよく引用されています。SQLクエリを構築するためのフロントAPIを使用しますが、これらのビルダーは軽量です。BunはSQLをあなたに隠しません。これは
database/sql
の薄いレイヤーであり、Go標準ライブラリが行う以上のオーバーヘッドはほとんどありません。ユーザーは、大規模なプロジェクトでGORMからBunに切り替えることで、大きな改善を観測しています。例えば、ある報告では、GORMが「非常に遅い」と彼らのスケールで評価され、それをBun(および一部の生のSQL)に置き換えることでパフォーマンスの問題を解決しました。go-orm-benchmarksなどのベンチマークでは、Bunはほとんどの操作でsqlx/sqlの1.5倍以内に位置し、GORMよりもスループットで優れています。また、バッチ挿入、結合の手動制御、および他の最適化を開発者が活用できるようにもしています。結論: Bunは、メタルに近いパフォーマンスを提供し、ORMの便利さを求めるが速度を犠牲にしたくない場合に最適な選択肢です。SQLファーストの性質により、生成されたクエリが最適でない場合でも最適化を常に可能にします。
パフォーマンスの要約: 最大のパフォーマンスが目標(例: データインテントなアプリケーション、高負荷のマイクロサービス)であれば、sqlcや、さらには手書きのdatabase/sql
コールが勝者です。それらにはほぼない抽象化コストがあり、線形にスケールします。EntとBunも非常に良くパフォーマンスを発揮し、複雑なクエリを効率的に処理できます。それらは速度と抽象化のバランスを取っています。GORMは最も多くの機能を提供しますが、オーバーヘッドのコストがあります。多くのアプリケーションでは完全に受け入れられますが、巨大なデータボリュームや超低レイテンシーを必要とする場合は、その影響に注意する必要があります。(そのようなケースでは、Joins
を使用してPreload
ではなくクエリ数を減らすことでGORMのコストを緩和したり、重要なパスで生のSQLを混ぜたりすることで対処できますが、複雑さが増します。)
開発者体験と使いやすさ
開発者体験は主観的ですが、学習曲線、APIの明確さ、および日常的なコーディングでの生産性を含みます。4つの候補者を比較して、使いやすさと開発ワークフローの観点から以下のように評価します:
-
GORM – 機能が豊富だが学習曲線がある: GORMは、Goの新規開発者が最初に試すORMの一つであり、構造体を定義するだけでSQLを書かずにすぐにCreate/Findできるという完全に自動化された体験を約束します。ドキュメントは非常に包括的であり、多くのガイドが含まれており、これは非常に役立ちます。しかし、GORMには独自の慣用的なやり方(「コードベースのアプローチ」によるDBの相互作用)があり、生のSQLに慣れている場合、最初は使いづらいと感じることがあります。多くの一般的な操作は直感的(例:
db.Find(&objs)
)ですが、関連性やポリモーフィックな関係、または高度なクエリに進むと、GORMの慣習(構造タグ、Preload
vsJoins
など)を学ぶ必要があります。そのため、急な初期学習曲線がよく言及されています。一度その曲線を乗り越えると、GORMは非常に生産的です。繰り返しのSQLを書く時間を減らし、Goのロジックに集中できるようになります。実際、マスターした後、多くの開発者はGORMで特徴を開発する速度が、低レベルのライブラリよりも速いと感じています。要約すると、GORMのDX: 初期の複雑さは高めですが、経験を積むにつれて滑らかになります。大規模なコミュニティにより、多くの例やStackOverflowの回答が利用でき、豊富なプラグインエコシステムによりタスクがさらに簡略化されます(例: ソフト削除、監査などのためのプラグイン)。主な注意点は、GORMが裏で何をしているかを常に意識しておく必要があることです(例: 事故的なN+1クエリ)。しかし、GORMに時間を投資した開発者にとっては、確かに「強力で統合されたソリューション」のように感じられ、多くのことを代わりに処理してくれます。 -
Ent – スキーマファーストで型安全: Entは、ORMの開発者体験を改善するために明確に設計されています。スキーマを1つの場所(Goコード)で記述し、強力な型安全なAPIを使用して作業できます。これは、文字列型のクエリがないことを意味し、多くのエラーがコンパイル時に検出されます。例えば、存在しないフィールドやエッジを参照しようとすると、コードは単純にコンパイルされません。この安全性ネットワークは、大規模なプロジェクトにとって大きなDXの勝利です。Entのクエリを構築するためのAPIは、フロントで直感的です。
.Where(user.EmailEQ("alice@example.com"))
などのメソッドをチェインし、これはほぼ英語のように読めます。他の言語のORMから来た開発者は、Entのアプローチが自然であると感じることが多い(Entity FrameworkやPrismaに似ていますが、Goで)。Entを学ぶには、コードジェネレーションワークフローを理解する必要があります。スキーマを変更するたびにentc generate
(またはgo generate
)を実行する必要があります。これは追加のステップですが、通常はビルドプロセスの一部です。Entの学習曲線は中程度です。GoとSQLの基本を理解している場合、Entの概念(フィールド、エッジなど)は直感的です。実際、多くの開発者は、GORMよりもEntがより簡単に理解できると感じています。なぜなら、SQLが生成されていることを気にする必要がないからです。メソッド名からクエリを予測でき、必要に応じてクエリを出力してデバッグできます。Entのドキュメントと例は非常に良いものであり、会社の支援により積極的にメンテナンスされています。全体的なEntのDX: クリア性と安全性を望む人にとって非常にフレンドリーです。書くコードは生のSQLに比べて冗長ですが、自己ドキュメント化されています。また、リファクタリングはより安全です(スキーマ内のフィールドをリネームし、再生成することでクエリ内のすべての使用を更新します)。デメリットは、Entのコードジェネレーションが提供するものに多少制限される可能性があることです。非常にカスタムなクエリが必要な場合、EntのAPIで書くこともできます(複雑な結合を処理できますが、まれに生のSQLで書いた方が簡単なケースもあります)。Entは必要に応じて生のSQLスニペットを許容しますが、頻繁にそれを行う必要がある場合、ORMが最適な選択肢かどうか疑問に思うかもしれません。要約すると、Entはコードジェネレーションステップを実行し、Entが強制するパターンに従うことを受け入れる限り、CRUDとクエリロジックの大部分に対して非常に滑らかな開発者体験を提供します。 -
Bun – SQLに近い、魔法は少ない: Bunを使用する体験は、GORMやEntを使用する体験とは異なります。Bunの哲学は、SQLを隠さないことです。SQLを知っているなら、Bunの多くはすでに知っていることになります。例えば、ユーザーをクエリするには、
db.NewSelect().Model(&users).Where("name = ?", name).Scan(ctx)
と書くかもしれません。これはフロントAPIですが、実際のSELECTステートメントの構造に非常に近いです。Bunの学習曲線は、SQL自体に精通している場合、一般的に低くです。学ぶべき「ORMの魔法」は少なく、クエリを構築するためのメソッド名と構造タグの慣習を学ぶだけです。これは、database/sql
やsqlx
などの他のクエリビルダーを使用した経験のある熟練開発者にとって非常にアプローチしやすいです。一方で、SQLを書く自信がない初心者にとっては、GORMよりも少し助けが少ないと感じるかもしれません。Bunは、関係を自動的に生成するのではなく、指定しない限り複雑なクエリを自動生成しません。しかし、Bunは関係と急ぎ読み込み(Relation()
メソッドでテーブルを結合)をサポートしています。これは明示的に行うため、一部の開発者は明確さのためにこれを好むかもしれません。Bunの開発者体験は、軽量で予測可能と説明できます。GORMに比べて一部の操作ではコードを少し多く書く必要があります(Bunは多くの場合、列や結合を明示的に指定するように要求します)が、その代わりに、より多くのコントロールと可視性を得られます。内部の魔法は最小限であり、デバッグは容易です(Bunが構築したクエリ文字列を通常はログに記録できます)。Bunのドキュメント(uptrace.devガイド)は詳細で、マイグレーションやトランザクションなどのパターンを含んでいます。ただし、コミュニティが小さいため、第三のチュートリアルは少ないかもしれません。DXのもう一つの側面は、利用可能なツールです。Bunはdatabase/sql
の拡張であるため、sql.DB
(デバッグプロキシ、クエリロガーなど)と動作するツールはBunでも簡単に動作します。要約すると、Bunは構造化されたSQLをGoで書くようなノンセンスな体験を提供します。これは、コントロールとパフォーマンスを重視する開発者にとって素晴らしいですが、GORMのようなものほど初心者を手助けする可能性は低いかもしれません。コンセンサスは、Bunが簡単なことには簡単で、複雑なことには可能(raw SQLのように)であり、あなたに多くのフレームワークを課さないことです。 -
sqlc – SQLを書く、Goコードを得る: sqlcのアプローチは、通常のORMの物語を逆転させます。SQLを書くことでGoコードを得ます。SQLを愛する開発者にとっては、これは素晴らしいことです。ORMの制限なしに、SQLのすべての力(複雑な結合、CTE、ウィンドウ関数など)を使用できます。sqlc自体の学習曲線は非常に小さく、SELECT/INSERT/UPDATEをSQLで書けるなら、難しい部分はすでに完了しています。
-- name: Name :one/many/exec
コメントでクエリを注釈付けし、設定ファイルを設定する必要がありますが、これは簡単です。生成されたコードは、通常のGo関数定義であり、任意のGo関数のように呼び出されます。開発者体験の利点: ORM APIを学ぶ必要がない、驚きがない。クエリは書いた通りに実行されます。ORMが特定のJOINを生成する理由を調べたり、クエリを調整する必要があるという一連のORMの問題を回避できます。また、コードレビューではSQL自体をレビューできるため、複雑なロジックではより明確です。もう一つの大きなDXの利点は、型安全とIDEサポートです。生成されたメソッドと構造体は、エディタでジャンプできます。リファクタリングツールはそれらに適用できます。一方、生の文字列クエリはIDEに不透明です。欠点としては、sqlcはSQLスクリプトを管理する必要があります。つまり、スキーマや要件が変更された場合、関連するSQLを手動で更新し、コードジェネレーションを再実行する必要があります。これは難しくはありませんが、ORMメソッドを呼び出すだけよりも手動の作業が必要です。また、動的なクエリ(SQLの一部が条件付き)は煩雑です。複数のSQLバリアントを書くか、SQLの構文トリックを使用する必要があります。一部の開発者は、これはsqlcのアプローチの制限と見なしています。実際には、データアクセスを構成して、非常に動的なSQLを必要としないようにすることが可能です。または、エッジケースではrawdatabase/sql
を呼び出すことができます。しかし、これは考慮すべき点です:sqlcは明確に定義されたクエリには非常に優れていますが、即席のクエリ構築にはそれほど適していません。要約すると、SQLに精通した開発者にとって、sqlcの使用は自然で非常に効率的です。学ぶべき新しいことはほとんどなく、繰り返しのGoの boilerplate を削除します。SQLにあまり自信がない開発者にとっては、ORMが基本的なCRUDのクエリを自動生成する場合に比べて、sqlcの初期使用は少し遅くなる可能性があります。しかし、多くのGo開発者は、sqlcが手動のコントロールと高安全性を提供するSweet Spotであるため、必須のツールと見なしています。
人気とエコシステムのサポート
採用状況とコミュニティのサポートは、あなたの選択に影響を与える可能性があります。人気のあるライブラリは、コミュニティからの貢献が多いため、メンテナンスが良く、学ぶためのリソースも豊富です。
-
GORM: 四つの中で最も古く、最も成熟しているGORMは、圧倒的に最も多くのユーザーを持つエコシステムを持っています。現在、GitHub上ではGo ORMの中でスター数が38,000を超えています。無数の本番環境プロジェクトで使用されており、メンテナは活発で、プロジェクトは定期的に更新されています(GORM v2はパフォーマンスとアーキテクチャの改善を目的とした大規模な見直しでした)。GORMの普及の大きな利点は、豊富な拡張機能と統合です。PostgreSQL、MySQL、SQLite、SQL Server、ClickHouseなど、データベースドライバの公式および第三者プラグインが豊富に用意されています。GORMは広範なユースケースをサポートしています:マイグレーション、スキーマの自動生成、ソフト削除、JSONフィールド(
gorm.io/datatypes
を使用)、全文検索など、ビルトイン機能またはアドオンを通じて提供されています。コミュニティはgormt
(既存のデータベースから構造定義を生成するためのツール)や、多くのチュートリアルと例を提供しています。問題に遭遇した場合は、検索をすれば、他の誰かが同じ質問をした可能性が高いです。エコシステムの要約: GORMは非常にサポートが充実しています。多くの人にとっての「デフォルト」のORMの選択肢であり、フレームワークやテンプレートに見つけることができます。一方で、その規模が大きいため、重く感じることもあるかもしれませんが、多くの人にとってはコミュニティの深さを支払う価値があります。 -
Ent: Entは新しい(2019年頃にオープンソース化)にもかかわらず、強いコミュニティを築いています。約16,000のスター数とLinux Foundationの支援を受けており、周辺プロジェクトではありません。大規模な企業がEntのスキーマ中心の利点を活用し始め、メンテナ(Ariga)は企業向けのサポートを提供しており、ビジネスクリティカルな使用に自信を示しています。Entのエコシステムは成長しています:OpenAPI/GraphQL統合、gRPC統合、Entスキーマと連携するSQLマイグレーションツールなど、**Ent拡張(ent/go)**があります。Entはコードを生成するため、エコシステムの一部のパターンが異なります。たとえば、GraphQLと統合したい場合は、Entのコード生成を使用してGraphQLリゾルバを生成するかもしれません。Entの学習リソースは良い(公式ドキュメントと、単純なアプリをカバーする例プロジェクトがあります)。コミュニティフォーラムとGitHubのディスカッションは、スキーマ設計の質問やヒントについて活発です。コミュニティサポートに関しては、Entは「早期採用者」の段階を過ぎており、生産性があり信頼性が高いとされています。GORMほどStack Overflowの回答が多くはないかもしれませんが、急速に追いついています。注意すべき点は、Entが少し意見を主張している(たとえば、スキーマを管理したい)ため、他のORMと一緒に使用するよりも単独で使用する可能性が高いです。GORMのように「プラグイン」は持っていないが、カスタムテンプレートを書くことでコード生成を拡張したり、ライフサイクルイベントにハック(Entは生成されたクライアントにハック/ミドルウェアサポートを提供)を組み込むことができます。Foundationの支援は長期的なサポートを示しており、Entが自分のモデルに合っている場合、選ぶのは安全な選択です。
-
Bun: Bun(Uptraceオープンソーススイートの一部)は、現在メンテナンスされていない
go-pg
ライブラリのファンに特に人気があります。約4,300のスター数で、この比較では最も小さなコミュニティですが、非常に活発なプロジェクトです。メンテナは対応が迅速で、機能を急速に追加しています。Bunのコミュニティはパフォーマンスに熱心です。GoフォーラムやRedditで、Bunに切り替えた開発者による議論が見つかります。しかし、ユーザーが少ないため、ニッチな質問に答えがすぐに見つからないこともあります。場合によっては、ドキュメント/ソースを読んだり、BunのGitHubやDiscord(Uptraceはコミュニティチャットを提供)に質問する必要があります。拡張エコシステムは限定的です:Bunには独自のマイグレーションライブラリ、フィクスチャローダー、Uptraceの観測ツールと連携していますが、GORMほど多くのプラグインは見つからないでしょう。ただし、Bunはsql.DB
の使用と互換性があり、混在させることもできます(たとえば、github.com/jackc/pgx
をドライバとして使用したり、*sql.DB
を期待する他のパッケージと統合したり)。Bunはあなたをロックアウトしません。サポートに関しては、新しいためドキュメントは最新で、例は現代的です(コンテキストの使用など)。公式ドキュメントはBunをGORMとEntと直接比較しており、これは役立ちます。コミュニティの規模が心配なら、Bunの利点を活用しつつ、使用を比較的浅く保つ戦略が考えられます(必要に応じて他のソリューションに切り替えることが可能)。いずれにせよ、Bunの軌道は上昇しており、特定のニッチ(パフォーマンス志向のORM)を満たすため、持続可能性があります。 -
sqlc: sqlcはGoコミュニティで非常に人気があり、約15,900のスター数と、特にパフォーマンスに気を配る人々の間で多くの支持者があります。“ORMを避ける"という議論ではよく推奨されることがあり、バランスが良いです。ツールは寄稿者によって維持されており(オリジナルの作者も改善に積極的)、ランタイムライブラリよりもコンパイラとしての役割を果たしています。エコシステムは統合を中心に回っています:たとえば、エディタ/IDEは
.sql
ファイルの構文強調表示をサポートし、sqlc generate
をビルドまたはCIパイプラインの一部として実行します。コミュニティはsqlcでコードを整理する方法のテンプレートやベースリポジトリの例を作成しています(sqlcとFlywayやGolang-Migrateなどのマイグレーションツールを組み合わせてスキーマバージョン管理を行うことが多いです、sqlc自体はスキーマを管理しません)。sqlcの公式Slack/Discordでは質問をしたり、GitHub上のIssueは注目されています。多くの一般的なパターン(nullable値の処理やJSONフィールドなど)はsqlcのドキュメントやコミュニティブログ記事で記載されています。強調すべき点は、sqlcはGoに特化していません:他の言語(TypeScript、Pythonなど)でもコードを生成できます。これにより、Go以外のコミュニティも拡大しますが、Goでは広く尊敬されています。sqlcを選択すれば、多くのスタートアップや大規模な企業(コミュニティの展示やリポジトリに記載されているスポンサー)が生産環境で使用しています。エコシステムの主要な考慮点は、sqlcがORMのようなランタイム機能を提供しないため、トランザクション(sql.Tx
を使用して簡単にsqlcと組み合わせることも可能)や軽量のDALラッパーが必要になる可能性があります。実際には、多くの場合、sqlcは標準ライブラリ(トランザクション、コンテキストキャンセルなど、コード内でdatabase/sql
のイディオムを使用)と併用されます。これは、ベンダーのロックインが少なくなります。sqlcから離れたい場合は、自分のデータレイヤーを書くだけです(SQLを書くのと同じくらい難しい)。全体的に、sqlcのコミュニティとサポートは堅牢で、多くの人がGoプロジェクトがSQLデータベースとやり取りする際の「必須」ツールとして推奨しています。そのシンプルさと信頼性のためです。
フィーチャーと拡張性
これらのツールはそれぞれ異なる一連のフィーチャーを提供しています。ここでは、それぞれの能力と、高度なユースケースでの拡張性を比較します:
- GORMのフィーチャー: GORMはフルサービスのORMを目指しています。そのフィーチャーのリストは非常に広範です:
- 複数のデータベース: PostgreSQL、MySQL、SQLite、SQL Server、その他にも一等のサポートがあります。DBの切り替えは、接続ドライバを変更するだけです。
- マイグレーション: モデルからスキーマを自動的にマイグレーションできます(
db.AutoMigrate(&User{})
はusers
テーブルを作成または変更します)。便利ですが、生産環境では自動マイグレーションの使用に注意してください。多くの人は開発環境で使用し、本番環境ではより制御されたマイグレーションを使用します。 - 関係: GORMの構造タグ(
gorm:"foreignKey:...,references:..."
)を使用して、1対多、多対多などの関係を定義できます。多対多の関係のためのリンクテーブルを処理し、Preload
を使用して関係を急いで読み込むことができます。GORMはデフォルトで遅延読み込み(つまり、別々のクエリ)を使用しますが、Preload
またはJoins
を使用してカスタマイズできます。 - トランザクション: GORMには使いやすいトランザクションラッパー(
db.Transaction(func(tx *gorm.DB) error { ... })
)があり、ネストされたトランザクション(セーブポイント)もサポートしています。 - フックとコールバック: モデル構造体に
BeforeCreate
、AfterUpdate
などのメソッドを定義したり、GORMがライフサイクルイベントで呼び出すグローバルコールバックを登録したりできます。これは、タイムスタンプを自動的に設定したり、ソフト削除の動作を実装したりするのに最適です。 - 拡張性: GORMのプラグインシステム(
gorm.Plugin
インターフェース)により、機能を拡張できます。例:gorm-gen
パッケージは、コンパイル時クエリチェックを好む場合に型安全なクエリメソッドを生成します。または、監査、マルチテナントなど、コミュニティのプラグインもあります。必要に応じていつでもdb.Raw("SELECT ...", params).Scan(&result)
を使用して、生のSQLに戻ることもできます。 - その他の便利な機能: GORMは複合プライマリキー、モデルの埋め込み、ポリモーフィック関連、複数のデータベース(読み取りレプリカ、シャーディングなど)を使用するためのスキーマリゾルバをサポートしています。
全体的に、GORMは非常に拡張性が高く、機能が豊富です。ORMに関連するほぼすべての機能は、GORMにビルトインのメカニズムまたはドキュメント化されたパターンがあります。この幅広さの代償は複雑さとある程度の硬直性(GORMのやり方に従う必要があることが多い)です。しかし、ワンストップのソリューションが必要な場合は、GORMが提供します。
- Entのフィーチャー: Entの哲学はスキーマをコードとしてすることに中心を置いています。主なフィーチャーは以下の通りです:
- スキーマ定義: フィールドに制約(ユニーク、デフォルト値、列挙型など)を定義し、エッジ(関係)にカーディナリティ(1対多など)を定義できます。Entはこれを使用してコードを生成し、また
ent/migrate
コンポーネントを使用して、現在のスキーマと望ましいスキーマの間の差分SQLを生成することもできます。 - 型安全性と検証: フィールドが強く型付けされているため、たとえば、整数フィールドに文字列を誤って設定することはできません。Entはカスタムフィールドタイプ(たとえば、
sql.Scanner
/driver.Valuer
を使用してJSONBフィールドや他の複雑なタイプと統合)も許可します。 - クエリビルダー: Entの生成されたクエリAPIは、ほとんどのSQL構造をカバーします:SELECT、フィルタ、並べ替え、制限、集計、エッジをまたがったJOIN、サブクエリなども可能です。表現力が高く、たとえば、
client.User.Query().WithOrders(func(q *ent.OrderQuery) { q.Limit(5) }).Where(user.StatusEQ(user.StatusActive)).All(ctx)
を使用して、一度にアクティブなユーザーとそれぞれの最初の5つの注文を取得できます。 - トランザクション: Entのクライアントは、クライアントのトランザクショナルなバリアント(
tx, err := client.Tx(ctx)
でent.Tx
を取得し、複数の操作を行い、コミットまたはロールバックできる)をサポートしています。 - フックとミドルウェア: Entはcreate/update/delete操作にフックを登録できます。これらは、たとえば、フィールドを自動で埋め込むか、カスタムルールを強制するためのインターセプタのようなものです。クライアントのミドルウェアも、操作をラップして(ロギングやインストゥルメンテーションに役立ちます)。
- 拡張性: Entには「プラグイン」は厳密にはありませんが、コード生成はテンプレート化されており、必要に応じてカスタムテンプレートを書くことで生成されたコードを拡張できます。多くの高度な機能はこの方法で実装されています:たとえば、OpenTelemetryとの統合でDBコールのトレースを取得する、またはEntスキーマからGraphQLリゾルバを生成するなど。Entは必要に応じて
entsql
パッケージや下位ドライバを介して生のSQLを混在させることもできます。追加のコードを生成できる能力により、チームはEntをベースに必要に応じて独自のパターンをレイヤーに追加できます。 - GraphQL/REST統合: Entのグラフベースアプローチの大きな売りは、GraphQLとの良好な適合性です。Entスキーマはほぼ直接GraphQLスキーマにマッピングできます。その自動化を多くの場合サポートするツールがあります。これは、APIサーバーを構築する際に生産性の向上につながります。
- パフォーマンスの調整: Entは、たとえば、関連エッジをバッチロードしてN+1クエリを避けることができます(急な読み込みAPI
.With<EdgeName>()
があります)。JOINまたは追加のクエリを使用して最適化された方法で内部で使用します。また、クエリ結果のキャッシュ(メモリ内)を有効にすることで、短い時間内に同じクエリを実行しないようにすることもできます。
要約すると、Entのフィーチャーは大規模で、保守可能なプロジェクト向けに設計されています。GORMのいくつかのデフォルトの便利な機能(たとえば、GORMの自動スキーママイグレーションとEntの生成されたマイグレーションスクリプトの違い)は欠けていますが、強力な開発ツールと型安全性で補っています。Entの拡張性は、必要なものを生成することに焦点を当てています。entc(コード生成)の仕組みに深く潜れば、非常に柔軟です。
- Bunのフィーチャー: Bunは、SQLを熟知している人向けの強力なツールに焦点を当てています。いくつかのフィーチャーと拡張性のポイント:
- クエリビルダー: Bunのコアには、SELECTとJOIN、CTE、サブクエリ、INSERT、UPDATE(バッチサポートあり)など、ほとんどのSQL構造をサポートする流暢なクエリビルダーがあります。クエリを開始し、深くカスタマイズし、生のSQLを注入することもできます(
.Where("some_condition (?)", value)
)。 - PostgreSQL特有の機能: BunはPostgreSQLの配列型、JSON/JSONB(
[]<type>
またはmap[string]interface{}
またはカスタム型へのマッピング)、複合型へのスキャンをサポートしています。たとえば、JSONB列がある場合、json:
タグでGo構造体にマッピングし、Bunはマーシャリングを自動で処理します。これは、JSONBを不透明なオブジェクトとして扱ういくつかのORMよりも強みがあります。 - 関係/急な読み込み: 以前に示したように、Bunは構造タグで関係を定義し、クエリで
.Relation("FieldName")
を使用して関連するエンティティをJOINして読み込むことができます。デフォルトでは、.Relation
はLEFT JOINを使用します(INNER JOINや他のタイプをシミュレートするには条件を追加できます)。これにより、関連データの取得方法(1つのクエリか複数のクエリか)を細かく制御できます。手動クエリを好む場合は、BunのSQLビルダーで直接JOINを書くこともできます。GORMとは異なり、Bunはあなたが要求しない限り関係を自動的に読み込まないため、意図せずにN+1の問題を引き起こすことを避けています。 - マイグレーション: Bunは
github.com/uptrace/bun/migrate
に含まれる別途のマイグレーションツールキットを提供しています。マイグレーションはGo(またはSQL文字列)で定義され、バージョン管理されます。GORMのauto-migrateほど自動的ではありませんが、堅牢でライブラリとよく統合されています。これは、スキーマ変更を明示的に保つのに最適です。 - 拡張性: Bunは
database/sql
のインターフェースと類似したインターフェースで構築されており、*sql.DB
をラップしています。そのため、任意の下位レベルのツールを使用できます(必要に応じて*pgx.Conn
クエリを実行し、Bunモデルにスキャンすることも可能です)。Bunはカスタム値スキャナをサポートしており、複雑な型を保存したい場合はsql.Scanner
/driver.Valuer
を実装し、Bunはそれを使用します。Bunの機能を拡張するには、比較的簡単なため、多くの人はプロジェクト内でBunのAPIの上に追加のヘルパー関数を書くだけで、明確なプラグインは書かないことが多いです。ライブラリ自体は進化しており、新しい機能(追加のクエリヘルパーまたは統合)がメンテナによって追加されています。 - その他の機能: Bunは
ctx
を受け取るすべてのクエリをサポートし、database/sql
経由で接続プール(設定可能)を提供し、pgx
を使用している場合、準備されたステートメントのキャッシュをサポートしています。また、Bunは構造ポインタまたはプリミティブスライスに簡単にスキャンできるという便利な機能もあります(たとえば、1つの列を[]string
に直接選択)。このような小さな便利な機能が、繰り返しスキャンコードを嫌う人々にとってBunを楽しむ理由です。
要約すると、BunのフィーチャーはORMの90%のニーズをカバーしていますが、透明性とパフォーマンスに重点を置いています。すべての機能(たとえば、自動的なスキーマ更新やアクティブレコードパターン)が備わっているわけではありませんが、必要なものを実装するための構築ブロックを提供しています。若いツールであるため、実際のユースケースに応じて機能セットが拡張され続けることを期待できます(メンテナはユーザーのリクエストに応じて機能を追加しています)。
- sqlcのフィーチャー: sqlcの「フィーチャー」は、それがジェネレータであるため非常に異なります:
- 完全なSQLサポート: 最も大きなフィーチャーは、PostgreSQL独自の機能を直接使用できるということです。PostgreSQLがサポートしているものはすべてsqlcで使用できます。これはCTE、ウィンドウ関数、JSON演算子、空間クエリ(PostGIS)などです。ライブラリ自体がこれらを使用するために特別なものを実装する必要はありません。
- 型マッピング: sqlcはSQL型をGo型にスマートにマッピングします。標準型を処理し、カスタム型や列挙型のマッピングや拡張を設定または構成できます。たとえば、PostgreSQLの
UUID
はgithub.com/google/uuid
型にマッピングできます。ドメイン型は、下位のGo型にマッピングできます。 - nullの処理: nullable列に対して
sql.NullString
またはポインタを生成できます(好みに応じて)、nullのスキャンに苦労する必要はありません。 - バッチ操作: sqlc自体はバッチ処理のための高レベルAPIを提供していませんが、バッチ挿入SQLを書くことはできます。または、ストアドプロシージャを呼び出すこともできます。これはもう一つのフィーチャー:SQLであるため、ストアドプロシージャやDB関数を活用し、sqlcがGoラッパーを生成できます。
- マルチステートメントクエリ: 複数のSQLステートメントを1つの名前付きクエリに配置し、sqlcはドライバがサポートしている場合、トランザクションで実行し、最後のクエリまたは指定された結果を返します。これは、たとえば、「作成してから選択」を1つのコールで行う方法です。
- 拡張性: コンパイラであるため、拡張性は新しい言語用のプラグインや、コミュニティの貢献による新しいSQL構造のサポートの形で現れます。たとえば、新しいPostgreSQLデータ型が登場した場合、sqlcはそれをサポートするように更新できます。アプリケーションでは、sqlcの周りにラッパー関数を書くことで拡張できます。生成されたコードはあなたの制御下にあるため、変更することもできます(ただし、通常はSQLを調整し、再生成します)。必要に応じて、sqlcと
database/sql
の生のコールをコードベースで混在させることもできます(sqlcはただGoコードを出力するだけなので、衝突はありません)。 - 最小限のランタイム依存: sqlcが生成するコードは通常、標準の
database/sql
(pgxなどの特定のドライバ)に依存します。重いランタイムライブラリはありません。これは、ライブラリ側からの生産環境でのオーバーヘッドがゼロであることを意味します。すべての作業はコンパイル時に完了します。これはまた、オブジェクトキャッシュやアイデンティティマップ(いくつかのORMが提供している)などの機能も得られないことを意味します。キャッシュが必要な場合は、サービスレイヤーで実装したり、別のライブラリを使用したりします。
実際には、sqlcの「フィーチャー」は、SQLデータベースとGoコードの間のギャップを縮めることであり、その間に余分なものを追加しません。これは専門的なツールであり、マイグレーションやオブジェクト状態の追跡などは設計上行いません。これらの問題は他のツールや開発者に任せられます。これは、スリムなデータレイヤーを望む場合に魅力的ですが、コード内でスキーマからクエリ、関係まですべてを処理するワンストップソリューションを探している場合は、sqlcだけではありません。他のパターンやツールと組み合わせる必要があります。
このセクションの要約として、GORMは機能が豊富で、プラグインで調整できる成熟した拡張性のあるORMの明確な選択肢です。Entは、コード生成、フック、APIレイヤーへの統合など、正しさと保守性を重視した現代的な、型安全なフィーチャーのアプローチを提供します。Bunは、SQLの熟練度に依存し、SQLを書くことやわずかなラッパーを書くことで簡単に拡張できます。sqlcは、金属的なフィーチャーにまで縮小し、SQLそのものと同様に機能豊かですが、キャッシュなどはあなたがレイヤーに追加する必要があります。
コード例: 各ORMにおけるCRUD操作
各ツールが単純なモデルに対する基本的なCRUD操作をどのように処理するかを確認するよりも、違いを示すものはありません。User
モデルがあり、ID
、Name
、Email
フィールドがあると仮定します。以下に、PostgreSQLをデータベースとして使用して、各ライブラリにおけるユーザーの作成、読み取り、更新、削除のコードスニペットを並べて示します。
注意: すべてのケースにおいて、既に確立されたデータベース接続(例: db
またはclient
)があると仮定し、エラーハンドリングは簡潔さのために省略しています。目的は、各アプローチのAPIと冗長性を比較することです。
GORM(Active Recordスタイル)
// モデルの定義
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Email string
}
// 新しいユーザーの作成
user := User{Name: "Alice", Email: "alice@example.com"}
db.Create(&user) // INSERT INTO users (name,email) VALUES ('Alice','alice@example.com')
// 読み取り(プライマリキーで検索)
var u User
db.First(&u, user.ID) // SELECT * FROM users WHERE id = X LIMIT 1
// 更新(単一カラム)
db.Model(&u).Update("Email", "alice_new@example.com")
// (生成: UPDATE users SET email='alice_new@example.com' WHERE id = X)
// 削除
db.Delete(&User{}, u.ID) // DELETE FROM users WHERE id = X
Ent(コード生成、フлюイドAPI)
// (Entのスキーマは他の場所で定義され、コードが生成される。clientはent.Clientと仮定)
// 新しいユーザーの作成
u, err := client.User.
Create().
SetName("Alice").
SetEmail("alice@example.com").
Save(ctx)
// SQL: INSERT INTO users (name, email) VALUES ('Alice','alice@example.com') RETURNING id, name, email
// 読み取り(IDで検索)
u2, err := client.User.Get(ctx, u.ID)
// SQL: SELECT * FROM users WHERE id = X
// 更新(単一フィールド)
u3, err := client.User.
UpdateOneID(u.ID).
SetEmail("alice_new@example.com").
Save(ctx)
// SQL: UPDATE users SET email='alice_new@example.com' WHERE id = X RETURNING id, name, email
// 削除
err = client.User.DeleteOneID(u.ID).Exec(ctx)
// SQL: DELETE FROM users WHERE id = X
Bun
// モデルの定義
type User struct {
bun.BaseModel `bun:"table:users"`
ID int64 `bun:",pk,autoincrement"`
Name string `bun:",notnull"`
Email string `bun:",unique,notnull"`
}
// 新しいユーザーの作成
user := &User{Name: "Alice", Email: "alice@example.com"}
_, err := db.NewInsert().Model(user).Exec(ctx)
// INSERT INTO users (name, email) VALUES ('Alice','alice@example.com')
// 読み取り(IDで検索)
var u User
err = db.NewSelect().Model(&u).
Where("id = ?", user.ID).
Scan(ctx)
// SELECT * FROM users WHERE id = X
// 更新(単一カラム)
_, err = db.NewUpdate().Model(&u).
Set("email = ?", "alice_new@example.com").
Where("id = ?", u.ID).
Exec(ctx)
// UPDATE users SET email='alice_new@example.com' WHERE id = X
// 削除
_, err = db.NewDelete().Model((*User)(nil)).
Where("id = ?", u.ID).
Exec(ctx)
// DELETE FROM users WHERE id = X
sqlc
// SQLをファイルに記述し、sqlcがQueries構造体とメソッドを生成したと仮定
queries := New(db) // Newは*sql.DB(またはpgx.Conn)を受け取り、生成されたQueriesを返す
// 新しいユーザーの作成(生成されたメソッドはINSERTを実行し、結果をスキャン)
newUser, err := queries.CreateUser(ctx, "Alice", "alice@example.com")
// queries.sql内のSQL -> INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email
// 読み取り(IDで検索)
user, err := queries.GetUser(ctx, newUser.ID)
// SQL -> SELECT id, name, email FROM users WHERE id = $1
// 更新(IDによるメールアドレスの更新)
updatedUser, err := queries.UpdateUserEmail(ctx, newUser.ID, "alice_new@example.com")
// SQL -> UPDATE users SET email=$2 WHERE id = $1 RETURNING id, name, email
// 削除
err = queries.DeleteUser(ctx, newUser.ID)
// SQL -> DELETE FROM users WHERE id = $1
上記のコードから、各アプローチには異なる特徴があります。
-
GORMは、
db.Model(&obj)
またはdb
オブジェクト上でのフロントチェインを使用して構造体メソッドを使用します。Create
後、user.ID
が設定されるなど、構造体に返された値を自動的に埋め込みます。SQLの詳細を隠蔽するように設計されており、デバッグしない限り、クエリを通常は見ません。 -
Entは、生成されたフロントAPIを使用します。
Create().SetX().Save(ctx)
やUpdateOneID(id).SetX().Save(ctx)
などのメソッドは、構築と実行フェーズを明確に分離しています。Entは、SQLクエリが結果またはエラーを返すように、ent.Type
オブジェクト(行に対応)とエラーを返します。 -
Bunは、明示的に指定する必要がある(例:
Set("email = ?", ...)
で更新)ため、SQLに非常に近いがGoの構文を使用します。挿入後、user
構造体はRETURNING
句を追加しない限り、自動的に新しいIDが設定されません(Bunは必要に応じて.Returning()
をサポートしています)。上記の例では、シンプルなものを保持しています。 -
sqlcは、Go関数を呼び出すように見えます。
queries.CreateUser
などのメソッドを呼び出し、それらは裏で準備されたステートメントを実行します。SQLは外部ファイルに記述されているため、Goコードでは見えませんが、完全なコントロールが可能です。返されるオブジェクト(例:newUser
)は、sqlcによって生成されたデータをモデル化するGo構造体です。
一つの違いは冗長性(GORMは非常に簡潔ですが、BunやsqlcはコードまたはSQLで少し多くのタイピングが必要)とスタイルの違い(EntとGORMは高レベルの抽象化を提供するが、Bunとsqlcは生クエリに近い)です。好みに応じて、明確性を簡潔性よりも重視するか、その逆かもしれません。
TL;DR
Goにおける「正しい」ORMまたはデータベースライブラリの選択は、アプリケーションのニーズとチームの好みに帰結します。
-
GORMは、多くのことを自動的に処理してくれる確立されたORMが望ましい場合に最適な選択です。CRUDアプリケーションの迅速な開発において、利便性と豊富な機能セットがパフォーマンスの最適化よりも重要である場合に特に強みを発揮します。コミュニティサポートとドキュメントは優れており、学習カーブの厳しい部分を滑らかにしてくれます。ただし、適切に機能を使用する必要があります(例:
Preload
やJoins
を使用して、遅延ロードの落とし穴を避ける)。その代償として、生産性と多くの問題に対するワンストップソリューションを得られます。 複数のデータベースサポートや拡張性のあるプラグインエコシステムが必要な場合は、GORMはあなたをサポートします。 -
Entは、型安全性、明確性、保守性を重視する人々に訴えかけます。頻繁なスキーマ変更が発生する大規模なコードベースに適しており、コンパイラがエラーを検出するのを助けてくれます。Entは、スキーマの定義や生成の実行などの初期設計に少し多くの時間を要するかもしれませんが、プロジェクトが成長するにつれてその価値が現れます。コードは堅牢でリファクタリングに適しています。パフォーマンス面では、最適化されたSQL生成とキャッシュにより、アクティブレコードORMよりも重い負荷や複雑なクエリを効率的に処理できます。 Entは、クイックなスクリプトでは「即席」に使えないかもしれませんが、長期にわたるサービスでは堅牢でスケーラブルな基盤を提供します。現代的なアプローチ(および活発な開発)により、将来性のある選択肢です。
-
Bunは、「SQLを知っているし、ただ軽いヘルパーが欲しいだけ」と言う開発者に最適です。いくつかのマジックを省略して、コントロールと速度を提供します。パフォーマンスが重要なサービスを構築し、データアクセスコードで明示的な処理を恐れない場合は、Bunは非常に魅力的な選択肢です。
database/sql
+sqlx
から移行し、構造を加えつつも効率をあまり犠牲にしない中間的な選択肢としても適しています。トレードオフは、コミュニティが小さく、高レベルの抽象化が少ないと、手で少し多くコードを書く必要があります。しかし、Bunのドキュメントに書かれているように、Bunはあなたの邪魔をしません。 PostgreSQLに特化したプロジェクトでデータベース固有の機能を活用したい場合、BunはORMとしてSQLを直接使用する感覚に近い選択肢です。 -
sqlcは、独自のカテゴリに属しています。Goチームが「ORMは不要で、SQLに対するコンパイル時の保証がほしい」と言う場合に最適です。SQLスキルが高く、クエリやスキーマをSQLで管理したい(DBAがいる、または効率的なSQLを書くのが好き)場合、sqlcは生産性と自信を高めるでしょう。パフォーマンスはほぼ最適で、クエリとデータベースの間に何もありません。sqlcを使用しない理由は、SQLを書くのが本当に嫌いであるか、クエリが非常に動的で多くのバリエーションを書くのが負担になる場合だけです。その場合でも、静的なクエリの多くをsqlcで処理し、動的なケースは他のアプローチで処理するかもしれません。sqlcは他のツールともよく連携します——プロジェクトの一部ではGORMを使用し、重要なパスではsqlcを使用する例もあります。簡潔に言えば、明確性、ゼロのオーバーヘッド、型安全なSQLを重視する場合は、sqlcを選択してください。それはあなたのツールボックスに非常に強力なツールです。
最後に、これらのツールはエコシステムにおいて互いに排他的ではありません。それぞれに長所と短所があり、Goの実用的な精神に従って、多くのチームはケースバイケースでトレードオフを評価します。開発速度を重視してGORMやEntから始め、最大のパフォーマンスが必要な特定のホットパスではsqlcやBunを使用するケースは珍しくありません。4つのソリューションはすべて活発にメンテナンスされており、広く使用されているため、全体的に「間違った」選択肢はありません——あなたの文脈に合った「正しい」選択肢を選ぶことです。この比較が、GORM、Ent、Bun、sqlcがどのように対立しているかを明確に示し、次のGoプロジェクトで情報をもとにした決定を助けることを願っています。