1. Wprowadzenie do operacji na jednostkach w ent

Ten samouczek wyczerpująco przeprowadzi cię przez opanowanie operacji na jednostkach w frameworku ent, obejmując cały proces tworzenia, wyszukiwania, aktualizacji i usuwania jednostek. Jest odpowiedni dla początkujących, aby stopniowo zagłębić się w podstawową funkcjonalność ent.

3. Operacja tworzenia jednostki

3.1 Tworzenie pojedynczej jednostki

Tworzenie jednostki jest podstawową operacją dotyczącą trwałości danych. Poniżej znajdują się kroki tworzenia pojedynczego obiektu jednostki przy użyciu frameworku ent i zapisania go do bazy danych:

  1. Najpierw zdefiniuj strukturę i pola jednostki, czyli zdefiniuj model jednostki w pliku schema.
  2. Uruchom polecenie ent generate, aby wygenerować odpowiadający kod operacji na jednostkach.
  3. Użyj wygenerowanej metody Create, aby utworzyć nową jednostkę i ustawiaj wartości pól jednostki za pomocą łańcuchowych wywołań.
  4. Na koniec wywołaj metodę Save, aby zapisać jednostkę w bazie danych.

Poniżej znajduje się przykład, który demonstruje, jak utworzyć i zapisać jednostkę użytkownika:

package main

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

func main() {
    // Utwórz instancję klienta do interakcji z bazą danych
    client, err := ent.Open("sqlite3", "plik:ent?mode=pamięć&cache=współdzielona&_fk=1")
    if err != nil {
        log.Fatalf("Nie udało się otworzyć połączenia z bazą danych: %v", err)
    }
    defer client.Close()

    // Utwórz kontekst
    ctx := context.Background()
    
    // Utwórz jednostkę użytkownika za pomocą klienta
    a8m, err := client.User.
        Create().
        SetName("a8m").
        Save(ctx)
    if err != nil {
        log.Fatalf("Nie udało się utworzyć jednostki użytkownika: %v", err)
    }

    // Jednostka pomyślnie zapisana w bazie danych
    log.Printf("Jednostka użytkownika została zapisana: %v", a8m)
}

W tym przykładzie najpierw utworzono klienta bazy danych client. Następnie, metoda User.Create jest używana do ustawienia atrybutów nowego użytkownika, a na końcu wywoływana jest metoda Save, aby zapisać użytkownika w bazie danych.

3.2 Tworzenie jednostek wsadowo

W pewnych sytuacjach może pojawić się potrzeba tworzenia wielu jednostek, na przykład podczas inicjalizacji bazy danych lub operacji importu danych wsadowego. Framework ent umożliwia tworzenie jednostek wsadowo, co oferuje lepszą wydajność w porównaniu z tworzeniem i zapisywaniem jednostek indywidualnie.

Kroki tworzenia jednostek wsadowo są następujące:

  1. Użyj metody CreateBulk zamiast metody Create, która umożliwia tworzenie wielu jednostek w pojedynczej operacji.
  2. Wywołaj Create dla każdej jednostki do utworzenia.
  3. Po utworzeniu wszystkich jednostek, użyj metody Save, aby zapisać jednostki w bazie danych wsadowo.

Poniżej znajduje się przykład tworzenia jednostek wsadowo:

package main

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

