1. Varlık ve İlişki Temel Kavramları
ent
çerçevesinde, bir varlık veritabanında yönetilen temel veri birimidir ve genellikle veritabanındaki bir tabloya karşılık gelir. Varlıkta bulunan alanlar, tablodaki sütunlara karşılık gelir, varlık (kenarlar) arasındaki ilişkiler ise varlıklar arasındaki ilişkileri ve bağımlılıkları tanımlamak için kullanılır. Varlık ilişkileri, karmaşık veri modelleri oluşturmanın temelini oluşturur ve ebeveyn-çocuk ilişkileri ve sahiplik ilişkileri gibi hiyerarşik ilişkilerin temsil edilmesine olanak tanır.
ent
çerçevesi, varlık şemasındaki bu ilişkileri tanımlamak ve yönetmek için zengin bir dizi API sağlar. Bu ilişkiler aracılığıyla, veri arasındaki karmaşık iş mantığını kolayca ifade edebilir ve işlemler gerçekleştirebiliriz.
2. ent
Çerçevesinde Varlık İlişkilerinin Türleri
2.1 Bir-Bir (O2O) İlişki
Bir-bir ilişkisi, iki varlık arasındaki bir-bir karşılıklı ilişkiyi ifade eder. Örneğin, kullanıcılar ve banka hesapları durumunda, her kullanıcının sadece bir banka hesabı olabilir ve her banka hesabı da yalnızca bir kullanıcıya ait olabilir. ent
çerçevesi, bu tür ilişkileri tanımlamak için edge.To
ve edge.From
yöntemlerini kullanır.
Öncelikle, User
şeması içinde Card
'a işaret eden bir bir-bir ilişkisi tanımlayabiliriz:
// User'ın kenarları.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("card", Card.Type). // Card varlığına işaret eder, ilişki adını "card" olarak tanımlar
Unique(), // Unique yöntemi, bu ilişkinin bir-bir ilişkisi olduğunu sağlar
}
}
Daha sonra, Card
şeması içinde User
'a ters ilişkiyi tanımlarız:
// Card'ın kenarları.
func (Card) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type). // Karttan kullanıcıya geri işaret eder, ilişki adını "owner" olarak tanımlar
Ref("card"). // Ref yöntemi, karşılıklı ters ilişki adını belirtir
Unique(), // Bir kartın bir sahibe karşılık gelmesini sağlamak için benzersiz işaretler
}
}
2.2 Bir-Çok (O2M) İlişki
Bir-çok ilişkisi, bir varlığın birden fazla diğer varlıkla ilişkilendirilebileceğini, ancak bu varlıkların yalnızca tek bir varlığa işaret edebileceğini gösterir. Örneğin, bir kullanıcının birden fazla evcil hayvanı olabilir, ancak her evcil hayvanın yalnızca bir sahibi olabilir.
ent
içinde, bu tür ilişkiyi tanımlamak için hala edge.To
ve edge.From
kullanırız. Aşağıdaki örnek, kullanıcılar ve evcil hayvanlar arasında bir-birçok ilişkiyi tanımlar:
// User'ın kenarları.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("pets", Pet.Type), // User varlığından Pet varlığına bir-çok ilişki
}
}
Pet
varlığında, User
'a geri bir-çok ilişki tanımlarız:
// Pet'in kenarları.
func (Pet) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type). // Pet'ten User'a bir-çok ilişki
Ref("pets"). // Evcil hayvandan sahibe geri ilişki adını belirtir
Unique(), // Bir sahibin birden fazla evcil hayvana sahip olmasını sağlar
}
}
2.3 Çok-Çok (M2M) İlişki
Çok-çok ilişkisi, iki tür varlığın birbirlerine birden fazla örneğe sahip olmalarını sağlar. Örneğin, bir öğrenci birden fazla derse kaydolabilir ve bir ders de birden fazla öğrenciye sahip olabilir. ent
bu tür ilişkileri kurmak için bir API sağlar:
Student
varlığında, Course
ile çok-çok ilişkiyi edge.To
kullanarak tanımlarız:
// Student'ın kenarları.
func (Student) Edges() []ent.Edge {
return []ent.Edge{
edge.To("courses", Course.Type), // Student'tan Course'a çok-çok ilişki tanımlar
}
}
Benzer şekilde, Course
varlığında, çok-çok ilişkinin ters ilişkisini Student
a tanımlarız:
// Course'ın kenarları.
func (Course) Edges() []ent.Edge {
return []ent.Edge{
edge.From("students", Student.Type). // Course'tan Student'a çok-çok ilişki tanımlar
Ref("courses"), // Dersden Öğrenciye ters ilişki adını belirtir
}
}
Bu tür ilişkiler, karmaşık uygulama veri modelleri oluşturmanın temelidir ve bunları ent
içinde nasıl tanımlayıp kullanacağımızı anlamak, veri modellerini ve iş mantığını genişletmek için oldukça önemlidir.
3. Varlık İlişkileri için Temel İşlemler
Bu bölüm, belirlenmiş ilişkilerle ent
kullanarak temel işlemleri gerçekleştirmeyi, dahil olmak üzere varlık oluşturma, sorgulama ve ilişkili varlıkları gezme işlemlerini gösterecektir.
3.1 İlişkili Varlıkların Oluşturulması
Varlıkları oluştururken, varlıklar arasındaki ilişkileri aynı anda ayarlayabilirsiniz. Bir birçok için (O2M) ve birçok için (M2M) ilişkiler için Add{Edge}
yöntemini kullanarak ilişkili varlıkları ekleyebilirsiniz.
Örneğin, bir kullanıcı varlığı ve bir evcil hayvan varlığı belirli bir ilişkiyle, bir kullanıcının birden fazla evcil hayvana sahip olabileceği durumu düşünelim. Aşağıdaki, yeni bir kullanıcı oluşturup onun için evcil hayvanlar eklemek örneğidir:
// Kullanıcı oluşturup evcil hayvanlar ekler
func CreateUserWithPets(ctx context.Context, client *ent.Client) (*ent.User, error) {
// Evcil hayvan örneği oluştur
fido := client.Pet.
Create().
SetName("Fido").
SaveX(ctx)
// Kullanıcı örneği oluştur ve evcil hayvanla ilişkilendir
user := client.User.
Create().
SetName("Alice").
AddPets(fido). // Evcil hayvanı ilişkilendirmek için AddPets yöntemini kullan
SaveX(ctx)
return user, nil
}
Bu örnekte, önce Fido adında bir evcil hayvan örneği oluşturuyoruz, daha sonra Alice adında bir kullanıcı oluşturuyoruz ve AddPets
yöntemini kullanarak evcil hayvan örneğini kullanıcıyla ilişkilendiriyoruz.
3.2 İlişkili Varlıkların Sorgulanması
İlişkili varlıkların sorgulanması, ent
te yaygın bir işlemdir. Örneğin, belirli bir varlıkla ilişkili diğer varlıkları almak için Query{Edge}
yöntemini kullanabilirsiniz.
Kullanıcılar ve evcil hayvanlar örneğimizle devam edecek olursak, bir kullanıcının sahip olduğu tüm evcil hayvanları sorgulamanın bir örneği şöyle olabilir:
// Bir kullanıcının tüm evcil hayvanlarını sorgula
func QueryUserPets(ctx context.Context, client *ent.Client, userID int) ([]*ent.Pet, error) {
pets, err := client.User.
Get(ctx, userID). // Kullanıcı kimliğine göre kullanıcı örneğini al
QueryPets(). // Kullanıcıyla ilişkili evcil hayvan varlıklarını sorgula
All(ctx) // Sorgulanan tüm evcil hayvan varlıklarını döndür
if err != nil {
return nil, err
}
return pets, nil
}
Yukarıdaki kod örneğinde, önce kullanıcı kimliğine göre kullanıcı örneğini alıyoruz, ardından QueryPets
yöntemini çağırarak bu kullanıcıyla ilişkili tüm evcil hayvan varlıklarını alıyoruz.
Not:
ent
'in kod üretim aracı, tanımlı varlık ilişkilerine dayalı olarak ilişki sorguları için API'yi otomatik olarak oluşturur. Üretilen kodları incelemeniz önerilir.
4. Önceden Yükleme (Eager Loading)
4.1 Önceden Yükleme İlkeleri
Önceden yükleme, veritabanı sorgularında ilişkili varlıkların önceden alınması ve yüklenmesi için kullanılan bir tekniktir. Bu yaklaşım, birden çok varlıkla ilgili verileri tek seferde almak için yaygın olarak kullanılır, böylece ardışık işlemlerde birden çok veritabanı sorgusu işlemi yapmaktan kaçınılarak uygulamanın performansını önemli ölçüde arttırır.
Ent çatısında, önceden yükleme öncelikle birçoklu ve birçoklu gibi varlık arasındaki ilişkileri işlemek için kullanılır. Veritabanından bir varlık alındığında, ilişkili varlıklar otomatik olarak yüklenmez. Bunun yerine, ihtiyaç duyulduğunda önceden yükleme ile açıkça yüklenirler. Bu, ayrı ayrı sorgular için N+1 sorgu problemine (her ana varlık için ayrı ayrı sorgular gerçekleştirme) bir çözüm olarak önemlidir.
Ent çatısında, önceden yükleme, sorgu oluşturucuda With
yöntemi kullanılarak gerçekleştirilir. Bu yöntem, her kenar için WithGroups
ve WithPets
gibi ilgili With...
işlevlerini oluşturur. Bu yöntemler, ent çatısı tarafından otomatik olarak oluşturulur ve programcılar belirli ilişkilerin önceden yüklenmesini istemek için bunları kullanabilirler.
Varlıkların önceden yüklenmesi prensibinin çalışma prensibi, birincil varlık sorgulandığında, ent tarafından tüm ilişkili varlıkları almak için ek sorgular yürütülmesidir. Daha sonra, bu varlıklar dönen nesnenin Edges
alanına doldurulur. Bu, en azından önceden yüklenmesi gereken her ilişkili kenar için en az bir kez olmak üzere, birden çok veritabanı sorgusu yürütmesi anlamına gelir. Bu yöntem bazı senaryolarda tek bir karmaşık JOIN
sorgusundan daha az verimli olabilir, ancak daha büyük esneklik sunar ve ent'in gelecekteki sürümlerinde performans optimizasyonları alacağı öngörülmektedir.
4.2 Önceden Yükleme Uygulaması
Şimdi, ent çatısı içinde önceden yükleme işlemlerinin modelleri kullanarak bazı örnek kodlar üzerinden nasıl gerçekleştirildiğini gösterelim.
Tekli İlişki Ön Yükleme
Diyelim ki veritabanından tüm kullanıcıları ve evcil hayvan verilerini ön yüklemek istiyoruz. Bunun için aşağıdaki kodu yazarak bu işlemi gerçekleştirebiliriz:
users, err := client.User.
Query().
WithPets().
All(ctx)
if err != nil {
// Hata yönetimi
return err
}
for _, u := range users {
for _, p := range u.Edges.Pets {
fmt.Printf("Kullanıcı (%v), evcil hayvan sahiplendi (%v)\n", u.ID, p.ID)
}
}
Bu örnekte, kullanıcılarla ilişkili evcil hayvan varlıklarını ön yüklemek için WithPets
yöntemini kullanıyoruz. Ön yüklenen evcil hayvan verileri, ilişkili verilere erişebileceğimiz Edges.Pets
alanına yerleştirilir.
Çoklu İlişki Ön Yükleme
ent bize bir seferde birden fazla ilişki ön yüklemesini yapma imkanı sağlar, hatta önceden filtreleme, sıralama, sınırlama yapılandırması yapmak veya iç içe geçmiş ilişkileri ön yüklemek mümkündür. Aşağıdaki örnek, yöneticilerin evcil hayvanlarını ve ait oldukları takımları ön yüklerken aynı zamanda takımlarla ilişkili kullanıcıları da ön yüklemektedir:
admins, err := client.User.
Query().
Where(user.Admin(true)).
WithPets().
WithGroups(func(q *ent.GroupQuery) {
q.Limit(5) // İlk 5 takımla sınırla
q.Order(ent.Asc(group.FieldName)) // Takım adına göre artan sırayla sırala
q.WithUsers() // Takımdaki kullanıcıları ön yükle
}).
All(ctx)
if err != nil {
// Hataları yönet
return err
}
for _, admin := range admins {
for _, p := range admin.Edges.Pets {
fmt.Printf("Yönetici (%v), evcil hayvan sahiplendi (%v)\n", admin.ID, p.ID)
}
for _, g := range admin.Edges.Groups {
fmt.Printf("Yönetici (%v), takım (%v)'e ait\n", admin.ID, g.ID)
for _, u := range g.Edges.Users {
fmt.Printf("Takım (%v), üye (%v)'e sahip\n", g.ID, u.ID)
}
}
}
Bu örnek üzerinden ent'in ne kadar güçlü ve esnek olduğunu görebilirsiniz. Sadece birkaç basit yöntem çağrısı ile zengin ilişkili verileri ön yükleyebilir ve yapılandırabilir. Bu, veri odaklı uygulamalar geliştirmek için büyük bir kolaylık sağlar.