Im Artikel Datenbankzugriffe mit Go wurde gezeigt wie man mittels SQL Datenbankzugriffe mit Golang umsetzen kann. In diesem Artikel soll hingegeben ein Objektrelationaler-Mapper (OR-Mapper) eingesetzt werden.
Im Internet findet man einige Bibliotheken, die den Zugriff auf Datenbanken von Go aus erleichtern sollen. Nicht bei allen handelt es sich um "klassische" OR-Mapper. Einge von Ihnen erweitern den Umfang des Golang sql Packages, damit man einfacher und komfortabler damit arbeiten kann.
Bei der Auswahl der richtigen Bibliothek für das eigene Projekt darf man nie die eigene Datenbank aus dem Auge lassen. Es ist immer zu prüfen ob die ausgewählte Bibliothek auch mit der eigenen Datenbank eingesetzt werden kann. Oftmals ist man z. B. mit einer IBM DB2 Datenbank offiziell außen vor. Mit MySQL, PostgreSQL, Sqlite3 und SQL Server sollte man keine großen Probleme zu befürchten zu haben.
Name | Url | Stars bei GitHub | Anmerkung |
---|---|---|---|
gorm | http://gorm.io | ca. 12000 | "Full-Featured ORM (almost)" - offiziell für MySQL, PostgreSQL, Sqlite3 und SQL Server |
beego | https://github.com/astaxie/beego/ | ca. 19000 | "It is heavily influenced by Django ORM, SQLAlchemy." - für MySQL, PostgreSQL, Sqlite3 |
upper.io | http://upper.io | ca. 1600 | "Productive data access layer for Go" - Adapter für PostgreSQL, MySQL, SQLite, QL, SQL Server, MongoDB |
sqlx | http://jmoiron.github.io/sqlx/ | ca. 5600 | "sqlx is a library which provides a set of extensions on go's standard database/sql library." |
xorm | http://xorm.io/ | ca. 4300 | "Simple & Powerful ORM Framework for Go Programming Language" - Support für Mysql, MyMysql, Postgres, Tidb, SQLite, MsSql, Oracle |
gorp | https://github.com/go-gorp/gorp | ca. 3000 | "Go Relational Persistence" - wird nicht aktiv entwickelt |
pg | https://github.com/go-pg/pg | ca. 2300 | "PostgreSQL client and ORM for Golang" |
sqlboiler | https://github.com/volatiletech/sqlboiler | ca.2000 | SQLBoiler is a tool to generate a Go ORM tailored to your database schema. |
Dieser Artikel greift sich GORM The fantastic ORM library for Golang
heraus und stellt ihn kurz vor.
Die Installation von GORM erfolgt go-typisch über
go get -u github.com/jinzhu/gorm
Da in diesem Beispiel mit einer SQLlite Datenbank gearbeitet werden soll muss zusätzlich noch ein DB Treiber installiert werden. Hier wird der go-sqlite3 Treiber verwendet.
go get github.com/mattn/go-sqlite3
Jetzt können die Abhängigkeiten in das eigene Go Programm importiert werden.
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
Das Objektmodell ist im ersten Schritt recht einfach gehalten. Es handelt sich um einen Kunden mit Vor- und Nachname. Weitere Metainformationen für das Modell lassen sich über Tags definieren.
type Customer struct {
gorm.Model
FirstName string
LastName string
}
Dieser soll im weiteren in der Datenbank abgelegt werden. GORM bietet natürlich auch Relationen bzw. Beziehungen zwischen den Objekten an. (Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
) Der Artikel Relationen mit GORM einem OR-Mapper für Golang zeigt diese Möglichkeiten.
Im nächsten Schritt muss eine Datenbankverbindung zu einer SQLlite Datenbank aufgebaut werden. Dies geschieht über einen Open()
Aufruf aus dem Package gorm
.
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
Nachdem eine Verbindung zur DB konfiguriert wurde, sorgen wir dafür, dass die für unser Objekt benötigten Tabellen mit Spalten in der DB vorhanden sind. Dies erfolgt mit einem Aufruf von AutoMigrate()
. Als Parameter wird der entsprechende Struct mitgegeben.
db.AutoMigrate(&Customer{})
An dieser Stelle könnte man natürlich eine Liste von Typen mitgeben.
WARNING: AutoMigrate will ONLY create tables, missing columns and missing indexes, and WON’T change existing column’s type or delete unused columns to protect your data.
Gorm hat in diesem Fall folgende Tabelle customers
angelegt:
Name | type |
---|---|
id | integer |
created_at | datetime |
updated_at | datetime |
deleted_at | datetime |
first_name | varchar(255) |
last_name | varchar(255) |
Mehr zur Migration findet man auch direkt auf der Projektseite. Hier werden auch Features zur manuellen Migration gezeigt. Wenn also der Automatismus nicht ausreicht.
Das Anlegen von Datensätzen funktioniert wie man sich das bei einem OR-Mapper vorstellt:
//Objekt anlegen
customer := Customer{FirstName: "Hans2", LastName: "wurst"}
//Objekt persistieren
db.Create(&customer)
Daten können ebenfalls recht einfach wieder gelesen werden:
//Daten Lesen
var foundCustomer Customer
db.First(&foundCustomer, "First_Name = ?", "Hans2")
//Ausgabe
fmt.Println("Gefunden wurde:", foundCustomer.FirstName)
Zu Beachten ist hier, dass man bei dieser Abfrage das SQL Schema im Blick haben muss. Es handelt sich hierbei um eine "Plain SQL" Abfrage.
Alternativ lässte sich eine Abfrage über eine Vorlage durchführen. So kann man ohne Kenntnisse des darunterliegenden Schemata auf die Daten zugreifen.
db.Where(&Customer{FirstName: "Hans"}).First(&foundCustomer)
Eine Aktualisierung erfolgt über einen Update
Call:
// Update
db.Model(&foundCustomer).Update("LastName", "Meiser")
Löschen funktioniert analog:
db.Delete(&foundCustomer)
Wenn man sich nach dem Löschen die Datenbank anschaut, darf man nicht erschrecken. Im ersten Moment würde man erwarten, dass die Tabelle leer ist. Das ist allerdings nicht der Fall, da GORM den Datensatz "nur" als gelöscht markiert. Der Wert für deleted_at
ist entsprechend gefüllt.
Für den Fall dass man ich etwas mehr anschauen möchte was GORM so macht, kann man das Logging einschalten. Setzt man den LogMode
werden die SQL Statements sowie etwas Metadaten mit ausgegeben.
db.LogMode(true)
Die Auswertung von Fehlern unterscheidet sich vom sonstigen Vorgehen bei Go/Golang. Bei den Datenbankzugriffen bekommt man nicht direkt einen Fehler zurück, auf den man reagieren kann. Grund dafür ist das Fluent-API von GORM. Aufrufe können verkettet werden. Hier ein Beispiel von der Projekt Seite
db.Where("name LIKE ?", "jinzhu%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
Durch die Verkettung kann man nicht direkt die Fehler auswerten. Stattdessen gibt es ein Error
Feld an *gorm.DB
das abgefragt werden kann.
err = db.Delete(&foundCustomer).Error
if err != nil {
panic(err)
}
Für den Fall dass mehrere Fehler in einer Verarbeitung auftreten, können diese über GetErrors()
abgerufen werden. Das ist bei verketteten Aufrufen unter Umständen der Fall.
db.First(&user).Limit(10).Find(&users).GetErrors()
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
}
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(&Customer{})
// Create
customer := Customer{FirstName: "Hans", LastName: "wurst"}
db.Create(&customer)
var foundCustomer Customer
// Plain SQL
db.First(&foundCustomer, "First_Name = ?", "Hans")
// Template
db.Where(&Customer{FirstName: "Hans"}).First(&foundCustomer)
fmt.Println("Gefunden wurde:", foundCustomer.FirstName)
// Update - update product's price to 2000
db.Model(&foundCustomer).Update("LastName", "Meiser")
err = db.Delete(&foundCustomer).Error
if err != nil {
panic(err)
}
}
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