1. Introdução às Operações de Entidades ent

Este tutorial irá guiá-lo de forma abrangente para dominar as operações de entidades no framework ent, cobrindo o processo completo de criação, consulta, atualização e exclusão de entidades. É adequado para iniciantes mergulharem gradualmente na funcionalidade central do ent.

3. Operação de Criação de Entidades

3.1 Criação de uma Única Entidade

Criar uma entidade é a operação fundamental para persistência de dados. Abaixo estão os passos para criar um único objeto de entidade usando o framework ent e salvá-lo no banco de dados:

  1. Primeiro, defina a estrutura e os campos de uma entidade, ou seja, defina o modelo da entidade no arquivo schema.
  2. Execute o comando ent generate para gerar o código de operação de entidade correspondente.
  3. Use o método Create gerado para construir uma nova entidade e definir os valores dos campos da entidade por meio de chamadas encadeadas.
  4. Por fim, chame o método Save para salvar a entidade no banco de dados.

O exemplo a seguir demonstra como criar e salvar uma entidade de usuário:

package main

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

func main() {
    // Crie uma instância do Cliente para interação com o banco de dados
    cliente, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("Falha ao abrir a conexão com o banco de dados: %v", err)
    }
    defer cliente.Close()

    // Crie um contexto
    ctx := context.Background()
    
    // Crie uma entidade de usuário usando o Cliente
    a8m, err := cliente.User.
        Create().
        SetName("a8m").
        Save(ctx)
    if err != nil {
        log.Fatalf("Falha ao criar a entidade de usuário: %v", err)
    }

    // Entidade salva com sucesso no banco de dados
    log.Printf("Entidade de usuário salva: %v", a8m)
}

Neste exemplo, um cliente de banco de dados cliente é criado primeiro. Em seguida, o método User.Create é usado para definir os atributos do novo usuário e, por fim, o método Save é chamado para salvar o usuário no banco de dados.

3.2 Criação em Lote de Entidades

Em certos cenários, pode haver a necessidade de criar múltiplas entidades, como durante a inicialização do banco de dados ou operações de importação em massa de dados. O framework ent fornece a capacidade de criar entidades em lotes, oferecendo melhor desempenho em comparação com a criação e salvamento de entidades individualmente.

Os passos para a criação em lote de entidades são os seguintes:

  1. Use o método CreateBulk em vez do método Create, o qual permite a criação de múltiplas entidades em uma única operação.
  2. Chame Create para cada entidade a ser criada.
  3. Uma vez que todas as entidades tenham sido criadas, use o método Save para salvá-las no banco de dados em lote.

Aqui está um exemplo de criação em lote de entidades:

package main

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

func main() {
    cliente, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("Falha ao abrir a conexão com o banco de dados: %v", err)
    }
    defer cliente.Close()
    ctx := context.Background()

    // Crie em lote entidades Pet
    pets, err := cliente.Pet.CreateBulk(
        cliente.Pet.Create().SetName("pedro").SetOwner(a8m),
        cliente.Pet.Create().SetName("xabi").SetOwner(a8m),
        cliente.Pet.Create().SetName("layla").SetOwner(a8m),
    ).Save(ctx)
    if err != nil {
        log.Fatalf("Falha ao criar em lote as entidades Pet: %v", err)
    }

    log.Printf("Criadas %d entidades Pet em lote\n", len(pets))
}

Neste exemplo, um cliente é criado primeiro e, em seguida, várias entidades Pet são construídas usando o método CreateBulk, definindo seus nomes e campos de proprietário. Todas as entidades são salvas no banco de dados de uma vez quando o método Save é chamado, proporcionando melhor desempenho para lidar com grandes quantidades de dados.

4.1 Consulta Básica

A consulta de banco de dados é a forma fundamental de recuperar informações. Em ent, o método Query é usado para iniciar uma consulta. Abaixo estão os passos e um exemplo de consulta básica de entidades:

  1. Verifique se você tem uma instância utilizável de Client.
  2. Use Client.Query() ou os métodos auxiliares da entidade como Pet.Query() para criar uma consulta.
  3. Adicione condições de filtragem conforme necessário, como Where.
  4. Execute a consulta e recupere os resultados chamando o método 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("Falha ao abrir a conexão com o banco de dados: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Consultar todos os usuários com o nome "a8m"
    users, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).
        All(ctx)
    if err != nil {
        log.Fatalf("Falha ao consultar os usuários: %v", err)
    }

    for _, u := range users {
        log.Printf("Usuário encontrado: %#v\n", u)
    }
}

Este exemplo demonstra como encontrar todos os usuários com o nome "a8m".

4.2 Paginação e Ordenação

