PostgreSQL के लिए Go ORMs की तुलना: GORM vs Ent vs Bun vs sqlc

GO में ORMs पर एक व्यावहारिक, कोड-भरी दृष्टिकोण

Page content

गो के लिए सबसे प्रमुख ORMs (Object-Relational Mappers) GORM, Ent, Bun और sqlc हैं। यहाँ हम इनके तुलनात्मक अध्ययन के साथ गो में CRUD (Create, Read, Update, Delete) ऑपरेशन्स के उदाहरण देखेंगे।

golang + postgresql

TL;DR

  • GORM: विशेषताओं से भरा और सुविधाजनक; “जल्दी शिप करने” के लिए सबसे आसान, लेकिन अधिक रनटाइम ओवरहेड है।
  • Ent (entgo.io): स्कीमा-कोड के साथ उत्पन्न, टाइप-सेफ API; बड़े कोडबेस और रिफैक्टरिंग के लिए उत्कृष्ट है।
  • Bun: हल्का, SQL-फर्स्ट क्वेरी बिल्डर/ORM; तेज़ है, उत्कृष्ट पोस्टग्रेस विशेषताएं, डिज़ाइन में स्पष्ट है।
  • sqlc (ORM नहीं): SQL लिखें, टाइप-सेफ गो प्राप्त करें; सर्वोत्तम कच्चा प्रदर्शन और नियंत्रण, कोई रनटाइम मजिक नहीं।

चयन मानदंड और तुलनात्मक अध्ययन

मेरे मानदंड हैं:

  • प्रदर्शन: लेटेंसी/थ्रूपुट, टालने योग्य ओवरहेड, बैच ऑपरेशन्स।
  • डेवलपर एक्सपीरियंस: सीखने की कर्व, टाइप सेफ्टी, डिबगिंग क्षमता, कोडजेन फ्रिक्शन।
  • इकोसिस्टम: दस्तावेज़, उदाहरण, गतिविधि, इंटीग्रेशन्स (माइग्रेशन्स, ट्रेसिंग)।
  • विशेषताएं: रिलेशन्स, ईगर लोडिंग, माइग्रेशन्स, हुक्स, कच्चे SQL एस्केप हैचेस।
टूल पैराडाइम टाइप सेफ्टी रिलेशन्स माइग्रेशन्स कच्चे SQL एर्गोनॉमिक्स टाइपिकल यूज केस
GORM एक्टिव रिकॉर्ड-स्टाइल ORM मध्यम (रनटाइम) हाँ (टैग्स, Preload/Joins) ऑटो-माइग्रेट (ऑप्ट-इन) db.Raw(...) तेज़ डिलीवरी, समृद्ध विशेषताएं, पारंपरिक CRUD ऐप्स
Ent स्कीमा → कोडजेन → फ्लुएंट API उच्च (कंपाइल-टाइम) प्रथम श्रेणी (एजेस) उत्पन्न SQL (अलग चरण) entsql, कस्टम SQL बड़े कोडबेस, रिफैक्टरिंग-हैवी टीम्स, स्ट्रिक्ट टाइपिंग
Bun SQL-फर्स्ट क्वेरी बिल्डर/ORM मध्यम–उच्च स्पष्ट (Relation) अलग माइग्रेट पैकेज प्राकृतिक (बिल्डर + कच्चा) प्रदर्शन-संवेदनशील सर्विसेज, पोस्टग्रेस विशेषताएं
sqlc SQL → कोडजेन फंक्शन्स (ORM नहीं) उच्च (कंपाइल-टाइम) SQL जॉइन्स के माध्यम से बाहरी टूल (उदाहरण के लिए, golang-migrate) यह है SQL अधिक नियंत्रण और गति; DBA-मित्र टीम्स

उदाहरण द्वारा CRUD

सेटअप (PostgreSQL)

pgx या टूल के नेटिव PG ड्राइवर का उपयोग करें। उदाहरण DSN:

export DATABASE_URL='postgres://user:pass@localhost:5432/app?sslmode=disable'

