Im Artikel GORM ein OR-Mapper (ORM) für Golang wurde gezeigt wie GORM grundsätzlich funktioniert und wie es eingesetzt werden kann. In diesem Artikel soll gezeigt werden wie man Relationen bzw. Associations mit GORM abbilden kann.
Das Beispiel aus dem vorherigen Artikel soll um eine (belongs-to 1:1) Relation erweitert werden. Der Customer
bekommt nun noch eine CreditCard
.
type Customer struct {
gorm.Model
FirstName string
LastName string
CreditCard CreditCard
CreditCardId uint
}
type CreditCard struct {
gorm.Model
Number string
}
Für das Mapping des Foreign-Keys werden Standardwerte angenommen. Hier CreditCardId
bzw. credit_card_id
zum Ablegen des Foreign-Keys im referenzierendem Modell. Über Tags kann dieses Verhalten angepasst werden. Das Beispiel von der Projektseite zeigt wie man den Fremdschlüssel ummappen kann:
type User struct {
gorm.Model
Name string
}
type Profile struct {
gorm.Model
Name string
User User `gorm:"foreignkey:UserRefer"` // use UserRefer as foreign key
UserRefer string
}
Im vorliegenden Beispiel soll mit dem Standardverhalten gearbeitet werden. Beim Aufruf von db.AutoMigrate(&CreditCard{}, &Customer{})
und angeschaltetem Logging sieht man, dass die entsprechenden Tabellen angelegt werden.
CREATE TABLE "credit_cards" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime,"number" varchar(255) )
CREATE INDEX idx_credit_cards_deleted_at ON "credit_cards"(deleted_at)
CREATE TABLE "customers" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"deleted_at" datetime,"first_name" varchar(255),"last_name" varchar(255),"credit_card_id" integer )
CREATE INDEX idx_customers_deleted_at ON "customers"(deleted_at)
Name | type |
---|---|
id | integer |
created_at | datetime |
updated_at | datetime |
deleted_at | datetime |
number | varchar(255) |
Name | type |
---|---|
id | integer |
created_at | datetime |
updated_at | datetime |
deleted_at | datetime |
first_name | varchar(255) |
last_name | varchar(255) |
credit_card_id | integer |
Das Speichern von Associations läuft recht einfach. Man kann sein Objektmodell befüllen und dann das "Hauptobjekt" speichern. In diesem Fall legen wir einen Kunden (Customer
) und eine Kreditkarte (CreditCard
) an und verknüpfen diese im Objektmodell. Danach speichern wir den Kunden.
// Create
customer := Customer{FirstName: "Hans", LastName: "wurst"}
customer.CreditCard = CreditCard{Number: "123-123-123"}
db.Create(&customer)
Dieser Aufruf führt zu folgendem abgesetztem SQL:
INSERT INTO "credit_cards" ("created_at","updated_at","deleted_at","number") VALUES ('xx','xx',NULL,'123-123-123')
INSERT INTO "customers" ("created_at","updated_at","deleted_at","first_name","last_name","credit_card_id") VALUES ('xx','xx',NULL,'Hans','wurst','1')
Auch das Verhalten, dass Referenzen direkt angelegt und die Hauptentität aktualisiert wird, kann konfiguriert werden. Entweder über einen Tag oder explizit im Code. Unterscheiden muss man hierbei zwischen gorm:association_autocreate
und gorm:association_autoupdate
. Bei autocreate
wird die angehängte Entität gespeichert, bei autoupdate
findet ein Update auf die Hauptentität statt.
db.Set("gorm:association_autoupdate", false)
.Set("gorm:association_autocreate", false)
.Create(&customer)
//oder
type Customer struct {
gorm.Model
FirstName string
LastName string
CreditCard CreditCard `gorm:"association_autoupdate:false;association_autocreate:false`
CreditCardId uint
}
Das Auslesen von Relationen bzw. Associations findet explizit statt. Man kann also nicht über den Objektbaum eines eingelesenen Objektes navigieren. Die Relationen müssen explizit nachgeladen werden. Im folgenden Beispiel geschieht dies über die Methode Related()
.
var foundCustomer Customer
var foundCreditCard CreditCard
db.Where(&Customer{FirstName: "Hans"}).First(&foundCustomer)
db.Model(&foundCustomer).Related(&foundCreditCard)
fmt.Println("Gefunden wurde:", foundCreditCard.Number)
Das komplette Beispiel kann auch in GitHub gefunden werden.
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type Customer struct {
gorm.Model
FirstName string
LastName string
CreditCard CreditCard
CreditCardId uint
}
type CreditCard struct {
gorm.Model
Number string
}
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
// LogMode enable
db.LogMode(true)
// Migrate the schema
db.AutoMigrate(&CreditCard{}, &Customer{})
// Create
customer := Customer{FirstName: "Hans", LastName: "wurst"}
customer.CreditCard = CreditCard{Number: "123-123-123"}
db.Create(&customer)
var foundCustomer Customer
var foundCreditCard CreditCard
db.Where(&Customer{FirstName: "Hans"}).First(&foundCustomer)
db.Model(&foundCustomer).Related(&foundCreditCard)
fmt.Println("Gefunden wurde:", foundCreditCard.Number)
db.Delete(&foundCustomer)
}
29.01.2019
Der praktische Soforteinstieg für Developer und Softwarearchitekten, die direkt mit Go produktiv werden wollen.
zur Buchseite beim Rheinwerk Verlag Rheinwerk Computing, ISBN 978-3-8362-7559-0 (als PDF, EPUB, MOBI und Papier)
Source Fellows GmbH
Lerchenstraße 31
72762 Reutlingen
Telefon: (0049) 07121 6969 802
E-Mail: info@source-fellows.com