1. Introduction to ent
Entity Operations
This tutorial will comprehensively guide you through mastering entity operations in the ent
framework, covering the complete process of creating, querying, updating, and deleting entities. It is suitable for beginners to gradually delve into the core functionality of ent
.
3. Entity Creation Operation
3.1 Creating a Single Entity
Creating an entity is the fundamental operation for data persistence. Below are the steps to create a single entity object using the ent
framework and save it to the database:
- First, define the structure and fields of an entity, i.e., define the model of the entity in the
schema
file. - Run the
ent generate
command to generate the corresponding entity operation code. - Use the generated
Create
method to build a new entity, and set the entity's field values through chained calls. - Finally, call the
Save
method to save the entity to the database.
The following is an example that demonstrates how to create and save a user entity:
package main
import (
"context"
"log"
"entdemo/ent"
)
func main() {
// Create a Client instance for database interaction
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("Failed to open the database connection: %v", err)
}
defer client.Close()
// Create a context
ctx := context.Background()
// Create a user entity using the Client
a8m, err := client.User.
Create().
SetName("a8m").
Save(ctx)
if err != nil {
log.Fatalf("Failed to create the user entity: %v", err)
}
// Entity successfully saved to the database
log.Printf("User entity saved: %v", a8m)
}
In this example, a database client client
is created first. Then, the User.Create
method is used to set the attributes of the new user, and finally, the Save
method is called to save the user to the database.
3.2 Batch Entity Creation
In certain scenarios, there may be a need to create multiple entities, such as during database initialization or bulk data import operations. The ent
framework provides the capability to create entities in batches, which offers better performance compared to creating and saving entities individually.
The steps for batch entity creation are as follows:
- Use the
CreateBulk
method instead of theCreate
method, which allows the creation of multiple entities in a single operation. - Call
Create
for each entity to be created. - Once all entities have been created, use the
Save
method to save the entities to the database in bulk.
Below is an example of batch entity creation:
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("Failed to open the database connection: %v", err)
}
defer client.Close()
ctx := context.Background()
// Batch create Pet entities
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("Failed to batch create Pet entities: %v", err)
}
log.Printf("Created %d Pet entities in batch\n", len(pets))
}
In this example, a client
is created first, and then multiple Pet
entities are constructed using the CreateBulk
method, setting their names and owner fields. All entities are saved to the database at once when the Save
method is called, providing better performance for handling large amounts of data.
4. Entity Query Operations
4.1 Basic Querying
Database querying is the fundamental way to retrieve information. In ent
, the Query
method is used to start a query. Below are the steps and an example of basic entity querying:
- Make sure you have a usable
Client
instance. - Use
Client.Query()
or entity helper methods likePet.Query()
to create a query. - Add filtering conditions as needed, such as
Where
. - Execute the query and retrieve the results by calling the
All
method.
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)
}
}
This example demonstrates how to find all users with the name "a8m".
4.2 Pagination and Sorting
Pagination and sorting are commonly used advanced features when querying, used to control the output order and quantity of data. Here's how to achieve pagination and sorting queries using ent
:
- Use the
Limit
method to set the maximum number of results to be returned. - Use the
Offset
method to skip some of the previous results. - Use the
Order
method to specify the sorting field and direction.
Here's an example of a pagination and sorting query:
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)
}
}
This example demonstrates how to retrieve the first page, up to 10 records, of pets sorted in descending order by age. By modifying the values of Limit
and Offset
, you can achieve paging through the entire dataset.
5. Entity Update Operations
5.1 Updating a Single Entity
In many applications, updating entities is an essential part of daily operations. In this section, we will demonstrate how to use the Ent framework to update a single entity in the database.
Firstly, assuming we need to update a user's age, we can utilize the Update
method generated by Ent.
// Assuming we already have a user entity 'a8m' and a context 'ctx'
a8m, err := a8m.Update(). // Create a user update builder
SetAge(30). // Set the user's age to 30 years old
Save(ctx) // Perform the save operation and return the result
if err != nil {
log.Fatalf("Failed to update the user: %v", err)
}
You can also update multiple fields simultaneously:
a8m, err := a8m.Update().
SetAge(30). // Update age
SetName("Ariel"). // Update name
AddRank(10). // Increase the rank by 10
Save(ctx)
if err != nil {
log.Fatalf("Failed to update the user: %v", err)
}
The update operation can be chained, which is very flexible and easy to read. Calling the Save
method will perform the update and return the updated entity or an error message.
5.2 Conditional Updates
Ent allows you to perform updates based on conditions. Here's an example where only users that meet specific conditions will be updated.
// Assuming we have a user's `id` and we want to mark that user as done for version `currentVersion`
err := client.Todo.
UpdateOneID(id). // Create a builder to update by user ID
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
todo.Version(currentVersion), // The update operation is only executed when the current version matches
).
Exec(ctx)
switch {
case ent.IsNotFound(err):
fmt.Println("Todo not found")
case err != nil:
fmt.Println("Update error:", err)
}
When using conditional updates, the .Where()
method must be involved. This allows you to determine whether the update should be performed based on the current values in the database, which is crucial for ensuring data consistency and integrity.
6. Entity Deletion Operations
6.1 Deleting a Single Entity
Deleting entities is another important function in database operations. Ent framework provides a simple API for performing deletion operations.
The following example demonstrates how to delete a given user entity:
err := client.User.
DeleteOne(a8m). // Create a user deletion builder
Exec(ctx) // Execute the deletion operation
if err != nil {
log.Fatalf("Failed to delete user: %v", err)
}
6.2 Conditional Deletion
Similar to update operations, we can also perform deletion operations based on specific conditions. In certain scenarios, we may only want to delete entities that meet specific conditions. Using the .Where()
method can define these conditions:
// Suppose we want to delete all files with an update time earlier than a certain date
affected, err := client.File.
Delete().
Where(file.UpdatedAtLT(date)). // Only execute deletion if the file's update time is earlier than the given date
Exec(ctx)
if err != nil {
log.Fatalf("Failed to delete files: %v", err)
}
// This operation returns the number of records affected by the deletion operation
fmt.Printf("%d files have been deleted\n", affected)
Using conditional deletion operations ensures precise control over our data operations, ensuring that only entities that truly meet the conditions are deleted. This enhances the security and reliability of database operations.