इम्पोर्ट्स (सभी ORMs के लिए सामान्य)

प्रत्येक go कोड उदाहरण वाले फ़ाइल के शुरुआत में जोड़ें:

import (
  "context"
  "os"
)

हम एक सरल users टेबल मॉडल करेंगे:

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

GORM

इनिशियलाइज़

import (
  "gorm.io/driver/postgres"
  "gorm.io/gorm"
)

type User struct {
  ID    int64  `gorm:"primaryKey"`
  Name  string
  Email string `gorm:"uniqueIndex"`
}

func newGorm() (*gorm.DB, error) {
  dsn := os.Getenv("DATABASE_URL")
  return gorm.Open(postgres.Open(dsn), &gorm.Config{})
}

// ऑटो-माइग्रेट (वैकल्पिक; प्रोडक्शन में सावधानी से)
func migrate(db *gorm.DB) error { return db.AutoMigrate(&User{}) }

CRUD

func gormCRUD(ctx context.Context, db *gorm.DB) error {
  // बनाना
  u := User{Name: "Alice", Email: "alice@example.com"}
  if err := db.WithContext(ctx).Create(&u).Error; err != nil { return err }

  // पढ़ना
  var got User
  if err := db.WithContext(ctx).First(&got, u.ID).Error; err != nil { return err }

  // अपडेट करना
  if err := db.WithContext(ctx).Model(&got).
    Update("email", "alice+1@example.com").Error; err != nil { return err }

  // हटाना
  if err := db.WithContext(ctx).Delete(&User{}, got.ID).Error; err != nil { return err }

  return nil
}

नोट्स

  • रिलेशन्स स्ट्रक्च टैग्स + Preload/Joins के माध्यम से।
  • ट्रांजैक्शन हेल्पर: db.Transaction(func(tx *gorm.DB) error { ... }).

Ent

स्कीमा परिभाषा (ent/schema/user.go में):

package schema

import (
  "entgo.io/ent"
  "entgo.io/ent/schema/field"
)

type User struct {
  ent.Schema
}

func (User) Fields() []ent.Field {
  return []ent.Field{
    field.Int64("id").Unique().Immutable(),
    field.String("name"),
    field.String("email").Unique(),
  }
}

कोड उत्पन्न करना

go run entgo.io/ent/cmd/ent generate ./ent/schema

इनिशियलाइज़

import (
  "entgo.io/ent/dialect"
  "entgo.io/ent/dialect/sql"
  _ "github.com/jackc/pgx/v5/stdlib"
  "your/module/ent"
)

func newEnt() (*ent.Client, error) {
  dsn := os.Getenv("DATABASE_URL")
  drv, err := sql.Open(dialect.Postgres, dsn)
  if err != nil { return nil, err }
  return ent.NewClient(ent.Driver(drv)), nil
}

CRUD

func entCRUD(ctx context.Context, client *ent.Client) error {
  // बनाना
  u, err := client.User.Create().
    SetName("Alice").
    SetEmail("alice@example.com").
    Save(ctx)
  if err != nil { return err }

  // पढ़ना
  got, err := client.User.Get(ctx, u.ID)
  if err != nil { return err }

  // अपडेट करना
  if _, err := client.User.UpdateOneID(got.ID).
    SetEmail("alice+1@example.com").
    Save(ctx); err != nil { return err }

  // हटाना
  if err := client.User.DeleteOneID(got.ID).Exec(ctx); err != nil { return err }

  return nil
}

नोट्स

  • अंत से अंत तक मजबूत टाइपिंग; रिलेशन्स के लिए एजेस।
  • उत्पन्न माइग्रेशन्स या अपने पसंदीदा माइग्रेशन टूल का उपयोग करें।

Bun

इनिशियलाइज़

import (
  "database/sql"

  "github.com/uptrace/bun"
  "github.com/uptrace/bun/dialect/pgdialect"
  _ "github.com/jackc/pgx/v5/stdlib"
)

