1. معرفی عملیات ش ent Entity

در این آموزش، شما به طور جامع به راهنمایی در مسلط شدن بر روی عملیات entity در چارچوب ent در زمینه ایجاد، جستجو، به‌روزرسانی و حذف entities می‌پردازید. این آموزش برای مبتدیان مناسب است تا به تدریج در اصلی‌ترین قابلیت‌های ent فرو روند.

3. عملیات ایجاد Entity

3.1 ایجاد یک Entity تکی

ایجاد یک entity عملیات بنیادی برای دوام داده‌ها است. مراحل زیر نحوه ایجاد یک شی entity تکی با استفاده از چارچوب ent و ذخیره آن در پایگاه داده را نشان می‌دهد:

  1. ابتدا ساختار و فیلدهای یک entity یعنی تعریف مدل entity را در فایل schema تعریف کنید.
  2. دستور ent generate را برای تولید کد عملیات entity موردنظر بزنید.
  3. از Create تولیدشده برای ساخت یک entity جدید و تنظیم مقادیر فیلدهای entity از طریق فراخوانی‌های پی‌درپی استفاده کنید.
  4. در نهایت از Save برای ذخیره entity در پایگاه داده استفاده کنید.

مثال زیر نحوه ایجاد و ذخیره‌سازی یک entity کاربر را نشان می‌دهد:

package main

import (
    "context"
    "log"
    "entdemo/ent"
)

func main() {
    // ایجاد نمونه Client برای تعامل با پایگاه داده
    client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("خطا در باز کردن اتصال به پایگاه داده: %v", err)
    }
    defer client.Close()

    // ایجاد یک context
    ctx := context.Background()
    
    // ایجاد یک entity کاربر با استفاده از Client
    a8m, err := client.User.
        Create().
        SetName("a8m").
        Save(ctx)
    if err != nil {
        log.Fatalf("خطا در ایجاد entity کاربر: %v", err)
    }

    // ذخیره entity با موفقیت در پایگاه داده
    log.Printf("Entity کاربر ذخیره شد: %v", a8m)
}

در این مثال، ابتدا یک مشتری پایگاه داده client ایجاد می‌شود. سپس از User.Create برای تنظیم ویژگی‌های کاربر جدید استفاده شده و در نهایت از Save برای ذخیره کاربر در پایگاه داده صدا زده می‌شود.

3.2 ایجاد Entity در دسته

در برخی حالات، ممکن است نیاز به ایجاد چندین entity، مانند هنگام شروع پایگاه داده یا عملیات واردات داده از بزرگ‌ترین حجم داده‌ها وجود داشته باشد. چارچوب ent امکان ایجاد entity در دسته را فراهم می‌کند، که ارائه کارایی بهتر نسبت به ایجاد و ذخیره entityها به صورت جداگانه دارد.

مراحل ایجاد entity در دسته به شرح زیر است:

  1. از متد CreateBulk به جای متد Create استفاده کنید که امکان ایجاد چندین entity در یک عملیات را فراهم می‌کند.
  2. برای هر entity مورد نظر، Create را فراخوانی کنید.
  3. هنگامی که تمام entityها ایجاد شده باشند، از Save برای ذخیره entityها در پایگاه داده به صورت دسته‌ای استفاده کنید.

مثال زیر نحوه ایجاد entity در دسته را نشان می‌دهد:

package main

import (
    "context"
    "log"
    "entdemo/ent"
)

