1. Einführung in die Entity-Operationen von ent

In diesem Tutorial werden Sie umfassend durch die Beherrschung der Entity-Operationen im ent-Framework geführt, die den vollständigen Prozess der Erstellung, Abfrage, Aktualisierung und Löschung von Entitäten abdecken. Es eignet sich für Anfänger, um sich allmählich in die Kernfunktionalität von ent einzuarbeiten.

3. Entity-Erstellungsoperation

3.1 Erstellung einer einzelnen Entität

Die Erstellung einer Entität ist die grundlegende Operation zur Datenpersistenz. Im Folgenden finden Sie die Schritte zur Erstellung eines einzelnen Entitätsobjekts mithilfe des ent-Frameworks und zur Speicherung in der Datenbank:

  1. Definieren Sie zunächst die Struktur und Felder einer Entität, d. h. definieren Sie das Modell der Entität in der schema-Datei.
  2. Führen Sie den Befehl ent generate aus, um den entsprechenden Entitätsoperationscode zu generieren.
  3. Verwenden Sie die generierte Create-Methode, um eine neue Entität zu erstellen, und setzen Sie die Feldwerte der Entität durch verkettete Aufrufe.
  4. Rufen Sie schließlich die Save-Methode auf, um die Entität in der Datenbank zu speichern.

Im Folgenden finden Sie ein Beispiel, das zeigt, wie eine Benutzerentität erstellt und gespeichert wird:

package main

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

func main() {
    // Erstellen einer Client-Instanz für die Datenbankinteraktion
    client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("Fehler beim Öffnen der Datenbankverbindung: %v", err)
    }
    defer client.Close()

    // Erstellen eines Kontexts
    ctx := context.Background()
    
    // Erstellen einer Benutzerentität mithilfe des Clients
    a8m, err := client.User.
        Create().
        SetName("a8m").
        Save(ctx)
    if err != nil {
        log.Fatalf("Fehler beim Erstellen der Benutzerentität: %v", err)
    }

    // Entität erfolgreich in der Datenbank gespeichert
    log.Printf("Benutzerentität gespeichert: %v", a8m)
}

In diesem Beispiel wird zunächst ein Datenbankclient client erstellt. Anschließend wird die Methode User.Create verwendet, um die Attribute des neuen Benutzers festzulegen, und schließlich wird die Methode Save aufgerufen, um den Benutzer in der Datenbank zu speichern.

3.2 Batch-Entitätserstellung

In bestimmten Szenarien kann es erforderlich sein, mehrere Entitäten zu erstellen, z. B. während der Datenbankinitialisierung oder bei Massendatenimportoperationen. Das ent-Framework ermöglicht die Erstellung von Entitäten im Batch, was im Vergleich zur individuellen Erstellung und Speicherung von Entitäten eine bessere Leistung bietet.

Die Schritte zur Batch-Entitätserstellung lauten wie folgt:

  1. Verwenden Sie die Methode CreateBulk anstelle der Methode Create, die die Erstellung mehrerer Entitäten in einer einzigen Operation ermöglicht.
  2. Rufen Sie für jede zu erstellende Entität Create auf.
  3. Sobald alle Entitäten erstellt wurden, verwenden Sie die Methode Save, um die Entitäten in der Datenbank im Batch zu speichern.

Im Folgenden finden Sie ein Beispiel zur Batch-Entitätserstellung:

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("Fehler beim Öffnen der Datenbankverbindung: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Batch-Erstellung von Pet-Entitäten
    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("Fehler beim Batch-Erstellen von Pet-Entitäten: %v", err)
    }

    log.Printf("In Batch %d Pet-Entitäten erstellt\n", len(pets))
}

In diesem Beispiel wird zuerst ein client erstellt, und dann werden mehrere Pet-Entitäten mithilfe der Methode CreateBulk erstellt, wobei ihre Namen und Besitzerfelder festgelegt werden. Alle Entitäten werden gleichzeitig in der Datenbank gespeichert, wenn die Methode Save aufgerufen wird, was eine bessere Leistung für die Verarbeitung großer Datenmengen bietet.