type User struct {
  bun.BaseModel `bun:"table:users"`
  ID    int64  `bun:",pk,autoincrement"`
  Name  string `bun:",notnull"`
  Email string `bun:",unique,notnull"`
}

func newBun() (*bun.DB, error) {
  dsn := os.Getenv("DATABASE_URL")
  sqldb, err := sql.Open("pgx", dsn)
  if err != nil { return nil, err }
  return bun.NewDB(sqldb, pgdialect.New()), nil
}

CRUD

func bunCRUD(ctx context.Context, db *bun.DB) error {
  // बनाना
  u := &User{Name: "Alice", Email: "alice@example.com"}
  if _, err := db.NewInsert().Model(u).Exec(ctx); err != nil { return err }

  // पढ़ना
  var got User
  if err := db.NewSelect().Model(&got).
    Where("id = ?", u.ID).
    Scan(ctx); err != nil { return err }

  // अपडेट करना
  if _, err := db.NewUpdate().Model(&got).
    Set("email = ?", "alice+1@example.com").
    WherePK().
    Exec(ctx); err != nil { return err }

  // हटाना
  if _, err := db.NewDelete().Model(&got).WherePK().Exec(ctx); err != nil { return err }

  return nil
}

नोट्स

  • स्पष्ट जॉइन्स/ईगर लोडिंग .Relation("...") के साथ।
  • माइग्रेशन्स के लिए अलग bun/migrate पैकेज।

sqlc

sqlc एक ORM नहीं है. आप SQL लिखते हैं; यह टाइप-सेफ गो विधियाँ उत्पन्न करता है।

sqlc.yaml

version: "2"
sql:
  - engine: postgresql
    queries: db/queries
    schema: db/migrations
    gen:
      go:
        package: db
        out: internal/db
        sql_package: "database/sql" # या "github.com/jackc/pgx/v5"

क्वेरीज़ (db/queries/users.sql)

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

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

-- name: UpdateUserEmail :one
UPDATE users SET email = $2 WHERE id = $1
RETURNING id, name, email;

-- name: DeleteUser :exec
DELETE FROM users WHERE id = $1;

उत्पन्न करना

sqlc generate

उपयोग

import (
  "database/sql"
  _ "github.com/jackc/pgx/v5/stdlib"
  "your/module/internal/db"
)

func sqlcCRUD(ctx context.Context) error {
  dsn := os.Getenv("DATABASE_URL")
  sqldb, err := sql.Open("pgx", dsn)
  if err != nil { return err }
  q := db.New(sqldb)

  // बनाना
  u, err := q.CreateUser(ctx, db.CreateUserParams{
    Name: "Alice", Email: "alice@example.com",
  })
  if err != nil { return err }

  // पढ़ना
  got, err := q.GetUser(ctx, u.ID)
  if err != nil { return err }

  // अपडेट करना
  up, err := q.UpdateUserEmail(ctx, db.UpdateUserEmailParams{
    ID: got.ID, Email: "alice+1@example.com",
  })
  if err != nil { return err }

  // हटाना
  if err := q.DeleteUser(ctx, up.ID); err != nil { return err }

  return nil
}

नोट्स

  • अपने माइग्रेशन्स लाएं (उदाहरण के लिए, golang-migrate)।
  • डायनामिक क्वेरीज़ के लिए: कई SQL वैरिएंट्स लिखें या एक छोटे बिल्डर के साथ मिलाएं।

प्रदर्शन नोट्स

  • GORM: सुविधाजनक लेकिन रिफ्लेक्शन/अभिव्यक्ति ओवरहेड जोड़ता है। सामान्य CRUD के लिए ठीक है; N+1 क्वेरीज़ से बचें (पसंद करें Joins या चयनात्मक Preload)।
  • Ent: उत्पन्न कोड रिफ्लेक्शन से बचाता है; जटिल स्कीमाओं के लिए अच्छा है। अक्सर भारी, रनटाइम-मजिक ORMs से तेज़ होता है।
  • Bun: database/sql पर पतला; तेज़, स्पष्ट, बैच ऑपरेशन्स और बड़े रिज़ल्ट सेट्स के लिए अच्छा है।
  • sqlc: लगभग कच्चे SQL प्रदर्शन के साथ कंपाइल-टाइम सुरक्षा।

