1. Introduction aux opérations d'entité ent

Ce tutoriel vous guidera de manière exhaustive dans la maîtrise des opérations d'entité dans le framework ent, couvrant l'ensemble du processus de création, de requête, de mise à jour et de suppression d'entités. Il convient aux débutants pour plonger progressivement dans les fonctionnalités essentielles de ent.

3. Opération de création d'entité

3.1 Création d'une seule entité

La création d'une entité est l'opération fondamentale pour la persistance des données. Voici les étapes pour créer un seul objet entité en utilisant le framework ent et le sauvegarder dans la base de données:

  1. Tout d'abord, définissez la structure et les champs d'une entité, c'est-à-dire définissez le modèle de l'entité dans le fichier schema.
  2. Exécutez la commande ent generate pour générer le code d'opération d'entité correspondant.
  3. Utilisez la méthode Create générée pour construire une nouvelle entité, et définissez les valeurs des champs de l'entité à travers des appels enchaînés.
  4. Enfin, appelez la méthode Save pour sauvegarder l'entité dans la base de données.

L'exemple suivant démontre comment créer et sauvegarder une entité utilisateur :

package main

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

func main() {
    // Créer une instance Client pour interagir avec la base de données
    client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("Impossible d'ouvrir la connexion à la base de données: %v", err)
    }
    defer client.Close()

    // Créer un contexte
    ctx := context.Background()
    
    // Créer une entité utilisateur en utilisant le Client
    a8m, err := client.User.
        Create().
        SetName("a8m").
        Save(ctx)
    if err != nil {
        log.Fatalf("Impossible de créer l'entité utilisateur: %v", err)
    }

    // Entité sauvegardée avec succès dans la base de données
    log.Printf("Entité utilisateur sauvegardée: %v", a8m)
}

Dans cet exemple, un client de base de données client est créé en premier. Ensuite, la méthode User.Create est utilisée pour définir les attributs du nouvel utilisateur, et enfin, la méthode Save est appelée pour sauvegarder l'utilisateur dans la base de données.

3.2 Création d'entité en lot

Dans certains scénarios, il peut être nécessaire de créer plusieurs entités, comme lors de l'initialisation de la base de données ou des opérations d'importation de données en masse. Le framework ent offre la possibilité de créer des entités en lots, ce qui offre de meilleures performances par rapport à la création et à la sauvegarde d'entités individuellement.

Les étapes pour la création d'entité en lot sont les suivantes :

  1. Utilisez la méthode CreateBulk au lieu de la méthode Create, qui permet la création de multiples entités en une seule opération.
  2. Appelez Create pour chaque entité à créer.
  3. Une fois que toutes les entités ont été créées, utilisez la méthode Save pour sauvegarder les entités dans la base de données en lot.

Voici un exemple de création d'entité en lot :

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("Impossible d'ouvrir la connexion à la base de données: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Créer en lot des entités 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("Impossible de créer en lot des entités Pet: %v", err)
    }

    log.Printf("Création de %d entités Pet en lot\n", len(pets))
}

Dans cet exemple, un client est créé en premier, puis plusieurs entités Pet sont construites en utilisant la méthode CreateBulk, définissant leurs noms et champs propriétaire. Toutes les entités sont sauvegardées dans la base de données en une seule fois lorsque la méthode Save est appelée, offrant de meilleures performances pour la gestion de grandes quantités de données.

4. Opérations de requête d'entité

4.1 Requête de base

Interroger la base de données est le moyen fondamental de récupérer des informations. Dans ent, la méthode Query est utilisée pour démarrer une requête. Voici les étapes et un exemple de requête d'entité de base :

  1. Assurez-vous d'avoir une instance utilisable de Client.
  2. Utilisez Client.Query() ou les méthodes d'aide d'entité telles que Pet.Query() pour créer une requête.
  3. Ajoutez les conditions de filtrage selon les besoins, telles que Where.
  4. Exécutez la requête et récupérez les résultats en appelant la méthode 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("Échec de l'ouverture de la connexion à la base de données : %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Requête de tous les utilisateurs nommés "a8m"
    users, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).
        All(ctx)
    if err != nil {
        log.Fatalf("Échec de la requête des utilisateurs : %v", err)
    }

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

Cet exemple démontre comment trouver tous les utilisateurs avec le nom "a8m".

4.2 Pagination et Tri

La pagination et le tri sont des fonctionnalités avancées couramment utilisées lors des requêtes, utilisées pour contrôler l'ordre de sortie et la quantité de données. Voici comment réaliser des requêtes de pagination et de tri à l'aide de ent :

  1. Utilisez la méthode Limit pour définir le nombre maximal de résultats à renvoyer.
  2. Utilisez la méthode Offset pour sauter certains des résultats précédents.
  3. Utilisez la méthode Order pour spécifier le champ de tri et la direction.