4. Entitätsabfrageoperationen

4.1 Grundlegende Abfrage

Datenbankabfragen sind der grundlegende Weg, um Informationen abzurufen. In ent wird die Methode Query verwendet, um eine Abfrage zu starten. Nachfolgend sind die Schritte und ein Beispiel für grundlegende Entity-Abfragen aufgeführt:

  1. Stellen Sie sicher, dass Sie eine verwendbare Client-Instanz haben.
  2. Verwenden Sie Client.Query() oder Entity-Hilfsmethoden wie Pet.Query(), um eine Abfrage zu erstellen.
  3. Fügen Sie bei Bedarf Filterbedingungen hinzu, wie z. B. Where.
  4. Führen Sie die Abfrage aus und rufen Sie die Ergebnisse durch Aufruf der Methode All ab.
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("Fehler beim Öffnen der Datenbankverbindung: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Abfrage aller Benutzer mit dem Namen "a8m"
    users, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).
        All(ctx)
    if err != nil {
        log.Fatalf("Fehler beim Abfragen von Benutzern: %v", err)
    }

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

Dieses Beispiel zeigt, wie alle Benutzer mit dem Namen "a8m" gefunden werden.

4.2 Seitenumbruch und Sortierung

Seitenumbruch und Sortierung sind häufig verwendete erweiterte Funktionen bei Abfragen, um die Ausgabereihenfolge und -menge von Daten zu steuern. So funktioniert die Umsetzung von Seitenumbruch- und Sortierabfragen mit ent:

  1. Verwenden Sie die Methode Limit, um die maximale Anzahl der zurückzugebenden Ergebnisse festzulegen.
  2. Verwenden Sie die Methode Offset, um einige der vorherigen Ergebnisse zu überspringen.
  3. Verwenden Sie die Methode Order, um das Sortierfeld und die Richtung anzugeben.