सामान्य टिप्स

  • ड्राइवर के लिए pgx का उपयोग करें (v5) और हर जगह context का उपयोग करें।
  • उच्च थ्रूपुट के लिए बैचिंग का पसंद करें (COPY, मल्टी-रो INSERT)।
  • SQL को प्रोफ़ाइल करें: EXPLAIN ANALYZE, इंडेक्स, कवरिंग इंडेक्स, अनावश्यक राउंडट्रिप्स से बचें।
  • कनेक्शन्स को पुनः उपयोग करें; वर्कलोड के आधार पर पूल साइज़ ट्यून करें।

डेवलपर अनुभव और पारिस्थितिकी

  • GORM: सबसे बड़ा समुदाय, बहुत सारे उदाहरण/प्लगइन्स; उन्नत पैटर्न्स के लिए अधिक सीखने की आवश्यकता।
  • Ent: उत्कृष्ट दस्तावेज़; कोडजन स्टेप मुख्य मानसिक मॉडल परिवर्तन है; सुपर रीफैक्टर-फ्रेंडली।
  • Bun: पढ़ने में आसान, पूर्वानुमेय क्वेरीज़; छोटा लेकिन सक्रिय समुदाय; उत्कृष्ट पोस्टग्रेस सुविधाएं।
  • sqlc: न्यूनतम रनटाइम डिपेंडेंसीज़; माइग्रेशन टूल्स और CI के साथ अच्छी तरह से एकीकृत; टीम्स के लिए उत्कृष्ट जो SQL में आरामदायक हैं।

विशेषताएं

  • संबंध और ईगर लोडिंग: सभी संबंधों को हैंडल करते हैं; GORM (टैग्स + Preload/Joins), Ent (एजेस + .With...()), Bun (Relation(...)), sqlc (आप जोइंस लिखते हैं).
  • माइग्रेशन: GORM (ऑटो-माइग्रेट; प्रोडक्शन में सावधानी), Ent (जेनरेटेड/डिफ SQL), Bun (bun/migrate), sqlc (बाहरी टूल्स).
  • हुक्स/एक्सटेंसिबिलिटी: GORM (कॉलबैक्स/प्लगइन्स), Ent (हुक्स/मिडलवेयर + टेम्पलेट/कोडजन), Bun (क्वेरी हुक्स, आसान रॉ SQL), sqlc (आपके एप्लिकेशन लेयर में कंपोज).
  • JSON/एरेयज़ (Postgres): Bun और GORM के पास अच्छे हेल्पर्स हैं; Ent/sqlc कस्टम टाइप्स या SQL के माध्यम से हैंडल करते हैं।

कब और किसे चुनना

  • GORM चुनें अगर आप अधिकतम सुविधा, समृद्ध विशेषताएं, और पारंपरिक CRUD सेवाओं के लिए तेज़ प्रोटोटाइपिंग चाहते हैं।
  • Ent चुनें अगर आप कंपाइल-टाइम सुरक्षा, स्पष्ट स्कीमास, और बड़े टीम्स में लंबे समय तक रखरखाव का मूल्य देते हैं।
  • Bun चुनें अगर आप प्रदर्शन और ORM सुविधाओं के साथ स्पष्ट SQL-आकार क्वेरीज़ चाहते हैं जहां यह मदद करता है।
  • sqlc चुनें अगर आप (और आपकी टीम) टाइप-सेफ गो बाइंडिंग्स के साथ शुद्ध SQL और शून्य रनटाइम ओवरहेड पसंद करते हैं।

स्थानीय पोस्टग्रेस के लिए न्यूनतम docker-compose.yml

version: "3.8"
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: app
    ports: ["5432:5432"]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d app"]
      interval: 5s
      timeout: 3s
      retries: 5

GO में ORM पैकेज और लाइब्रेरी

अन्य उपयोगी लिंक्स