Voici un exemple de requête de pagination et de tri :

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("Échec de l'ouverture de la connexion à la base de données : %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Requête des animaux de compagnie dans l'ordre décroissant de l'âge avec pagination
    pets, err := client.Pet.
        Query().
        Order(ent.Desc(pet.FieldAge)).
        Limit(10).
        Offset(0).
        All(ctx)
    if err != nil {
        log.Fatalf("Échec de la requête des animaux de compagnie : %v", err)
    }

    for _, p := range pets {
        log.Printf("Animal de compagnie trouvé : %#v\n", p)
    }
}

Cet exemple démontre comment récupérer la première page, jusqu'à 10 enregistrements, d'animaux de compagnie triés par ordre décroissant d'âge. En modifiant les valeurs de Limit et Offset, vous pouvez effectuer la pagination de l'ensemble des données.

5. Opérations de mise à jour d'entité

5.1 Mise à jour d'une seule entité

Dans de nombreuses applications, la mise à jour des entités est une partie essentielle des opérations quotidiennes. Dans cette section, nous allons démontrer comment utiliser le framework Ent pour mettre à jour une seule entité dans la base de données.

Tout d'abord, supposons que nous devons mettre à jour l'âge d'un utilisateur, nous pouvons utiliser la méthode Update générée par Ent.

// En supposant que nous avons déjà une entité utilisateur 'a8m' et un contexte 'ctx'

a8m, err := a8m.Update().         // Créer un constructeur de mise à jour d'utilisateur
    SetAge(30).                   // Définir l'âge de l'utilisateur à 30 ans
    Save(ctx)                     // Effectuer l'opération de sauvegarde et retourner le résultat
if err != nil {
    log.Fatalf("Échec de la mise à jour de l'utilisateur : %v", err)
}

Vous pouvez également mettre à jour plusieurs champs simultanément :

a8m, err := a8m.Update().
    SetAge(30).                    // Mettre à jour l'âge
    SetName("Ariel").              // Mettre à jour le nom
    AddRank(10).                   // Augmenter le rang de 10
    Save(ctx)
if err != nil {
    log.Fatalf("Échec de la mise à jour de l'utilisateur : %v", err)
}

L'opération de mise à jour peut être chaînée, ce qui est très flexible et facile à lire. L'appel de la méthode Save effectuera la mise à jour et renverra l'entité mise à jour ou un message d'erreur.

5.2 Mises à jour conditionnelles

Ent vous permet d'effectuer des mises à jour basées sur des conditions. Voici un exemple où seuls les utilisateurs répondant à des conditions spécifiques seront mis à jour.

// Supposons que nous avons l'`id` d'un utilisateur et que nous voulons marquer cet utilisateur comme étant terminé pour la version `currentVersion`

err := client.Todo.
    UpdateOneID(id).          // Créer un constructeur pour la mise à jour par ID utilisateur
    SetStatus(todo.StatusDone).
    AddVersion(1).
    Where(
        todo.Version(currentVersion),  // L'opération de mise à jour est uniquement exécutée lorsque la version actuelle correspond
    ).
    Exec(ctx)
switch {
case ent.IsNotFound(err):
    fmt.Println("Todo non trouvé")
case err != nil:
    fmt.Println("Erreur de mise à jour :", err)
}

Lors de l'utilisation de mises à jour conditionnelles, la méthode .Where() doit être impliquée. Cela vous permet de déterminer si la mise à jour doit être effectuée en fonction des valeurs actuelles dans la base de données, ce qui est crucial pour garantir la cohérence et l'intégrité des données.

6. Opérations de suppression d'entité

6.1 Suppression d'une seule entité

La suppression d'entités est une autre fonction importante dans les opérations de base de données. Le framework Ent fournit une API simple pour effectuer des opérations de suppression.

L'exemple suivant montre comment supprimer une entité utilisateur donnée :

err := client.User.
    DeleteOne(a8m).  // Créer un constructeur de suppression d'utilisateur
    Exec(ctx)        // Exécuter l'opération de suppression
if err != nil {
    log.Fatalf("Échec de la suppression de l'utilisateur : %v", err)
}

6.2 Suppression conditionnelle

Tout comme les opérations de mise à jour, nous pouvons également effectuer des opérations de suppression basées sur des conditions spécifiques. Dans certains scénarios, nous ne voulons peut-être supprimer que des entités répondant à des conditions spécifiques. L'utilisation de la méthode .Where() permet de définir ces conditions :

// Supposons que nous voulons supprimer tous les fichiers dont l'heure de mise à jour est antérieure à une certaine date

affected, err := client.File.
    Delete().
    Where(file.UpdatedAtLT(date)).  // N'exécutez la suppression que si l'heure de mise à jour du fichier est antérieure à la date donnée
    Exec(ctx)
if err != nil {
    log.Fatalf("Échec de la suppression des fichiers : %v", err)
}

// Cette opération renvoie le nombre d'enregistrements touchés par l'opération de suppression
fmt.Printf("%d fichiers ont été supprimés\n", affected)

L'utilisation d'opérations de suppression conditionnelles assure un contrôle précis sur nos opérations de données, garantissant que seules les entités répondant véritablement aux conditions sont supprimées. Cela améliore la sécurité et la fiabilité des opérations de base de données.