Gorm Veritabanı ORM Kütüphanesi
Resmi Dokümantasyon: GORM - The fantastic ORM library for Golang, aims to be developer friendly.
Depo: go-gorm/gorm: The fantastic ORM library for Golang, aims to be developer friendly (github.com)
Go topluluğunda veritabanı etkileşimi için iki kamp vardır. Bir kamp sqlx gibi daha basit kütüphaneleri tercih eder. Bunlar güçlü değildir ancak SQL üzerinde tam kontrole sahip olmanızı sağlar ve performansı sonuna kadar optimize etmenize izin verir. Diğer kamp geliştirme verimliliği için ORM'i tercih eder. Bu geliştirme sırasında birçok gereksiz sorundan kurtarabilir. ORM söz konusu olduğunda Go dil topluluğunda gorm kesinlikle kaçınılmazdır. Çok köklü bir ORM'dir. xorm ent gibi nispeten daha genç olanlara benzer. Bu makale gorm hakkındadır. Bu makale yalnızca temel tanıtım içeriğini açıklar. Başlangıç noktası olarak. Daha fazla ayrıntıyı öğrenmek için resmi dokümantasyonu okuyabilirsiniz. Çincede dokümantasyonu oldukça eksiksizdir ve yazar da gorm dokümantasyonunun çevirmenlerinden biridir.
Özellikler
- Tam özellikli ORM
- İlişkiler (Has One, Has Many, Belongs To, Many To Many, Polymorphism, Single-table inheritance)
- Hooks (Before/After Create/Save/Update/Delete/Find)
- Eager Loading ile Preloading (Preload, Joins)
- İşlemler, İç İçe İşlemler, Save Points, Kaydedilen Noktalara Rollback
- Context, Prepared Statement Modu, DryRun Modu
- Toplu Ekleme, FindInBatches, Map ile Find/Create, SQL İfadeleri ile CRUD, Context Valuer
- SQL Builder, Upsert, Lock, Optimizer/Index/Comment Hint, Named Parameters, SubQuery
- Composite Primary Key, Index, Constraint
- Auto Migration
- Özel Logger
- Esnek Genişletilebilir Plugin API: Database Resolver (Çoklu Veritabanları, Read/Write Splitting), Prometheus...
- Her özellik titizlikle test edilmiştir
- Geliştirici Dostu
Gorm'un bazı eksiklikleri vardır. Örneğin neredeyse tüm metod parametreleri boş interface türlerindendir. Dokümantasyona bakmadan ne geçeceğinizi muhtemelen bilemezsiniz. Bazen struct bazen string bazen map bazen slice geçebilirsiniz. Semantik biraz belirsizdir ve birçok durumda hala SQL'i elle yazmanız gerekir.
Alternatif olarak deneyebileceğiniz iki ORM vardır. İlki kısa bir süre önce açık kaynak yapılan aorm'dur. Artık elle tablo alan adları yazmanızı gerektirmez. Çoğunlukla zincir işlemlerini kullanır ve yansıma tabanlıdır. Yıldız sayısı büyük olmadığından bekleyip görebilirsiniz. İkincisi facebook tarafından açık kaynak yapılan ent'tir. Ayrıca zincir işlemlerini destekler ve çoğu durumda SQL'i elle yazmanız gerekmez. Tasarım felsefesi graflara dayanır (veri yapılarından) ve implementasyonu yansıma yerine kod üretimine dayanır (ki buna daha çok katılıyorum). Ancak dokümantasyon tamamen İngilizcedir ve belirli bir öğrenme eşiği vardır.
Kurulum
gorm kütüphanesini kurun:
$ go get -u gorm.io/gormBağlantı
Gorm şu anda aşağıdaki veritabanlarını destekler:
- MySQL:
"gorm.io/driver/mysql" - PostgreSQL:
"gorm.io/driver/postgres" - SQLite:
"gorm.io/driver/sqlite" - SQL Server:
"gorm.io/driver/sqlserver" - TIDB:
"gorm.io/driver/mysql", TIDB mysql protokolü ile uyumludur - ClickHouse:
"gorm.io/driver/clickhouse"
Ayrıca oracle sürücüsü CengSin/oracle gibi üçüncü taraf geliştiriciler tarafından sağlanan başka veritabanı sürücüleri de vardır. Bu makale MySQL'i gösterim için kullanacaktır. Hangi veritabanını kullanırsanız kullanın ilgili sürücüyü kurmanız gerekir. Burada MySQL'in gorm sürücüsünü kuruyoruz:
$ go get -u gorm.io/driver/mysqlArdından dsn (data source name) kullanarak veritabanına bağlanın. Sürücü kütüphanesi dsn'yi otomatik olarak ilgili yapılandırmaya ayrıştırır:
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log/slog"
)
func main() {
dsn := "root:123456@tcp(192.168.48.138:3306)/hello?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
slog.Error("db connect error", err)
}
slog.Info("db connect success")
}Veya manuel olarak yapılandırma geçin:
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log/slog"
)
func main() {
db, err := gorm.Open(mysql.New(mysql.Config{}))
if err != nil {
slog.Error("db connect error", err)
}
slog.Info("db connect success")
}Her iki yöntem de eşdeğerdir. Kullanım tercihine bağlıdır.
Bağlantı Yapılandırması
gorm.Config yapılandırma struct'ını geçirerek gorm'un bazı davranışlarını kontrol edebiliriz:
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})Aşağıda bazı basit açıklamalar bulunmaktadır. İhtiyacınıza göre yapılandırabilirsiniz:
type Config struct {
// Varsayılan işlemi atla. gorm veri tutarlılığını korumak için tek create ve update için işlem başlatacak
SkipDefaultTransaction bool
// Özel adlandırma stratejisi
NamingStrategy schema.Namer
// Tam ilişkileri kaydet
FullSaveAssociations bool
// Özel logger
Logger logger.Interface
// Özel nowfunc. CreatedAt ve UpdatedAt alanlarını enjekte etmek için kullanılır
NowFunc func() time.Time
// Yalnızca SQL oluştur. Yürütme
DryRun bool
// Hazırlanmış ifadeler kullan
PrepareStmt bool
// Bağlantı kurduktan sonra veritabanını ping et
DisableAutomaticPing bool
// Veritabanı taşırken foreign key'leri yoksay
DisableForeignKeyConstraintWhenMigrating bool
// Veritabanı taşırken ilişki referanslarını yoksay
IgnoreRelationshipsWhenMigrating bool
// İç içe işlemleri devre dışı bırak
DisableNestedTransaction bool
// Global update'ye izin ver. where clause olmadan update
AllowGlobalUpdate bool
// Tablonun tüm alanlarını sorgula
QueryFields bool
// Toplu oluşturma boyutu
CreateBatchSize int
// Hata çevirisini etkinleştir
TranslateError bool
// ClauseBuilders clause builder
ClauseBuilders map[string]clause.ClauseBuilder
// ConnPool db conn pool
ConnPool ConnPool
// Dialector veritabanı dialector
Dialector
// Plugins kayıtlı plugin'ler
Plugins map[string]Plugin
callbacks *callbacks
cacheStore *sync.Map
}Model
gorm'da bir model bir veritabanı tablosuna karşılık gelir. Genellikle bir struct ile temsil edilir. Aşağıdaki struct gibi:
type Person struct {
Id uint
Name string
Address string
Mom string
Dad string
}Struct'ın iç alanları temel veri türlerinden ve sql.Scanner ve sql.Valuer arayüzlerini uygulayan türlerden oluşabilir. Varsayılan olarak Person struct'ı tarafından eşlenen tablo adı persons'tır. Bu snake_case çoğul stilindedir. Alt çizgilerle ayrılır. Sütun adları da snake_case stilindedir. Örneğin Id sütun adı id'ye karşılık gelir. Gorm bunu yapılandırmak için bazı yollar da sağlar.
Sütun Adlarını Belirtme
Struct etiketleri aracılığıyla struct alanları için sütun adlarını belirtebiliriz. Bu şekilde varlık eşleme sırasında gorm belirtilen sütun adlarını kullanacaktır:
type Person struct {
Id uint `gorm:"column:ID;"`
Name string `gorm:"column:Name;"`
Address string
Mom string
Dad string
}Tablo Adlarını Belirtme
Table arayüzünü uygulayarak tablo adını belirtebilirsiniz. Yalnızca bir metodu vardır ve tablo adını döndürür:
type Tabler interface {
TableName() string
}Uygulanan metodda person string'ini döndürür. Veritabanı taşıma sırasında gorm person adında bir tablo oluşturacaktır:
type Person struct {
Id uint `gorm:"column:ID;"`
Name string `gorm:"column:Name;"`
Address string
Mom string
Dad string
}
func (p Person) TableName() string {
return "person"
}Adlandırma stratejileri için bağlantı oluştururken kendi strateji implementasyonunuzu geçirebilir ve özel efektler elde edebilirsiniz.
Zaman İzleme
type Person struct {
Id uint
Name string
Address string
Mom string
Dad string
CreatedAt sql.NullTime
UpdatedAt sql.NullTime
}
func (p Person) TableName() string {
return "person"
}CreatedAt veya UpdatedAt alanlarını içerdiğinde kayıt oluşturma veya güncelleme sırasında sıfır değerleri varsa gorm otomatik olarak zamanı ayarlamak için time.Now() kullanacaktır:
db.Create(&Person{
Name: "jack",
Address: "usa",
Mom: "lili",
Dad: "tom",
})
// INSERT INTO `person` (`name`,`address`,`mom`,`dad`,`created_at`,`updated_at`) VALUES ('jack','usa','lili','tom','2023-10-25 14:43:57.16','2023-10-25 14:43:57.16')Gorm ayrıca zaman damgası izlemeyi de destekler:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string `gorm:"primaryKey;"`
Address string
Mom string
Dad string
// nanosaniye
CreatedAt uint64 `gorm:"autoCreateTime:nano;"`
// milisaniye
UpdatedAt uint64 `gorm:"autoUpdateTime;milli;"`
}Sonra Create yürütme sırasında aşağıdaki SQL'e eşdeğerdir:
INSERT INTO `person` (`name`,`address`,`mom`,`dad`,`created_at`,`updated_at`) VALUES ('jack','usa','lili','tom',1698216540519000000,1698216540)Gerçek durumlarda zaman izlemeye ihtiyacınız varsa arka uçta zaman damgalarını saklamanızı öneririm. Bu çapraz zaman dilimi senaryolarında işlemeyi daha basit hale getirir.
Model
Gorm bir önayarlı Model struct'ı sağlar. Bir ID birincil anahtarı iki zaman izleme alanını ve bir soft delete kayıt alanını içerir:
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}Kullanmak için varlık modelinize embed edin:
type Order struct {
gorm.Model
Name string
}Bu şekilde gorm.Model'in tüm özelliklerine otomatik olarak sahip olacaktır.
Birincil Anahtar
Varsayılan olarak Id adlı alan birincil anahtardır. Birincil anahtar alanını belirtmek için struct etiketlerini kullanabilirsiniz:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string
Address string
Mom string
Dad string
CreatedAt sql.NullTime
UpdatedAt sql.NullTime
}Birden fazla alan bileşik birincil anahtar oluşturur:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string `gorm:"primaryKey;"`
Address string
Mom string
Dad string
CreatedAt sql.NullTime
UpdatedAt sql.NullTime
}İndeks
index struct etiketi aracılığıyla sütun indekslerini belirtebilirsiniz:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string `gorm:"primaryKey;"`
Address string `gorm:"index:idx_addr,unique,sort:desc;"`
Mom string
Dad string
// nanosaniye
CreatedAt uint64 `gorm:"autoCreateTime:nano;"`
// milisaniye
UpdatedAt uint64 `gorm:"autoUpdateTime;milli;"`
}Yukarıdaki struct'ta Address alanı için benzersiz bir indeks oluşturulur. Aynı indeks adını kullanan iki alan bileşik bir indeks oluşturur:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string `gorm:"primaryKey;"`
Address string `gorm:"index:idx_addr,unique;"`
School string `gorm:"index:idx_addr,unique;"`
Mom string
Dad string
// nanosaniye
CreatedAt uint64 `gorm:"autoCreateTime:nano;"`
// milisaniye
UpdatedAt uint64 `gorm:"autoUpdateTime;milli;"`
}Foreign Key
Bir struct'ta foreign key ilişkileri struct'ları embed ederek tanımlanır. Örneğin:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string
MomId uint
Mom Mom `gorm:"foreignKey:MomId;"`
DadId uint
Dad Dad `gorm:"foreignKey:DadId;"`
}
type Mom struct {
Id uint
Name string
Persons []Person `gorm:"foreignKey:MomId;"`
}
type Dad struct {
Id uint
Name string
Persons []Person `gorm:"foreignKey:DadId;"`
}Örnekte Person struct'ı sırasıyla Dad ve Mom struct'larının birincil anahtarlarına referans veren iki foreign key'e sahiptir. Varsayılan olarak birincil anahtara referans verir. Person'ın Dad ve Mom ile bire-bir ilişkisi vardır - bir kişinin yalnızca bir babası ve bir annesi olabilir. Dad ve Mom'un Person ile bire-çok ilişkisi vardır çünkü babaların ve annelerin birden fazla çocuğu olabilir.
Mom Mom `gorm:"foreignKey:MomId;"`Struct'ları embed etmenin amacı foreign key'leri ve referansları belirtmeyi kolaylaştırmaktır. Varsayılan olarak foreign key alan adı formatı ReferencedTypeName+Id'dir. Örneğin MomId. Varsayılan olarak birincil anahtara referans verir. Referans vermek için bir alanı struct etiketleri aracılığıyla belirtebilirsiniz:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string
MomId uint
Mom Mom `gorm:"foreignKey:MomId;references:Sid;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
DadId uint
Dad Dad `gorm:"foreignKey:DadId;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type Mom struct {
Id uint
Sid uint `gorm:"uniqueIndex;"`
Name string
Persons []Person `gorm:"foreignKey:MomId;"`
}Bunlardan constraint:OnUpdate:CASCADE,OnDelete:SET NULL; foreign key kısıtlamasını tanımlar.
Hooks
Bir varlık modeli hook'ları özelleştirebilir:
- Create
- Update
- Delete
- Query
İlgili arayüzler aşağıdaki gibidir:
// Create öncesi tetiklenir
type BeforeCreateInterface interface {
BeforeCreate(*gorm.DB) error
}
// Create sonrası tetiklenir
type AfterCreateInterface interface {
AfterCreate(*gorm.DB) error
}
// Update öncesi tetiklenir
type BeforeUpdateInterface interface {
BeforeUpdate(*gorm.DB) error
}
// Update sonrası tetiklenir
type AfterUpdateInterface interface {
AfterUpdate(*gorm.DB) error
}
// Save öncesi tetiklenir
type BeforeSaveInterface interface {
BeforeSave(*gorm.DB) error
}
// Save sonrası tetiklenir
type AfterSaveInterface interface {
AfterSave(*gorm.DB) error
}
// Delete öncesi tetiklenir
type BeforeDeleteInterface interface {
BeforeDelete(*gorm.DB) error
}
// Delete sonrası tetiklenir
type AfterDeleteInterface interface {
AfterDelete(*gorm.DB) error
}
// Query sonrası tetiklenir
type AfterFindInterface interface {
AfterFind(*gorm.DB) error
}Struct'lar bu arayüzleri uygulayarak davranışları özelleştirebilir.
Etiketler
Aşağıda gorm tarafından desteklenen bazı etiketler bulunmaktadır:
| Etiket Adı | Açıklama |
|---|---|
column | Veritabanı sütun adını belirtir |
type | Sütun veri türü. Önerilen uyumlu genel türleri kullanmaktır. Örneğin tüm veritabanları bool, int, uint, float, string, time, bytes destekler ve diğer etiketlerle kullanılabilir. Örneğin not null, size, autoIncrement... varbinary(8) gibi belirtilen veritabanı veri türlerini belirtmek de desteklenir. Belirtilen veritabanı veri türlerini kullanırken tam veritabanı veri türü olmalıdır. Örneğin MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT |
serializer | Veriyi veritabanına serileştirmek veya serileştirmeyi açmak için serializer belirtir. Örneğin serializer:json/gob/unixtime |
size | Sütun veri türü boyutunu veya uzunluğunu tanımlar. Örneğin size: 256 |
primaryKey | Sütunu birincil anahtar olarak tanımlar |
unique | Sütunu benzersiz anahtar olarak tanımlar |
default | Sütun varsayılan değerini tanımlar |
precision | Sütun hassasiyetini belirtir |
scale | Sütun ölçeğini belirtir |
not null | Sütunu NOT NULL olarak belirtir |
autoIncrement | Sütunu otomatik artan olarak belirtir |
autoIncrementIncrement | Otomatik artım adımı. Ardışık kayıtlar arasındaki aralığı kontrol eder |
embedded | Gömülü alan |
embeddedPrefix | Gömülü alan için sütun adı öneki |
autoCreateTime | Oluşturma sırasında mevcut zamanı izler. int alanları için zaman damgası saniyelerini izler. Nanosaniye/milisaniye zaman damgasını izlemek için nano/milli kullanabilirsiniz. Örneğin autoCreateTime:nano |
autoUpdateTime | Oluşturma/güncelleme sırasında mevcut zamanı izler. int alanları için zaman damgası saniyelerini izler. Nanosaniye/milisaniye zaman damgasını izlemek için nano/milli kullanabilirsiniz. Örneğin autoUpdateTime:milli |
index | Parametrelere dayalı indeks oluşturur. Aynı adı kullanan birden fazla alan bileşik indeks oluşturur. Ayrıntılar için Indexes bölümüne bakın |
uniqueIndex | index ile aynıdır ancak benzersiz indeks oluşturur |
check | Check kısıtlaması oluşturur. Örneğin check:age > 13. Ayrıntılar için Constraints bölümüne bakın |
<- | Alan yazma iznini ayarlar. <-:create yalnızca oluşturma, <-:update yalnızca güncelleme, <-:false yazma izni yok, <- oluşturma ve güncelleme izni |
-> | Alan okuma iznini ayarlar. ->:false okuma izni yok |
- | Bu alanı yoksayar. - okuma/yazma yok, -:migration taşıma izni yok, -:all okuma/yazma/taşıma izni yok |
comment | Taşıma sırasında alan için yorum ekler |
foreignKey | Mevcut modelin sütununu join tablosu için foreign key olarak belirtir |
references | Referans alınan tablonun sütun adını belirtir. Join tablosu foreign key'i olarak eşlenir |
polymorphic | Polimorfik türü belirtir. Örneğin model adı |
polymorphicValue | Polimorfik değeri belirtir. Varsayılan tablo adı |
many2many | Join tablosu adını belirtir |
joinForeignKey | Join tablosundaki foreign key sütun adını belirtir. Mevcut tabloya eşlenir |
joinReferences | Join tablosundaki foreign key sütun adını belirtir. Referans alınan tabloya eşlenir |
constraint | İlişki kısıtlamaları. Örneğin OnUpdate, OnDelete |
Migration
AutoMigrate metodu bizim için otomatik taşımaya yardımcı olur. Tabloları kısıtlamaları indeksleri foreign key'leri vb. oluşturacaktır:
func (db *DB) AutoMigrate(dst ...interface{}) errorÖrneğin:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string `gorm:"type:varchar(100);uniqueIndex;"`
Address string
}
type Order struct {
Id uint
Name string
}
db.AutoMigrate(Person{}, Order{})
// CREATE TABLE `person` (`id` bigint unsigned AUTO_INCREMENT,`name` varchar(100),`address` longtext,PRIMARY KEY (`id`),UNIQUE INDEX `idx_person_name` (`name`))
// CREATE TABLE `orders` (`id` bigint unsigned AUTO_INCREMENT,`name` longtext,PRIMARY KEY (`id`))Veya Migrator metodu aracılığıyla Migrator arayüzüne erişerek manuel olarak çalıştırabiliriz:
func (db *DB) Migrator() MigratorAşağıdaki arayüz metodlarını destekler:
type Migrator interface {
// AutoMigrate
AutoMigrate(dst ...interface{}) error
// Database
CurrentDatabase() string
FullDataTypeOf(*schema.Field) clause.Expr
GetTypeAliases(databaseTypeName string) []string
// Tables
CreateTable(dst ...interface{}) error
DropTable(dst ...interface{}) error
HasTable(dst interface{}) bool
RenameTable(oldName, newName interface{}) error
GetTables() (tableList []string, err error)
TableType(dst interface{}) (TableType, error)
// Columns
AddColumn(dst interface{}, field string) error
DropColumn(dst interface{}, field string) error
AlterColumn(dst interface{}, field string) error
MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error
HasColumn(dst interface{}, field string) bool
RenameColumn(dst interface{}, oldName, field string) error
ColumnTypes(dst interface{}) ([]ColumnType, error)
// Views
CreateView(name string, option ViewOption) error
DropView(name string) error
// Constraints
CreateConstraint(dst interface{}, name string) error
DropConstraint(dst interface{}, name string) error
HasConstraint(dst interface{}, name string) bool
// Indexes
CreateIndex(dst interface{}, name string) error
DropIndex(dst interface{}, name string) error
HasIndex(dst interface{}, name string) bool
RenameIndex(dst interface{}, oldName, newName string) error
GetIndexes(dst interface{}) ([]Index, error)
}Metod listesi birden fazla boyutu içerir: veritabanı, tablo, sütun, görünüm, indeks, kısıtlama. Özelleştirme gereken kullanıcılar için daha ince taneli işlemlere izin verir.
Tablo Yorumlarını Belirtme
Taşıma sırasında tablo yorumları eklemek istiyorsanız aşağıdaki gibi ayarlayabilirsiniz:
db.Set("gorm:table_options", " comment 'person table'").Migrator().CreateTable(Person{})AutoMigrate() metodunu taşıma için kullanırsanız ve struct'lar arasında referans ilişkileri varsa gorm önce referans alınan tabloları yinelemeli olarak oluşturacaktır. Bu hem referans alınan hem de referans eden tablolarda yinelenen yorumlara neden olacaktır. Bu nedenle CreateTable metodunu kullanmanız önerilir.
TIP
Tablo oluştururken CreateTable metodu referans edilen tablonun referans eden tablodan önce oluşturulmasını gerektirir. Aksi takdirde hata verir. AutoMigrate metodu bunu gerektirmez çünkü tabloları referans ilişkilerini takip ederek yinelemeli olarak oluşturur.
Create
Create
Yeni kayıtlar oluştururken Create metodu çoğu durumda kullanılır:
func (db *DB) Create(value interface{}) (tx *DB)Verilen struct:
type Person struct {
Id uint `gorm:"primaryKey;"`
Name string
}Kayıt oluştur:
user := Person{
Name: "jack",
}
// Referans geçirmelisiniz
db = db.Create(&user)
// Yürütme sırasında hata oluştu
err = db.Error
// Etkilenen satır sayısı
affected := db.RowsAffectedOluşturmadan sonra gorm birincil anahtarı user struct'ına yazacaktır. Bu nedenle işaretçi geçirmeniz gerekir. Slice geçirdiyseniz toplu oluşturur:
user := []Person{
{Name: "jack"},
{Name: "mike"},
{Name: "lili"},
}
db = db.Create(&user)Benzer şekilde gorm birincil anahtarı slice'a yazar. Veri hacmi çok büyük olduğunda CreateInBatches metodunu kullanarak toplu olarak oluşturabilirsiniz. Çünkü oluşturulan INSERT INTO table VALUES (),() SQL ifadesi çok uzun hale gelir ve her veritabanının SQL uzunluğu için sınırları vardır. Bu nedenle gerektiğinde toplu oluşturmayı seçebilirsiniz:
db = db.CreateInBatches(&user, 50)Ayrıca Save metodu da kayıt oluşturabilir. İşlevi birincil anahtar eşleştiğinde kaydı güncellemektir. Aksi takdirde ekler:
func (db *DB) Save(value interface{}) (tx *DB)user := []Person{
{Name: "jack"},
{Name: "mike"},
{Name: "lili"},
}
db = db.Save(&user)Upsert
Save metodu yalnızca birincil anahtarları eşleştirebilir. Daha özel bir upsert'i Clause oluşturarak构建 edebiliriz. Örneğin bu kod satırı:
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "name"}},
DoNothing: false,
DoUpdates: clause.AssignmentColumns([]string{"address"}),
UpdateAll: false,
}).Create(&p)İşlevi şudur: name alanı çakıştığında address alanının değerini güncelle. Çakışma yoksa yeni kayıt oluştur. Çakışma olduğunda hiçbir şey yapmayabilirsiniz:
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "name"}},
DoNothing: true,
}).Create(&p)Veya doğrudan tüm alanları güncelleyin:
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "name"}},
UpdateAll: true,
}).Create(&p)Upsert kullanmadan önce çakışma alanına indeks eklemeyi unutmayın.
Query
First
Sorgular için gorm oldukça fazla metod sağlar. İlki First metodudur:
func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB)İşlevi birincil anahtara göre artan sırada ilk kaydı bulmaktır. Örneğin:
var person Person
result := db.First(&person)
err := result.Error
affected := result.RowsAffectedGorm'un sorgulanan veriyi struct'a eşlemesi için dest işaretçisi geçirin.
Veya sorgu tablosunu belirtmek için Table ve Model metodlarını kullanın. İlki string tablo adı alır. İkincisi varlık modeli alır:
db.Table("person").Find(&p)
db.Model(Person{}).Find(&p)TIP
Geçirilen işaretçi elemanı bir varlık modeli içeriyorsa (struct işaretçisi veya struct slice işaretçisi gibi) hangi tabloyu sorgulayacağınızı manuel olarak belirtmenize gerek yoktur. Bu kural tüm CRUD işlemleri için geçerlidir.
Take
Take metodu First'a benzer. Farkı birincil anahtara göre sıralamamasıdır:
func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB)var person Person
result := db.Take(&person)
err := result.Error
affected := result.RowsAffectedPluck
Pluck metodu bir tablodan tek bir sütunu toplu olarak sorgulamak için kullanılır. Sorgu sonuçları belirli bir türde slice'ta toplanabilir. Mutlaka varlık türü slice'ı olmak zorunda değildir:
func (db *DB) Pluck(column string, dest interface{}) (tx *DB)Örneğin herkesin adresini string slice'ta topla:
var adds []string
// SELECT `address` FROM `person` WHERE name IN ('jack','lili')
db.Model(Person{}).Where("name IN ?", []string{"jack", "lili"}).Pluck("address", &adds)Aslında şuna eşdeğerdir:
db.Select("address").Where("name IN ?", []string{"jack", "lili"}).Find(&adds)Count
Count metodu varlık kayıt sayısını saymak için kullanılır:
func (db *DB) Count(count *int64) (tx *DB)Kullanım örneğine bakın:
var count int64
// SELECT count(*) FROM `person`
db.Model(Person{}).Count(&count)Find
Toplu sorgular için en yaygın kullanılan metod Find metodudur:
func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB)Verilen koşullarla eşleşen tüm kayıtları bulur:
// SELECT * FROM `person`
var ps []Person
db.Find(&ps)Select
Varsayılan olarak gorm tüm alanları sorgular. Select metodu aracılığıyla alanları belirtebiliriz:
func (db *DB) Select(query interface{}, args ...interface{}) (tx *DB)Örneğin:
// SELECT `address`,`name` FROM `person` ORDER BY `person`.`id` LIMIT 1
db.Select("address", "name").First(&p)Şuna eşdeğerdir:
db.Select([]string{"address", "name"}).First(&p)Aynı zamanda Omit metodunu kullanarak alanları yoksayabilirsiniz:
func (db *DB) Omit(columns ...string) (tx *DB)Örneğin:
// SELECT `person`.`id`,`person`.`name` FROM `person` WHERE id IN (1,2,3,4)
db.Omit("address").Where("id IN ?", []int{1, 2, 3, 4}).Find(&ps)Select ve Omit tarafından seçilen veya yoksayılan alanlar oluşturma ve güncelleme sorgularında da geçerli olacaktır.
Where
Koşullu sorgular Where metodunu kullanır:
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB)İşte basit bir örnek:
var p Person
db.Where("id = ?", 1).First(&p)Zincir işlemlerinde birden fazla Where kullanmak birden fazla AND ifadesi oluşturur. Örneğin:
// SELECT * FROM `person` WHERE id = 1 AND name = 'jack' ORDER BY `person`.`id` LIMIT 1
db.Where("id = ?", 1).Where("name = ?", "jack").First(&p)Veya OR ifadeleri oluşturmak için Or metodunu kullanın:
func (db *DB) Or(query interface{}, args ...interface{}) (tx *DB)// SELECT * FROM `person` WHERE id = 1 OR name = 'jack' AND address = 'usa' ORDER BY `person`.`id` LIMIT 1
db.Where("id = ?", 1).
Or("name = ?", "jack").
Where("address = ?", "usa").
First(&p)Ayrıca Not metodu da benzerdir:
func (db *DB) Not(query interface{}, args ...interface{}) (tx *DB)// SELECT * FROM `person` WHERE id = 1 OR name = 'jack' AND NOT name = 'mike' AND address = 'usa' ORDER BY `person`.`id` LIMIT 1
db.Where("id = ?", 1).
Or("name = ?", "jack").
Not("name = ?", "mike").
Where("address = ?", "usa").
First(&p)IN koşulları için slice'ı doğrudan Where metoduna geçirebilirsiniz:
db.Where("address IN ?", []string{"cn", "us"}).Find(&ps)Veya çok sütunlu IN koşulları için parametreleri taşımak için [][]any türünü kullanmanız gerekir:
// SELECT * FROM `person` WHERE (id, name, address) IN ((1,'jack','uk'),(2,'mike','usa'))
db.Where("(id, name, address) IN ?", [][]any{{1, "jack", "uk"}, {2, "mike", "usa"}}).Find(&ps)Gorm where gruplandırmayı destekler. Yukarıdaki ifadeleri birleştirir:
db.Where(
db.Where("name IN ?", []string{"cn", "uk"}).Where("id IN ?", []uint{1, 2}),
).Or(
db.Where("name IN ?", []string{"usa", "jp"}).Where("id IN ?", []uint{3, 4}),
).Find(&ps)
// SELECT * FROM `person` WHERE (name IN ('cn','uk') AND id IN (1,2)) OR (name IN ('usa','jp') AND id IN (3,4))Order
Sıralama Order metodunu kullanır:
func (db *DB) Order(value interface{}) (tx *DB)Kullanım örneğine bakın:
var ps []Person
// SELECT * FROM `person` ORDER BY name ASC, id DESC
db.Order("name ASC, id DESC").Find(&ps)Birden fazla kez çağırabilirsiniz:
// SELECT * FROM `person` ORDER BY name ASC, id DESC, address
db.Order("name ASC, id DESC").Order("address").Find(&ps)Limit
Limit ve Offset metodları genellikle sayfalama sorguları için kullanılır:
func (db *DB) Limit(limit int) (tx *DB)
func (db *DB) Offset(offset int) (tx *DB)İşte basit bir sayfalama örneği:
var (
ps []Person
page = 2
size = 10
)
// SELECT * FROM `person` LIMIT 10 OFFSET 10
db.Offset((page - 1) * size).Limit(size).Find(&ps)Group
Group ve Having metodları çoğunlukla gruplama işlemleri için kullanılır:
func (db *DB) Group(name string) (tx *DB)
func (db *DB) Having(query interface{}, args ...interface{}) (tx *DB)Örneğe bakın:
var (
ps []Person
)
// SELECT `address` FROM `person` GROUP BY `address` HAVING address IN ('cn','us')
db.Select("address").Group("address").Having("address IN ?", []string{"cn", "us"}).Find(&ps)Distinct
Distinct metodu çoğunlukla tekrarları önlemek için kullanılır:
func (db *DB) Distinct(args ...interface{}) (tx *DB)Örneğe bakın:
// SELECT DISTINCT `name` FROM `person` WHERE address IN ('cn','us')
db.Where("address IN ?", []string{"cn", "us"}).Distinct("name").Find(&ps)SubQuery
Alt sorgu iç içe geçmiş bir sorgudur. Örneğin id değeri ortalamadan büyük olan tüm kişileri bulmak istiyorsanız:
// SELECT * FROM `person` WHERE id > (SELECT AVG(id) FROM `person`
db.Where("id > (?)", db.Model(Person{}).Select("AVG(id)")).Find(&ps)Alt sorgudan:
// SELECT * FROM (SELECT * FROM `person` WHERE address IN ('cn','uk')) as p
db.Table("(?) as p", db.Model(Person{}).Where("address IN ?", []string{"cn", "uk"})).Find(&ps)Lock
Gorm kilitleme desteği sağlamak için clause.Locking clause'unu kullanır:
// SELECT * FROM `person` FOR UPDATE
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&ps)
// SELECT * FROM `person` FOR SHARE NOWAIT
db.Clauses(clause.Locking{Strength: "SHARE", Options: "NOWAIT"}).Find(&ps)Iteration
Rows metodu aracılığıyla iterator alabilirsiniz:
func (db *DB) Rows() (*sql.Rows, error)Iterator'ı travers ederek ScanRows metodunu kullanarak her satır sonucunu struct'a tarayın:
rows, err := db.Model(Person{}).Rows()
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
var p Person
err := db.ScanRows(rows, &p)
if err != nil {
return
}
}Update
Save
Save metodu oluşturma sırasında belirtilmişti. Kayıtları güncellemek için de kullanılabilir. Tüm alanları güncelleyecektir. Bazı struct alanları sıfır değer olsa bile. Ancak birincil anahtar eşleşmezse ekleme işlemi yapar:
var p Person
db.First(&p)
p.Address = "poland"
// UPDATE `person` SET `name`='json',`address`='poland' WHERE `id` = 2
db.Save(&p)Birincil anahtar hariç tüm alanları SET ifadesine eklediğini görebilirsiniz.
Update
Bu nedenle çoğu durumda Update metodunu kullanmanız önerilir:
func (db *DB) Update(column string, value interface{}) (tx *DB)Öncelikle tek sütun alanını güncellemek için kullanılır:
var p Person
db.First(&p)
// UPDATE `person` SET `address`='poland' WHERE id = 2
db.Model(Person{}).Where("id = ?", p.Id).Update("address", "poland")Updates
Updates metodu birden fazla sütunu güncellemek için kullanılır. Parametre olarak struct'ları ve map'leri kabul eder. Struct alanları sıfır değer olduğunda bu alanlar yoksayılır. Ancak map'lerde yoksayılmaz:
func (db *DB) Updates(values interface{}) (tx *DB)İşte bir örnek:
var p Person
db.First(&p)
// UPDATE `person` SET `name`='jojo',`address`='poland' WHERE `id` = 2
db.Model(p).Updates(Person{Name: "jojo", Address: "poland"})
// UPDATE `person` SET `address`='poland',`name`='jojo' WHERE `id` = 2
db.Model(p).Updates(map[string]any{"name": "jojo", "address": "poland"})SQL İfadeleri
Bazen alanlarda kendi kendine artırma veya azaltma ya da alanın kendisini içeren diğer işlemleri yapmanız gerekir. Genellikle önce sorgulama sonra hesaplama sonra güncelleme yaparsınız veya SQL ifadelerini kullanırsınız:
func Expr(expr string, args ...interface{}) clause.ExprAşağıdaki örneğe bakın:
// UPDATE `person` SET `age`=age + age,`name`='jojo' WHERE `id` = 2
db.Model(p).Updates(map[string]any{"name": "jojo", "age": gorm.Expr("age + age")})
// UPDATE `person` SET `age`=age * 2 + age,`name`='jojo' WHERE `id` = 2
db.Model(p).Updates(map[string]any{"name": "jojo", "age": gorm.Expr("age * 2 + age")})Delete
gorm'da kayıt silme Delete metodunu kullanır. Doğrudan varlık struct'ı geçirebilir veya koşullar geçirebilir:
func (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB)Örneğin doğrudan struct geçirin:
var p Person
db.First(&p)
// DELETE FROM `person` WHERE `person`.`id` = 2
db.Delete(&p)Veya:
var p Person
db.First(&p)
// DELETE FROM `person` WHERE `person`.`id` = 2
db.Model(p).Delete(nil)Veya koşulları belirtin:
// DELETE FROM `person` WHERE id = 2
db.Model(Person{}).Where("id = ?", p.Id).Delete(nil)Veya şu şekilde basitleştirin:
var p Person
db.First(&p)
// DELETE FROM `person` WHERE id = 2
db.Delete(&Person{}, "id = ?", 2)
// DELETE FROM `person` WHERE `person`.`id` = 2
db.Delete(&Person{}, 2)Toplu silme için slice geçirin:
// DELETE FROM `person` WHERE id IN (1,2,3)
db.Delete(&Person{}, "id IN ?", []uint{1, 2, 3})
// DELETE FROM `person` WHERE `person`.`id` IN (1,2,3)
db.Delete(&Person{}, []uint{1, 2, 3})Soft Delete
Varlık modeliniz soft delete kullanıyorsa silme sırasında varsayılan olarak güncelleme işlemi yapar. Kalıcı olarak silmek istiyorsanız Unscoped metodunu kullanabilirsiniz:
db.Unscoped().Delete(&Person{}, []uint{1, 2, 3})İlişki Tanımı
Gorm tablo ilişkisi etkileşim yetenekleri sağlar. Embed struct'lar ve alanlar aracılığıyla struct'lar arasındaki ilişkileri tanımlar.
Bire-Bir
Bire-bir ilişki en basitidir. Normalde bir kişinin yalnızca bir annesi olabilir. Aşağıdaki struct'a bakın:
type Person struct {
Id uint
Name string
Address string
Age uint
MomId sql.NullInt64
Mom Mom `gorm:"foreignKey:MomId;"`
}
type Mom struct {
Id uint
Name string
}Person struct'ı Mom struct'ını embed ederek Mom türüne referans gerçekleştirir. Bunlardan Person.MomId referans alandır ve birincil anahtar Mom.Id referans alınan alandır. Bu bire-bir ilişkiyi tamamlar. Foreign key'leri referansları kısıtlamaları ve varsayılan foreign key kurallarını nasıl özelleştireceğiniz Foreign Key Tanımı bölümünde açıklanmıştır. Tekrarlanmayacaktır.
TIP
Foreign key alanları için sql paketi tarafından sağlanan türleri kullanmanız önerilir. Çünkü foreign key'ler varsayılan olarak NULL olabilir. Create ile kayıt oluştururken sıradan türler kullanırsanız sıfır değer 0 da oluşturulur. Mevcut olmayan foreign key oluşturmak açıkça izin verilmez.
Bire-Çok
Aşağıda bir school struct'ı ekleyin. Okul ve öğrenci arasındaki ilişki bire-çoktur. Bir okulun birden fazla öğrencisi vardır ancak bir öğrenci yalnızca bir okula gidebilir:
type Person struct {
Id uint
Name string
Address string
Age uint
MomId sql.NullInt64
Mom Mom `gorm:"foreignKey:MomId;"`
SchoolId sql.NullInt64
School School `gorm:"foreignKey:SchoolId;"`
}
type Mom struct {
Id uint
Name string
}
type School struct {
Id uint
Name string
Persons []Person `gorm:"foreignKey:SchoolId;"`
}school.Persons []Person türündedir. Birden fazla öğrencisi olabileceğini gösterirken Person School'a referans veren bir foreign key içermelidir. Bu Person.SchoolId'dir.
Çoka-Çok
Bir kişi birçok eve sahip olabilir ve bir evde birçok kişi yaşayabilir. Bu çoka-çok ilişkidir:
type Person struct {
Id uint
Name string
Address string
Age uint
MomId sql.NullInt64
Mom Mom `gorm:"foreignKey:MomId;"`
SchoolId sql.NullInt64
School School `gorm:"foreignKey:SchoolId;"`
Houses []House `gorm:"many2many:person_house;"`
}
type Mom struct {
Id uint
Name string
}
type School struct {
Id uint
Name string
Persons []Person
}
type House struct {
Id uint
Name string
Persons []Person `gorm:"many2many:person_house;"`
}
type PersonHouse struct {
PersonId sql.NullInt64
Person Person `gorm:"foreignKey:PersonId;"`
HouseId sql.NullInt64
House House `gorm:"foreignKey:HouseId;"`
}Person ve House çoka-çok ilişkiyi temsil etmek için birbirlerinin slice türlerini tutar. Çoka-çok ilişkiler genellikle many2many aracılığıyla belirtilen bir join tablosu oluşturmayı gerektirir. Join tablosunun foreign key'leri doğru şekilde belirtilmelidir.
Struct'ları oluşturduktan sonra gorm'un veritabanına otomatik olarak taşımasına izin verin:
tables := []any{
School{},
Mom{},
Person{},
House{},
PersonHouse{},
}
for _, table := range tables {
db.Migrator().CreateTable(&table)
}Referans edilen ve referans eden tablolar arasındaki oluşturma sırasına dikkat edin.
İlişki İşlemleri
Yukarıdaki üç ilişkiyi oluşturduktan sonra sonraki adım ilişkileri kullanarak CRUD yapmaktır. Bu öncelikle Association metodunu kullanır:
func (db *DB) Association(column string) *AssociationBir ilişki parametresi alır. Değeri referans struct'ına embed edilen referans alınan türün alan adı olmalıdır:
db.Model(&person).Association("Mom").Find(&mom)Örneğin bir kişinin annesini ilişki aracılığıyla bulmak için Association parametresi Mom'dur. Bu Person.Mom alan adıdır.
İlişki Oluştur
// Veri tanımla
jenny := Mom{
Name: "jenny",
}
mit := School{
Name: "MIT",
Persons: nil,
}
h1 := House{
Id: 0,
Name: "h1",
Persons: nil,
}
h2 := House{
Name: "h2",
Persons: nil,
}
jack := Person{
Name: "jack",
Address: "usa",
Age: 18,
}
mike := Person{
Name: "mike",
Address: "uk",
Age: 20,
}
// INSERT INTO `people` (`name`,`address`,`age`,`mom_id`,`school_id`) VALUES ('jack','usa',18,NULL,NULL)
db.Create(&jack)
// INSERT INTO `schools` (`name`) VALUES ('MIT')
db.Create(&mit)
// Person-Mom ilişkisi ekle. Bire-bir ilişki
// INSERT INTO `moms` (`name`) VALUES ('jenny') ON DUPLICATE KEY UPDATE `id`=`id`
// UPDATE `people` SET `mom_id`=1 WHERE `id` = 1
db.Model(&jack).Association("Mom").Append(&jenny)
// School-Person ilişkisi ekle. Bire-çok ilişki
// INSERT INTO `people` (`name`,`address`,`age`,`mom_id`,`school_id`,`id`) VALUES ('jack','usa',18,1,1,1),('mike','uk',20,NULL,1,DEFAULT) ON DUPLICATE KEY UPDATE `school_id`=VALUES(`school_id`)
db.Model(&mit).Association("Persons").Append([]Person{jack, mike})
// Person-Houses ilişkisi ekle. Çoka-çok ilişki
// INSERT INTO `houses` (`name`) VALUES ('h1'),('h2') ON DUPLICATE KEY UPDATE `id`=`id`
// INSERT INTO `person_house` (`person_id`,`house_id`) VALUES (1,1),(1,2) ON DUPLICATE KEY UPDATE `person_id`=`person_id`
db.Model(&jack).Association("Houses").Append([]House{h1, h2})Tüm kayıtlar mevcut değilse ilişki oluşturma sırasında önce kayıtları oluşturur sonra ilişkiyi oluşturur.
İlişki Bul
Aşağıda ilişkilerin nasıl bulunacağı gösterilmiştir:
// Bire-bir ilişki bul
var person Person
var mom Mom
// SELECT * FROM `people` ORDER BY `people`.`id` LIMIT 1
db.First(&person)
// SELECT * FROM `moms` WHERE `moms`.`id` = 1
db.Model(person).Association("Mom").Find(&mom)
// Bire-çok ilişki bul
var school School
var persons []Person
// SELECT * FROM `schools` ORDER BY `schools`.`id` LIMIT 1
db.First(&school)
// SELECT * FROM `people` WHERE `people`.`school_id` = 1
db.Model(&school).Association("Persons").Find(&persons)
// Çoka-çok ilişki bul
var houses []House
// SELECT `houses`.`id`,`houses`.`name` FROM `houses` JOIN `person_house` ON `person_house`.`house_id` = `houses`.`id` AND `person_house`.`person_id` IN (1,2)
db.Model(&persons).Association("Houses").Find(&houses)İlişki bulma mevcut verilere dayalı olarak referans tabloda koşullarla eşleşen kayıtları bulur. Çoka-çok ilişkiler için gorm tablo join işlemini otomatik olarak tamamlar.
İlişki Güncelle
Aşağıda ilişkilerin nasıl güncelleneceği gösterilmiştir:
// Bire-bir ilişki güncelle
var jack Person
lili := Mom{
Name: "lili",
}
// SELECT * FROM `people` WHERE name = 'jack' ORDER BY `people`.`id` LIMIT 1
db.Where("name = ?", "jack").First(&jack)
// INSERT INTO `moms` (`name`) VALUES ('lili')
db.Create(&lili)
// INSERT INTO `moms` (`name`,`id`) VALUES ('lili',2) ON DUPLICATE KEY UPDATE `id`=`id`
// UPDATE `people` SET `mom_id`=2 WHERE `id` = 1
db.Model(&jack).Association("Mom").Replace(&lili)
// Bire-çok ilişki güncelle
var mit School
newPerson := []Person{{Name: "bob"}, {Name: "jojo"}}
// INSERT INTO `people` (`name`,`address`,`age`,`mom_id`,`school_id`) VALUES ('bob','',0,NULL,NULL),('jojo','',0,NULL,NULL)
db.Create(&newPerson)
// SELECT * FROM `schools` WHERE name = 'mit' ORDER BY `schools`.`id` LIMIT 1
db.Where("name = ?", "mit").First(&mit)
// INSERT INTO `people` (`name`,`address`,`age`,`mom_id`,`school_id`,`id`) VALUES ('bob','',0,NULL,1,4),('jojo','',0,NULL,1,5) ON DUPLICATE KEY UPDATE `school_id`=VALUES(`school_id`)
// UPDATE `people` SET `school_id`=NULL WHERE `people`.`id` NOT IN (4,5) AND `people`.`school_id` = 1
db.Model(&mit).Association("Persons").Replace(newPerson)
// Çoka-çok ilişki güncelle
// INSERT INTO `houses` (`name`) VALUES ('h3'),('h4'),('h5') ON DUPLICATE KEY UPDATE `id`=`id`
// INSERT INTO `person_house` (`person_id`,`house_id`) VALUES (1,3),(1,4),(1,5) ON DUPLICATE KEY UPDATE `person_id`=`person_id`
// DELETE FROM `person_house` WHERE `person_house`.`person_id` = 1 AND `person_house`.`house_id` NOT IN (3,4,5)
db.Model(&jack).Association("Houses").Replace([]House{{Name: "h3"}, {Name: "h4"}, {Name: "h5"}})İlişki güncelleme sırasında hem referans edilen hem de referans eden veriler mevcut değilse gorm bunları oluşturmaya çalışır.
İlişki Sil
Aşağıda ilişkilerin nasıl silineceği gösterilmiştir:
// Bire-bir ilişki sil
var (
jack Person
lili Mom
)
// SELECT * FROM `people` WHERE name = 'jack' ORDER BY `people`.`id` LIMIT 1
db.Where("name = ?", "jack").First(&jack)
// SELECT * FROM `moms` WHERE name = 'lili' ORDER BY `moms`.`id` LIMIT 1
db.Where("name = ?", "lili").First(&lili)
// UPDATE `people` SET `mom_id`=NULL WHERE `people`.`id` = 1 AND `people`.`mom_id` = 2
db.Model(&jack).Association("Mom").Delete(&lili)
// Bire-çok ilişki sil
var (
mit School
persons []Person
)
// SELECT * FROM `schools` WHERE name = 'mit' ORDER BY `schools`.`id` LIMIT 1
db.Where("name = ?", "mit").First(&mit)
// SELECT * FROM `people` WHERE name IN ('jack','mike')
db.Where("name IN ?", []string{"jack", "mike"}).Find(&persons)
// UPDATE `people` SET `school_id`=NULL WHERE `people`.`school_id` = 1 AND `people`.`id` IN (1,2)
db.Model(&mit).Association("Persons").Delete(&persons)
// Çoka-çok ilişki sil
var houses []House
// SELECT * FROM `houses` WHERE name IN ('h3','h4')
db.Where("name IN ?", []string{"h3", "h4"}).Find(&houses)
// DELETE FROM `person_house` WHERE `person_house`.`person_id` = 1 AND `person_house`.`house_id` IN (3,4)
db.Model(&jack).Association("Houses").Delete(&houses)İlişki silme yalnızca aralarındaki referans ilişkisini siler. Varlık kayıtlarını silmez. İlişkiyi doğrudan temizlemek için Clear metodunu da kullanabiliriz:
db.Model(&jack).Association("Houses").Clear()İlgili varlık kayıtlarını silmek istiyorsanız Association işleminden sonra Unscoped işlemini ekleyebilirsiniz (many2many'i etkilemez):
db.Model(&jack).Association("Houses").Unscoped().Delete(&houses)Bire-çok ve çoka-çok için kayıtları silmek için Select işlemini kullanabilirsiniz:
var (
mit School
)
db.Where("name = ?", "mit").First(&mit)
db.Select("Persons").Delete(&mit)Preloading
Preloading ilişki verilerini sorgulamak için kullanılır. İlişki ilişkileri olan varlıklar için önce ilişkili referans alınan varlıkları yükler. Daha önce belirtilen ilişki sorgusu ilişki ilişkilerini sorgular. Preloading doğrudan varlık kayıtlarını sorgular. Tüm ilişki ilişkileri dahil. Sözdizimi açısından ilişki sorgusu önce belirtilen []Person'ı sonra []Person'a dayalı olarak ilişkili []Mom'u sorgulamayı gerektirir. Preloading sözdizimi açısından doğrudan []Person'ı sorgular ve tüm ilişki ilişkilerini de yükler. Ancak pratikte yürüttükleri SQL neredeyse aynıdır. Örneğe bakın:
var users []Person
// SELECT * FROM `moms` WHERE `moms`.`id` = 1
// SELECT * FROM `people`
db.Preload("Mom").Find(&users)Bu bire-bir ilişki sorgu örneğidir. Çıktısı:
[{Id:1 Name:jack Address:usa Age:18 MomId:{Int64:1 Valid:true} Mom:{Id:1 Name:jenny} SchoolId:{Int64:1 Valid:true} School:{Id:0 Name: Persons:[]} Houses:[]} {Id:2 Name:mike Address:uk Age:20 MomId:{Int64:0 Valid:false} Mom:{Id:0 Name:} SchoolId:{Int64:1 Valid:true} School:{Id:0 Name: Persons:[]} Houses:[]}]İlişkili Mom'un da sorgulandığını görebilirsiniz ancak school ilişkisi preload edilmedi. Bu nedenle tüm School struct'ları sıfır değerlerdir. Tüm ilişkileri preload etmek için clause.Associations'u da kullanabilirsiniz. İç içe ilişkiler hariç:
db.Preload(clause.Associations).Find(&users)Aşağıda iç içe preload örneği bulunmaktadır. İşlevi tüm school ilişkilerinin tüm öğrencilerini her öğrenciyle ilişkili anneyi ve her öğrencinin sahip olduğu evleri sorgulamaktır. Ayrıca her evin sahip setini de sorgular. School->Student->House->Student:
var schools []School
db.Preload("Persons").
Preload("Persons.Mom").
Preload("Persons.Houses").
Preload("Persons.Houses.Persons").Find(&schools)
// Çıktı kodu. Mantık yoksayılabilir
for _, school := range schools {
fmt.Println("school", school.Name)
for _, person := range school.Persons {
fmt.Println("person", person.Name)
fmt.Println("mom", person.Mom.Name)
for _, house := range person.Houses {
var persons []string
for _, p := range house.Persons {
persons = append(persons, p.Name)
}
fmt.Println("house", house.Name, "owner", persons)
}
fmt.Println()
}
}Çıktı:
school MIT
person jack
mom jenny
house h1 owner [jack]
house h2 owner [jack]
person mike
momHer okulun her öğrencisinin annesini ve evlerini ve her evin sahiplerini çıktılar.
İşlemler
Gorm varsayılan olarak işlemleri etkinleştirir. Herhangi bir ekleme ve güncelleme işlemi başarısız olduktan sonra geri alınır. Bağlantı Yapılandırması bölümünde devre dışı bırakabilirsiniz. Performans yaklaşık yüzde 30 artar. Gorm'da işlemleri kullanmak için birden fazla metod vardır. Aşağıda basit bir tanıtım bulunmaktadır.
Otomatik
Closure işlemi. Transaction metodu aracılığıyla closure fonksiyonu geçirin. Fonksiyon dönüş değeri nil değilse otomatik olarak geri alınır:
func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error)Örneğe bakın. Closure içindeki işlemler harici db yerine parametre tx'i kullanmalıdır:
var ps []Person
db.Transaction(func(tx *gorm.DB) error {
err := tx.Create(&ps).Error
if err != nil {
return err
}
err = tx.Create(&ps).Error
if err != nil {
return err
}
err = tx.Model(Person{}).Where("id = ?", 1).Update("name", "jack").Error
if err != nil {
return err
}
return nil
})Manuel
Manuel işlemler daha çok önerilir. Ne zaman geri alınacağını ve ne zaman commit edileceğini kontrol ederiz. Manuel işlemler aşağıdaki üç metodu kullanır:
// Begin metodu işlem başlatmak için kullanılır
func (db *DB) Begin(opts ...*sql.TxOptions) *DB
// Rollback metodu işlemi geri almak için kullanılır
func (db *DB) Rollback() *DB
// Commit metodu işlemi commit etmek için kullanılır
func (db *DB) Commit() *DBÖrneğe bakın. İşlem başlattıktan sonra ORM'i çalıştırmak için tx kullanmalısınız:
var ps []Person
tx := db.Begin()
err := tx.Create(&ps).Error
if err != nil {
tx.Rollback()
return
}
err = tx.Create(&ps).Error
if err != nil {
tx.Rollback()
return
}
err = tx.Model(Person{}).Where("id = ?", 1).Update("name", "jack").Error
if err != nil {
tx.Rollback()
return
}
tx.Commit()Savepoint'leri belirtebilirsiniz:
var ps []Person
tx := db.Begin()
err := tx.Create(&ps).Error
if err != nil {
tx.Rollback()
return
}
tx.SavePoint("createBatch")
err = tx.Create(&ps).Error
if err != nil {
tx.Rollback()
return
}
err = tx.Model(Person{}).Where("id = ?", 1).Update("name", "jack").Error
if err != nil {
tx.RollbackTo("createBatch")
return
}
tx.Commit()Özet
Yukarıdaki içeriğin tamamını okuduysanız ve kodu yazdıysanız veritabanında CRUD işlemleri gerçekleştirmek için gorm'u kullanabilirsiniz. Bu işlemlerin yanı sıra gorm'un birçok başka özelliği vardır. Daha fazla ayrıntı için resmi dokümantasyonu kontrol edebilirsiniz.