Hier ist ein Beispiel für eine Seitenumbruch- und Sortierabfrage:

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("Fehler beim Öffnen der Datenbankverbindung: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Abfrage von Haustieren in absteigender Reihenfolge nach Alter mit Seitenumbruch
    pets, err := client.Pet.
        Query().
        Order(ent.Desc(pet.FieldAge)).
        Limit(10).
        Offset(0).
        All(ctx)
    if err != nil {
        log.Fatalf("Fehler beim Abfragen von Haustieren: %v", err)
    }

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

Dieses Beispiel zeigt, wie die erste Seite mit bis zu 10 Datensätzen von Haustieren in absteigender Reihenfolge nach Alter abgerufen wird. Durch Ändern der Werte von Limit und Offset können Sie das gesamte Datensatz durchblättern.

5. Entity-Aktualisierungsvorgänge

5.1 Aktualisieren einer einzelnen Entity

In vielen Anwendungen ist die Aktualisierung von Entities ein wesentlicher Bestandteil des täglichen Betriebs. In diesem Abschnitt wird gezeigt, wie das Ent-Framework verwendet wird, um eine einzelne Entity in der Datenbank zu aktualisieren.

Angenommen, wir müssen das Alter eines Benutzers aktualisieren, können wir die von Ent generierte Update-Methode nutzen.

// Angenommen, wir haben bereits eine Benutzer-Entity 'a8m' und einen Kontext 'ctx'

a8m, err := a8m.Update().         // Erstellen eines Benutzer-Aktualisierungs-Builder
    SetAge(30).                   // Setzen des Alters des Benutzers auf 30 Jahre
    Save(ctx)                     // Durchführen der Speicheroperation und Rückgabe des Ergebnisses
if err != nil {
    log.Fatalf("Fehler beim Aktualisieren des Benutzers: %v", err)
}

Sie können auch mehrere Felder gleichzeitig aktualisieren:

a8m, err := a8m.Update().
    SetAge(30).                    // Alter aktualisieren
    SetName("Ariel").              // Name aktualisieren
    AddRank(10).                   // Rang um 10 erhöhen
    Save(ctx)
if err != nil {
    log.Fatalf("Fehler beim Aktualisieren des Benutzers: %v", err)
}

Die Aktualisierung kann verkettet werden, was sehr flexibel und leicht lesbar ist. Durch Aufruf der Methode Save wird die Aktualisierung durchgeführt und die aktualisierte Entity oder eine Fehlermeldung zurückgegeben.

5.2 Bedingte Aktualisierungen

Ent ermöglicht es Ihnen, Aktualisierungen basierend auf Bedingungen durchzuführen. Hier ist ein Beispiel, bei dem nur Benutzer, die bestimmte Bedingungen erfüllen, aktualisiert werden.

// Angenommen, wir haben die `id` eines Benutzers und wir möchten diesen Benutzer als erledigt für die Version `currentVersion` markieren

err := client.Todo.
    UpdateOneID(id).          // Erstellen eines Builders zur Aktualisierung anhand der Benutzer-ID
    SetStatus(todo.StatusDone).
    AddVersion(1).
    Where(
        todo.Version(currentVersion),  // Die Aktualisierung wird nur ausgeführt, wenn die aktuelle Version übereinstimmt
    ).
    Exec(ctx)
switch {
case ent.IsNotFound(err):
    fmt.Println("Aufgabe nicht gefunden")
case err != nil:
    fmt.Println("Fehler bei der Aktualisierung:", err)
}

Bei der Verwendung von bedingten Aktualisierungen muss die Methode .Where() involviert sein. Dies ermöglicht es Ihnen zu bestimmen, ob die Aktualisierung basierend auf den aktuellen Werten in der Datenbank durchgeführt werden soll, was entscheidend für die Gewährleistung der Datenkonsistenz und -integrität ist.

6. Entitätslöschoperationen

6.1 Löschen einer Einzelnen Entität

Das Löschen von Entitäten ist eine weitere wichtige Funktion bei Datenbankoperationen. Das Ent-Framework bietet eine einfache API für Löschvorgänge.

Das folgende Beispiel zeigt, wie man eine bestimmte Benutzerentität löscht:

err := client.User.
    DeleteOne(a8m).  // Erstellen eines Benutzerlösch-Builders
    Exec(ctx)        // Führen Sie die Löschoperation aus
if err != nil {
    log.Fatalf("Fehler beim Löschen des Benutzers: %v", err)
}

6.2 Bedingtes Löschen

Ähnlich wie bei Aktualisierungsvorgängen können auch Löschvorgänge basierend auf spezifischen Bedingungen durchgeführt werden. In bestimmten Szenarien möchten wir möglicherweise nur Entitäten löschen, die bestimmte Bedingungen erfüllen. Mit der Methode .Where() können diese Bedingungen definiert werden:

// Angenommen, wir möchten alle Dateien löschen, die vor einem bestimmten Datum aktualisiert wurden

betroffen, err := client.File.
    Delete().
    Where(file.UpdatedAtLT(date)).  //führen Sie die Löschung nur aus, wenn die Aktualisierungszeit der Datei früher als das angegebene Datum ist
    Exec(ctx)
if err != nil {
    log.Fatalf("Fehler beim Löschen der Dateien: %v", err)
}

// Diese Operation gibt die Anzahl der durch den Löschvorgang betroffenen Datensätze zurück
fmt.Printf("%d Dateien wurden gelöscht\n", betroffen)

Durch die Verwendung von bedingten Löschvorgängen wird eine präzise Kontrolle über unsere Datenoperationen sichergestellt, wodurch nur Entitäten gelöscht werden, die tatsächlich die Bedingungen erfüllen. Dies verbessert die Sicherheit und Zuverlässigkeit der Datenbankoperationen.