A paginação e ordenação são recursos avançados comumente utilizados ao consultar, usados para controlar a ordem de saída e a quantidade de dados. Veja como realizar consultas de paginação e ordenação usando ent:

  1. Use o método Limit para definir o número máximo de resultados a serem retornados.
  2. Use o método Offset para pular alguns dos resultados anteriores.
  3. Use o método Order para especificar o campo de ordenação e a direção.

Aqui está um exemplo de consulta de paginação e ordenação:

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("Falha ao abrir a conexão com o banco de dados: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // Consultar Pets em ordem decrescente de idade com paginação
    pets, err := client.Pet.
        Query().
        Order(ent.Desc(pet.FieldAge)).
        Limit(10).
        Offset(0).
        All(ctx)
    if err != nil {
        log.Fatalf("Falha ao consultar Pets: %v", err)
    }

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

Este exemplo demonstra como recuperar a primeira página, até 10 registros, de pets ordenados em ordem decrescente por idade. Ao modificar os valores de Limit e Offset, é possível fazer a paginação por todo o conjunto de dados.

5. Operações de Atualização da Entidade

5.1 Atualizando uma Única Entidade

Em muitas aplicações, a atualização de entidades é uma parte essencial das operações diárias. Nesta seção, vamos demonstrar como usar o framework Ent para atualizar uma única entidade no banco de dados.

Primeiramente, assumindo que precisamos atualizar a idade de um usuário, podemos utilizar o método Update gerado pelo Ent.

// Considerando que já temos uma entidade de usuário 'a8m' e um contexto 'ctx'

a8m, err := a8m.Update().         // Criar um construtor de atualização de usuário
    SetAge(30).                   // Definir a idade do usuário para 30 anos
    Save(ctx)                     // Realizar a operação de salvamento e retornar o resultado
if err != nil {
    log.Fatalf("Falha ao atualizar o usuário: %v", err)
}

Também é possível atualizar vários campos simultaneamente:

a8m, err := a8m.Update().
    SetAge(30).                    // Atualizar idade
    SetName("Ariel").              // Atualizar nome
    AddRank(10).                   // Aumentar a classificação em 10
    Save(ctx)
if err != nil {
    log.Fatalf("Falha ao atualizar o usuário: %v", err)
}

A operação de atualização pode ser encadeada, o que é muito flexível e fácil de ler. Chamar o método Save irá realizar a atualização e retornar a entidade atualizada ou uma mensagem de erro.

5.2 Atualizações Condicionais

Ent permite que você faça atualizações baseadas em condições. Aqui está um exemplo onde apenas usuários que atendam a condições específicas serão atualizados.

// Supondo que temos o `id` de um usuário e queremos marcá-lo como concluído para a versão `currentVersion`

err := client.Todo.
    UpdateOneID(id).          // Cria um construtor para atualizar pelo ID do usuário
    SetStatus(todo.StatusDone).
    AddVersion(1).
    Where(
        todo.Version(currentVersion),  // A operação de atualização é executada apenas quando a versão atual corresponder
    ).
    Exec(ctx)
switch {
case ent.IsNotFound(err):
    fmt.Println("Tarefa não encontrada")
case err != nil:
    fmt.Println("Erro na atualização:", err)
}

Ao utilizar atualizações condicionais, o método .Where() deve estar envolvido. Isso permite que você determine se a atualização deve ser realizada com base nos valores atuais no banco de dados, o que é crucial para garantir a consistência e integridade dos dados.

6. Operações de Exclusão de Entidades

6.1 Excluindo uma Única Entidade

Excluir entidades é outra função importante em operações de banco de dados. A estrutura Ent fornece uma API simples para realizar operações de exclusão.

O exemplo a seguir demonstra como excluir uma entidade de usuário específica:

err := client.User.
    DeleteOne(a8m).  // Cria um construtor de exclusão de usuário
    Exec(ctx)        // Executa a operação de exclusão
if err != nil {
    log.Fatalf("Falha ao excluir usuário: %v", err)
}

6.2 Exclusão Condicional

Assim como as operações de atualização, também podemos realizar operações de exclusão com base em condições específicas. Em certos cenários, podemos querer excluir apenas entidades que atendam a condições específicas. O uso do método .Where() pode definir essas condições:

// Suponha que desejamos excluir todos os arquivos com uma data de atualização anterior a uma determinada data

afetados, err := client.File.
    Delete().
    Where(file.UpdatedAtLT(date)).  // Execute a exclusão apenas se o tempo de atualização do arquivo for anterior à data fornecida
    Exec(ctx)
if err != nil {
    log.Fatalf("Falha ao excluir arquivos: %v", err)
}

// Esta operação retorna o número de registros afetados pela operação de exclusão
fmt.Printf("%d arquivos foram excluídos\n", afetados)

O uso de operações de exclusão condicionais garante controle preciso sobre nossas operações de dados, garantindo que apenas entidades que realmente atendam às condições sejam excluídas. Isso aprimora a segurança e confiabilidade das operações de banco de dados.