Relationen mit GORM einem OR-Mapper für Golang

Relationen mit GORM einem OR-Mapper für Golang

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.

Datenmodell definieren

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)

Tabelle credit_cards

Nametype
idinteger
created_atdatetime
updated_atdatetime
deleted_atdatetime
numbervarchar(255)

Tabelle customers

Nametype
idinteger
created_atdatetime
updated_atdatetime
deleted_atdatetime
first_namevarchar(255)
last_namevarchar(255)
credit_card_idinteger

Speichern von Associations

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
}

Lesen von Associations

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)

Komplettes Beispiel

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

 

Kennen Sie schon das Buch zum Thema?

Der praktische Soforteinstieg für Developer und Softwarearchitekten, die direkt mit Go produktiv werden wollen.

  • Von den Sprachgrundlagen bis zur Qualitätssicherung
  • Architekturstil verstehen und direkt anwenden
  • Idiomatic Go, gRPC, Go Cloud Development Kit
  • Cloud-native Anwendungen erstellen
Microservices mit Go Buch

zur Buchseite beim Rheinwerk Verlag Rheinwerk Computing, ISBN 978-3-8362-7559-0 (als PDF, EPUB, MOBI und Papier)

Kontakt

Source Fellows GmbH

Source Fellows GmbH Logo

Lerchenstraße 31

72762 Reutlingen

Telefon: (0049) 07121 6969 802

E-Mail: info@source-fellows.com