func main() {
    client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("خطا در باز کردن اتصال به پایگاه داده: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // ایجاد entityهای Pet به صورت دسته‌ای
    pets, err := client.Pet.CreateBulk(
        client.Pet.Create().SetName("pedro").SetOwner(a8m),
        client.Pet.Create().SetName("xabi").SetOwner(a8m),
        client.Pet.Create().SetName("layla").SetOwner(a8m),
    ).Save(ctx)
    if err != nil {
        log.Fatalf("خطا در ایجاد دسته‌ای entityهای Pet: %v", err)
    }

    log.Printf("در دسته %d entity Pet ایجاد شد\n", len(pets))
}

در این مثال، ابتدا یک client ایجاد می‌شود و سپس چندین entity Pet با استفاده از متد CreateBulk ساخته شده و نام‌ها و فیلدهای مالک آن‌ها تنظیم می‌شود. زمانی که متد Save فراخوانی می‌شود، تمام entityها به صورت یکجا در پایگاه داده ذخیره می‌شوند که بهبود عملکرد در مدیریت حجم بزرگ داده فراهم می‌آورد.

4. عملیات پرس‌وجوی Entity

4.1 جستجوی پایه

جستجو در پایگاه‌داده روش اساسی برای بازیابی اطلاعات است. در ent، از متد Query برای شروع یک جستجو استفاده می‌شود. در زیر مراحل و یک مثال از جستجوی پایه موجودیت آورده شده است:

  1. مطمئن شوید که نمونه قابل استفاده از Client دارید.
  2. از Client.Query() یا از متدهای کمکی موجودیت مانند Pet.Query() برای ایجاد یک جستجو استفاده کنید.
  3. شرایط فیلتر کردن مورد نیاز را اضافه کنید، مانند Where.
  4. جستجو را انجام داده و نتایج آن را با فراخوانی متد All دریافت کنید.
package main

import (
    "context"
    "log"
    "entdemo/ent"
    "entdemo/ent/user"
)

func main() {
    client, err := ent.Open("sqlite3", "file:ent?cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("Failed to open the database connection: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Query all users named "a8m"
    users, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).
        All(ctx)
    if err != nil {
        log.Fatalf("Failed to query users: %v", err)
    }

    for _, u := range users {
        log.Printf("Found user: %#v\n", u)
    }
}

این مثال نشان می‌دهد چگونه می‌توانید تمامی کاربران با نام "a8m" را پیدا کنید.

4.2 صفحه‌بندی و مرتب‌سازی

صفحه‌بندی و مرتب‌سازی ویژگی‌های پیشرفته‌ای هستند که هنگام جستجو از آن‌ها استفاده می‌شود تا ترتیب و تعداد خروجی داده را کنترل کنند. در زیر چگونگی انجام جستجوهای صفحه‌بندی و مرتب‌سازی با استفاده از ent آمده است:

  1. از متد Limit برای تعیین حداکثر تعداد نتایج برگردانده شده استفاده کنید.
  2. از متد Offset برای رد کردن بعضی از نتایج قبلی استفاده کنید.
  3. از متد Order برای تعیین فیلد و جهت مرتب‌سازی استفاده کنید.

در زیر مثالی از یک جستجوی صفحه‌بندی و مرتب‌سازی آمده است:

package main

import (
    "context"
    "log"
    "entdemo/ent"
    "entdemo/ent/pet"
)

func main() {
    client, err := ent.Open("sqlite3", "file:ent?cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("Failed to open the database connection: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Query Pets in descending order of age with pagination
    pets, err := client.Pet.
        Query().
        Order(ent.Desc(pet.FieldAge)).
        Limit(10).
        Offset(0).
        All(ctx)
    if err != nil {
        log.Fatalf("Failed to query Pets: %v", err)
    }

    for _, p := range pets {
        log.Printf("Found Pet: %#v\n", p)
    }
}

این مثال نشان می‌دهد چگونه می‌توانید صفحه اول، تا 10 رکورد، از حیوانات خانگی مرتب شده بر اساس سن به صورت نزولی را دریافت کنید. با تغییر مقادیر Limit و Offset می‌توانید از طریق کل مجموعه داده‌ها صفحه‌بندی کنید.

5. عملیات به‌روزرسانی موجودیت

5.1 به‌روزرسانی یک موجودیت تکی

در بسیاری از برنامه‌ها، به‌روزرسانی موجودیت‌ها بخش اساسی از عملیات روزانه هستند. در این بخش، نشان می‌دهیم چگونه از چارچوب Ent برای به‌روزرسانی یک موجودیت تکی در پایگاه‌داده استفاده کنیم.

در ابتدا، فرض کنید که می‌خواهیم سن یک کاربر را به‌روز کنیم، می‌توانیم از متد Update تولید شده توسط Ent استفاده کنیم.

// فرضا که قبلا موجودیت کاربر 'a8m' و یک محیط 'ctx' داریم

a8m, err := a8m.Update().         // ایجاد یک سازنده به‌روزرسانی کاربر
    SetAge(30).                   // تنظیم سن کاربر به 30 سال
    Save(ctx)                     // انجام عملیات ذخیره‌سازی و بازگرداندن نتیجه
if err != nil {
    log.Fatalf("ناتوان در به‌روزرسانی کاربر: %v", err)
}

همچنین می‌توانید همزمان چندین فیلد را به‌روز کنید:

a8m, err := a8m.Update().
    SetAge(30).                    // به‌روزرسانی سن
    SetName("Ariel").              // به‌روزرسانی نام
    AddRank(10).                   // افزایش رتبه به 10
    Save(ctx)
if err != nil {
    log.Fatalf("ناتوان در به‌روزرسانی کاربر: %v", err)
}

عملیات به‌روزرسانی قابل زنجیره‌سازی است که بسیار انعطاف‌پذیر و آسان برای خواندن است. فراخوانی متد Save عمل به‌روزرسانی را انجام می‌دهد و موجودیت به‌روزرسانی شده یا پیام خطایی را باز می‌گرداند.

5.2 به‌روزرسانی‌های شرطی

Ent به شما این امکان را می‌دهد که به‌روزرسانی‌هایی را براساس شرایط انجام دهید. در زیر مثالی آمده که فقط کاربرانی که شرایط خاصی را برآورده می‌کنند به‌روزرسانی می‌شوند.

// فرض کنید ما `id` یک کاربر را داریم و می‌خواهیم این کاربر را به عنوان انجام شده برای نسخه `currentVersion` علامت بزنیم

err := client.Todo.
    UpdateOneID(id).          // یک سازنده برای به‌روزرسانی بر اساس شناسه کاربر ایجاد کنید
    SetStatus(todo.StatusDone).
    AddVersion(1).
    Where(
        todo.Version(currentVersion),  // عملیات به‌روزرسانی تنها زمانی اجرا می‌شود که نسخه فعلی مطابقت داشته باشد
    ).
    Exec(ctx)
switch {
case ent.IsNotFound(err):
    fmt.Println("کاربری یافت نشد")
case err != nil:
    fmt.Println("خطا در به‌روزرسانی:", err)
}

در استفاده از به‌روزرسانی‌های شرطی، باید از متد .Where() استفاده کرد. این به شما این امکان را می‌دهد که تعیین کنید آیا به‌روزرسانی باید بر اساس مقادیر فعلی در پایگاه داده صورت بگیرد یا خیر که برای حفظ سازگاری و اصالت داده بسیار حیاتی است.

6. عملیات حذف موجودیت

6.1 حذف یک موجودیت تکی

حذف موجودیت‌ها یک عملیات مهم دیگر در عملیات پایگاه داده است. چارچوب Ent یک API ساده برای انجام عملیات حذف فراهم می‌کند.

مثال زیر نحوه حذف یک موجودیت کاربر داده شده را نشان می‌دهد:

err := client.User.
    DeleteOne(a8m).  // یک سازنده حذف کاربر ایجاد کنید
    Exec(ctx)        // عملیات حذف را اجرا کنید
if err != nil {
    log.Fatalf("ناتوان در حذف کاربر: %v", err)
}

6.2 حذف شرطی

مشابه عملیات به‌روزرسانی، ما همچنین می‌توانیم عملیات حذف را براساس شرایط خاصی انجام دهیم. در برخی صورت‌ها، ممکن است فقط بخواهیم موجودیت‌هایی که شرایط خاصی را برآورده می‌کنند را حذف کنیم. استفاده از متد .Where() این شرایط را تعریف می‌کند:

// فرض کنید می‌خواهیم تمامی فایل‌هایی که زمان به‌روزرسانی آن‌ها قبل از یک تاریخ خاص است را حذف کنیم

affected, err := client.File.
    Delete().
    Where(file.UpdatedAtLT(date)).  // حذف را تنها زمانی اجرا کنید که زمان به‌روزرسانی فایل قبل از تاریخ داده شده باشد
    Exec(ctx)
if err != nil {
    log.Fatalf("ناتوان در حذف فایل‌ها: %v", err)
}

// این عملیات تعداد رکوردهای تحت تأثیر عملیات حذف را برمی‌گرداند
fmt.Printf("تعداد %d فایل حذف شده است\n", affected)

استفاده از عملیات حذف شرطی، کنترل دقیق بر عملیات داده‌ای ما را فراهم می‌کند و اطمینان حاصل می‌کند که فقط موجودیت‌هایی که واقعاً شرایط را برآورده می‌کنند حذف می‌شوند. این امر امنیت و قابلیت اطمینان عملیات پایگاه داده را بهبود می‌بخشد.