func main() {
    client, err := ent.Open("sqlite3", "plik:ent?mode=pamięć&cache=współdzielona&_fk=1")
    if err != nil {
        log.Fatalf("Nie udało się otworzyć połączenia z bazą danych: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Tworzenie jednostek Pet wsadowo
    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("Nie udało się wsadowo utworzyć jednostek Pet: %v", err)
    }

    log.Printf("Utworzono %d jednostek Pet wsadowo\n", len(pets))
}

W tym przykładzie najpierw utworzono klienta client, a następnie utworzono wiele jednostek Pet za pomocą metody CreateBulk, ustawiając ich nazwy i właściciela. Wszystkie jednostki są zapisywane w bazie danych jednocześnie, gdy wywoływana jest metoda Save, co zapewnia lepszą wydajność w obsłudze dużej ilości danych.

4. Operacje wyszukiwania jednostek

4.1 Podstawowe zapytania

Zapytania do bazy danych są podstawowym sposobem pobierania informacji. W ent metoda Query jest używana do rozpoczęcia zapytania. Poniżej znajdują się kroki i przykład podstawowego zapytania dotyczącego encji:

  1. Upewnij się, że masz użyteczną instancję Client.
  2. Użyj Client.Query() lub metod pomocniczych encji, takich jak Pet.Query() do utworzenia zapytania.
  3. Dodaj warunki filtrowania, jeśli są potrzebne, takie jak Where.
  4. Wykonaj zapytanie i pobierz wyniki, wywołując metodę 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("Nie udało się otworzyć połączenia z bazą danych: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Zapytaj o wszystkich użytkowników o nazwie "a8m"
    users, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).
        All(ctx)
    if err != nil {
        log.Fatalf("Nie udało się zapytać użytkowników: %v", err)
    }

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

Ten przykład demonstruje, jak znaleźć wszystkich użytkowników o nazwie "a8m".

4.2 Paginacja i Sortowanie

Paginacja i sortowanie to powszechne zaawansowane funkcje podczas zapytań, używane do kontroli kolejności wyników i ilości danych. Oto jak osiągnąć zapytania paginacji i sortowania za pomocą ent:

  1. Użyj metody Limit do ustawienia maksymalnej liczby wyników do zwrócenia.
  2. Użyj metody Offset do pominięcia niektórych poprzednich wyników.
  3. Użyj metody Order do określenia pola sortowania i kierunku.

Oto przykład zapytania paginacji i sortowania:

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("Nie udało się otworzyć połączenia z bazą danych: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Zapytaj o zwierzęta w malejącej kolejności wieku z paginacją
    pets, err := client.Pet.
        Query().
        Order(ent.Desc(pet.FieldAge)).
        Limit(10).
        Offset(0).
        All(ctx)
    if err != nil {
        log.Fatalf("Nie udało się zapytać zwierząt: %v", err)
    }

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

Ten przykład demonstruje, jak pobrać pierwszą stronę, do 10 rekordów, zwierząt posortowanych malejąco według wieku. Poprzez modyfikację wartości Limit i Offset, można osiągnąć przeglądanie danych przez cały zbiór.

5. Operacje aktualizacji encji

5.1 Aktualizacja pojedynczej encji

W wielu aplikacjach aktualizacja encji jest istotną częścią codziennych operacji. W tej sekcji pokażemy, jak użyć frameworka Ent do aktualizacji pojedynczej encji w bazie danych.

Załóżmy, że chcemy zaktualizować wiek użytkownika, możemy skorzystać z metody Update generowanej przez Ent.

// Zakładając, że mamy już encję użytkownika 'a8m' i kontekst 'ctx'

a8m, err := a8m.Update().         // Utwórz generator aktualizacji użytkownika
    SetAge(30).                   // Ustaw wiek użytkownika na 30 lat
    Save(ctx)                     // Wykonaj operację zapisu i zwróć wynik
if err != nil {
    log.Fatalf("Nie udało się zaktualizować użytkownika: %v", err)
}

Można również aktualizować wiele pól jednocześnie:

a8m, err := a8m.Update().
    SetAge(30).                    // Zaktualizuj wiek
    SetName("Ariel").              // Zaktualizuj nazwę
    AddRank(10).                   // Zwiększ rangę o 10
    Save(ctx)
if err != nil {
    log.Fatalf("Nie udało się zaktualizować użytkownika: %v", err)
}

Operację aktualizacji można łańcuchować, co jest bardzo elastyczne i łatwe do odczytu. Wywołanie metody Save spowoduje wykonanie aktualizacji i zwrócenie zaktualizowanej encji lub komunikatu o błędzie.

5.2 Aktualizacje Warunkowe

Ent pozwala na wykonywanie aktualizacji na podstawie warunków. Oto przykład, w którym aktualizowane będą tylko użytkownicy, którzy spełniają określone warunki.

// Zakładając, że mamy `id` użytkownika i chcemy oznaczyć tego użytkownika jako zakończonego dla wersji `currentVersion`

err := client.Todo.
    UpdateOneID(id).          // Tworzymy konstruktor aktualizacji według identyfikatora użytkownika
    SetStatus(todo.StatusDone).
    AddVersion(1).
    Where(
        todo.Version(currentVersion),  // Operacja aktualizacji jest wykonywana tylko, gdy bieżąca wersja pasuje
    ).
    Exec(ctx)
switch {
case ent.IsNotFound(err):
    fmt.Println("Zadanie nie znalezione")
case err != nil:
    fmt.Println("Błąd aktualizacji:", err)
}

Podczas korzystania z aktualizacji warunkowych, konieczne jest użycie metody .Where(). Umożliwia to określenie, czy aktualizacja powinna być wykonana na podstawie bieżących wartości w bazie danych, co jest kluczowe dla zapewnienia spójności i integralności danych.

6. Operacje Usuwania Elementów

6.1 Usuwanie Jednego Elementu

Usuwanie elementów to kolejna istotna funkcja w operacjach na bazie danych. Framework Ent udostępnia prosty interfejs API do wykonywania operacji usuwania.

Poniższy przykład pokazuje, jak usunąć podanego użytkownika:

err := client.User.
    DeleteOne(a8m).  // Tworzymy konstruktor usuwania użytkownika
    Exec(ctx)        // Wykonujemy operację usuwania
if err != nil {
    log.Fatalf("Nie udało się usunąć użytkownika: %v", err)
}

6.2 Usuwanie Warunkowe

Podobnie jak w operacjach aktualizacji, możemy również wykonywać operacje usuwania na podstawie określonych warunków. W pewnych scenariuszach chcemy być w stanie usunąć tylko elementy spełniające określone warunki. Używając metody .Where() możemy zdefiniować te warunki:

// Załóżmy, że chcemy usunąć wszystkie pliki z datą aktualizacji wcześniejszą niż zadana data

affected, err := client.File.
    Delete().
    Where(file.UpdatedAtLT(date)).  // Wykonujemy usuwanie tylko jeśli czas aktualizacji pliku jest wcześniejszy niż podana data
    Exec(ctx)
if err != nil {
    log.Fatalf("Nie udało się usunąć plików: %v", err)
}

// Ta operacja zwraca liczbę rekordów dotkniętych operacją usuwania
fmt.Printf("%d plików zostało usuniętych\n", affected)

Wykorzystanie operacji usunięcia warunkowego zapewnia precyzyjną kontrolę nad operacjami na danych, upewniając się, że zostaną usunięte tylko elementy, które rzeczywiście spełniają warunki. Wzmocni to bezpieczeństwo i niezawodność operacji na bazie danych.