1. Giới thiệu về Hoạt động của Thực thể trong ent
Hướng dẫn này sẽ giúp bạn nắm vững về hoạt động thực thể trong framework ent
, bao gồm toàn bộ quá trình tạo, truy vấn, cập nhật và xóa các thực thể. Được thiết kế phù hợp cho người mới bắt đầu dần dần khám phá chức năng cốt lõi của ent
.
3. Hoạt động Tạo Thực thể
3.1 Tạo Một Thực thể Đơn
Việc tạo một thực thể là hoạt động cơ bản để lưu trữ dữ liệu. Dưới đây là các bước để tạo một đối tượng thực thể đơn sử dụng framework ent
và lưu nó vào cơ sở dữ liệu:
- Đầu tiên, định nghĩa cấu trúc và trường của một thực thể, tức là định nghĩa mô hình của thực thể trong tệp
schema
. - Chạy lệnh
ent generate
để tạo mã hoạt động thực thể tương ứng. - Sử dụng phương thức
Create
được tạo ra để xây dựng một thực thể mới, và thiết lập giá trị trường của thực thể thông qua các cuộc gọi nối tiếp. - Cuối cùng, gọi phương thức
Save
để lưu thực thể vào cơ sở dữ liệu.
Dưới đây là ví dụ minh họa cách tạo và lưu một thực thể người dùng:
package main
import (
"context"
"log"
"entdemo/ent"
)
func main() {
// Tạo một phiên bản Client để tương tác với cơ sở dữ liệu
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("Không thể mở kết nối cơ sở dữ liệu: %v", err)
}
defer client.Close()
// Tạo một ngữ cảnh
ctx := context.Background()
// Tạo một thực thể người dùng bằng cách sử dụng Client
a8m, err := client.User.
Create().
SetName("a8m").
Save(ctx)
if err != nil {
log.Fatalf("Không thể tạo thực thể người dùng: %v", err)
}
// Thực thể đã được lưu vào cơ sở dữ liệu thành công
log.Printf("Thực thể người dùng đã được lưu: %v", a8m)
}
Trong ví dụ này, một phiên bản của cơ sở dữ liệu client
được tạo trước tiên. Sau đó, phương thức User.Create
được sử dụng để thiêt lập các thuộc tính của người dùng mới, và cuối cùng, phương thức Save
được gọi để lưu người dùng vào cơ sở dữ liệu.
3.2 Tạo Thực thể theo Lô
Trong một số tình huống, có thể có nhu cầu tạo nhiều thực thể, chẳng hạn như trong quá trình khởi tạo cơ sở dữ liệu hoặc thao tác nhập dữ liệu hàng loạt. Framework ent
cung cấp khả năng tạo thực thể theo lô, cung cấp hiệu suất tốt hơn so với việc tạo và lưu thực thể một cách riêng lẻ.
Các bước để tạo thực thể theo lô như sau:
- Sử dụng phương thức
CreateBulk
thay vì phương thứcCreate
, cho phép tạo nhiều thực thể trong một hoạt động duy nhất. - Gọi
Create
cho mỗi thực thể cần tạo. - Sau khi tất cả thực thể đã được tạo, sử dụng phương thức
Save
để lưu các thực thể vào cơ sở dữ liệu theo lô.
Dưới đây là ví dụ về việc tạo thực thể theo lô:
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("Không thể mở kết nối cơ sở dữ liệu: %v", err)
}
defer client.Close()
ctx := context.Background()
// Tạo thực thể Pet theo lô
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("Không thể tạo thực thể Pet theo lô: %v", err)
}
log.Printf("Đã tạo %d thực thể Pet trong lô\n", len(pets))
}
Trong ví dụ này, một client
được tạo trước tiên, sau đó, nhiều thực thể Pet
được xây dựng bằng cách sử dụng phương thức CreateBulk
, thiết lập tên và trường chủ sở hữu của chúng. Tất cả các thực thể được lưu vào cơ sở dữ liệu cùng một lúc khi phương thức Save
được gọi, cung cấp hiệu suất tốt cho việc xử lý lượng lớn dữ liệu.
4. Hoạt động Truy vấn Thực thể
4.1 Truy vấn cơ bản
Truy vấn cơ sở dữ liệu là cách cơ bản để truy xuất thông tin. Trong ent
, phương thức Query
được sử dụng để bắt đầu một truy vấn. Dưới đây là các bước và một ví dụ về việc truy vấn thực thể cơ bản:
- Hãy đảm bảo rằng bạn có một trường hợp
Client
có thể sử dụng. - Sử dụng
Client.Query()
hoặc các phương thức trợ giúp của thực thể nhưPet.Query()
để tạo một truy vấn. - Thêm các điều kiện lọc khi cần thiết, như
Where
. - Thực hiện truy vấn và nhận kết quả bằng cách gọi phương thức
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("Không thể mở kết nối cơ sở dữ liệu: %v", err)
}
defer client.Close()
ctx := context.Background()
// Truy vấn tất cả người dùng có tên "a8m"
users, err := client.User.
Query().
Where(user.NameEQ("a8m")).
All(ctx)
if err != nil {
log.Fatalf("Không thể truy vấn người dùng: %v", err)
}
for _, u := range users {
log.Printf("Tìm thấy người dùng: %#v\n", u)
}
}
Ví dụ này minh họa cách tìm tất cả người dùng có tên "a8m".
4.2 Phân trang và Sắp xếp
Phân trang và sắp xếp là các tính năng nâng cao thường được sử dụng khi truy vấn, được sử dụng để kiểm soát thứ tự và số lượng dữ liệu đầu ra. Dưới đây là cách thực hiện truy vấn phân trang và sắp xếp bằng cách sử dụng ent
:
- Sử dụng phương thức
Limit
để đặt số lượng kết quả tối đa cần trả về. - Sử dụng phương thức
Offset
để bỏ qua một số kết quả trước đó. - Sử dụng phương thức
Order
để chỉ định trường sắp xếp và hướng.
Dưới đây là một ví dụ về truy vấn phân trang và sắp xếp:
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("Không thể mở kết nối cơ sở dữ liệu: %v", err)
}
defer client.Close()
ctx := context.Background()
// Truy vấn Pets theo thứ tự giảm dần của tuổi với phân trang
pets, err := client.Pet.
Query().
Order(ent.Desc(pet.FieldAge)).
Limit(10).
Offset(0).
All(ctx)
if err != nil {
log.Fatalf("Không thể truy vấn Pets: %v", err)
}
for _, p := range pets {
log.Printf("Tìm thấy Pet: %#v\n", p)
}
}
Ví dụ này minh họa cách lấy trang đầu tiên, tối đa 10 bản ghi của pets được sắp xếp theo tuổi giảm dần. Bằng cách thay đổi giá trị của Limit
và Offset
, bạn có thể thực hiện phân trang qua toàn bộ tập dữ liệu.
5. Thao tác Cập nhật Thực thể
5.1 Cập nhật Một Thực thể Đơn
Trong nhiều ứng dụng, việc cập nhật các thực thể là một phần thiết yếu của các hoạt động hàng ngày. Ở phần này, chúng ta sẽ minh họa cách sử dụng khung công việc Ent để cập nhật một thực thể đơn trong cơ sở dữ liệu.
Đầu tiên, giả sử chúng ta cần cập nhật tuổi của một người dùng, chúng ta có thể sử dụng phương thức Update
được tạo ra bởi Ent.
// Giả sử chúng ta đã có một thực thể người dùng 'a8m' và một context 'ctx'
a8m, err := a8m.Update(). // Tạo một bộ cập nhật người dùng
SetAge(30). // Đặt tuổi của người dùng thành 30 tuổi
Save(ctx) // Thực hiện hoạt động lưu và trả về kết quả
if err != nil {
log.Fatalf("Không thể cập nhật người dùng: %v", err)
}
Bạn cũng có thể cập nhật nhiều trường đồng thời:
a8m, err := a8m.Update().
SetAge(30). // Cập nhật tuổi
SetName("Ariel"). // Cập nhật tên
AddRank(10). // Tăng xếp hạng lên 10
Save(ctx)
if err != nil {
log.Fatalf("Không thể cập nhật người dùng: %v", err)
}
Các hoạt động cập nhật có thể được nối tiếp, rất linh hoạt và dễ đọc. Gọi phương thức Save
sẽ thực hiện cập nhật và trả về thực thể đã cập nhật hoặc một thông báo lỗi.
5.2 Cập Nhật Có Điều Kiện
Ent cho phép bạn thực hiện cập nhật dữ liệu dựa trên điều kiện. Đây là một ví dụ trong đó chỉ những người dùng đáp ứng điều kiện cụ thể mới được cập nhật.
// Giả sử chúng ta có `id` của một người dùng và muốn đánh dấu người dùng đó là đã hoàn thành cho phiên bản `currentVersion`
err := client.Todo.
UpdateOneID(id). // Tạo một bộ xử lý để cập nhật theo ID người dùng
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
todo.Version(currentVersion), // Thao tác cập nhật chỉ được thực hiện khi phiên bản hiện tại khớp
).
Exec(ctx)
switch {
case ent.IsNotFound(err):
fmt.Println("Không tìm thấy công việc")
case err != nil:
fmt.Println("Lỗi cập nhật:", err)
}
Khi sử dụng cập nhật có điều kiện, phương thức .Where()
phải được sử dụng. Điều này cho phép bạn xác định liệu cập nhật có nên được thực hiện dựa trên các giá trị hiện tại trong cơ sở dữ liệu, điều quan trọng để đảm bảo tính nhất quán và toàn vẹn dữ liệu.
6. Thực Hiện Xóa Thực Thể
6.1 Xóa Một Thực Thể Duy Nhất
Xóa các thực thể là một chức năng quan trọng khác trong các hoạt động cơ sở dữ liệu. Framework Ent cung cấp một API đơn giản để thực hiện các hoạt động xóa.
Ví dụ dưới đây minh họa cách xóa một thực thể người dùng cụ thể:
err := client.User.
DeleteOne(a8m). // Tạo một bộ xử lý xóa người dùng
Exec(ctx) // Thực hiện thao tác xóa
if err != nil {
log.Fatalf("Xóa người dùng thất bại: %v", err)
}
6.2 Xóa Có Điều Kiện
Tương tự như các thao tác cập nhật, chúng ta cũng có thể thực hiện các hoạt động xóa dựa trên điều kiện cụ thể. Trong một số tình huống, chúng ta có thể chỉ muốn xóa các thực thể đáp ứng điều kiện cụ thể. Sử dụng phương thức .Where()
có thể định nghĩa các điều kiện này:
// Giả sử chúng ta muốn xóa tất cả các tệp có thời gian cập nhật trước một ngày nhất định
affected, err := client.File.
Delete().
Where(file.UpdatedAtLT(date)). // Chỉ thực hiện xóa nếu thời gian cập nhật của tệp sớm hơn ngày đã cho
Exec(ctx)
if err != nil {
log.Fatalf("Xóa tệp thất bại: %v", err)
}
// Thao tác này trả về số lượng bản ghi bị ảnh hưởng bởi thao tác xóa
fmt.Printf("%d tệp đã được xóa\n", affected)
Sử dụng các hoạt động xóa có điều kiện đảm bảo việc kiểm soát chính xác các hoạt động dữ liệu của chúng ta, đảm bảo rằng chỉ có các thực thể thực sự đáp ứng điều kiện mới bị xóa, từ đó tăng cường tính bảo mật và đáng tin cậy của các hoạt động cơ sở dữ liệu.