Go bietet mit dem Package sql eine Datenbankabstraktion an, mit deren Hilfe man herstellerunabhängig auf SQL Datenbanken zugreifen kann. Dies geschieht über den type sql.DB
Package sql provides a generic interface around SQL (or SQL-like) databases.
Der type sql.DB
stellt ein Handle zu einem Datenbankpool dar, der die darunterliegende Datenbankverbindungen verwaltet. Ein konkurrierender Zugriff, z. B. von mehreren Go-Routinen (goroutine) heraus, ist ausdrücklich möglich.
Importiert werden kann der type mittels:
import "database/sql"
Zusätzlich zum sql Package wird ein Datenbanktreiber benötigt. Eine Liste von verfügbaren Treibern findet man auf den GitHub Seiten des Golang Projektes.
Die Installation eines Treibers erfolgt im Normalfall über die Installation einer Go Abhängigkeit. Ein MySQL Treiber kann so installiert werden:
go get -u github.com/go-sql-driver/mysql
Anschließend muss der Treiber noch in der Anwendung importiert werden.
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
Nachdem die Vorbereitungen für eine Datenbankverbindung geschaffen wurden kann man eine Verbindung “öffnen”. Öffnen bedeutet hier nicht, dass sofort eine physikalische Verbindung aufgebaut wird. Dies geschieht erst, wenn tatsächlich eine Abfrage/Query abgesetzt wird.
Allgemein:
db, err := sql.Open(driver, dataSourceName)
Der Parameter driver
gibt den Treibernamen an, mit dem Parameter dataSourceName
werden Datenbankverbindungsparameter wie URL, Username, Passwort etc übergeben.
Für MySQL sieht es so aus:
db, err := sql.Open("mysql", "user:password@/dbname")
Da, wie oben erwähnt, nicht sofort eine Verbindung aufgebaut wird, gibt es einen sogenannten PingContext
Aufruf, mit dem eine Verbindung validiert werden kann.
ctx := context.Background()
if err := db.PingContext(ctx); err != nil {
log.Fatal(err)
}
Sobald eine Verbindung aufgebaut ist können Daten gelesen oder geschrieben werden. Folgender Code erzeugt einen neuen Kunden in der Datenbank mit der ID ‘1’ und dem Vornamen ‘gopher’:
result, err := db.ExecContext(ctx,
"INSERT INTO CUSTOMER (id, firstname) VALUES (?, ?)",
1,
"gopher"
)
Die Variable result
ist vom type Result und enthält zwei Werte (über entsprechende Methoden):
RowsAffected
lastInsertId, err := result.LastInsertId()
rowsAffected, err := result.RowsAffected()
Zum Auslesen der Werte kann die Methode QueryContext
verwendet werden.
rows, err := db.QueryContext(ctx, "SELECT firstname FROM customer where id = ?", 2)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var firstname string
var id int
if err := rows.Scan(&firstname); err != nil {
log.Fatal(err)
}
log.Printf("%s is %d\n", firstname, id)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
Alternativ, wenn nur ein Ergebnis erwartet wird, gibt es noch die Methode QueryRowContext
, die analog verwendet werden kann.
Die Schnittstelle enthält weiterhin Methoden wie z. B. PrepareContext
mit der man Prepared Statements für eine spätere Nutzung anlegen kann.
Natürlich unterstützt das sql Package auch Transaktionen. Diese können durch Aufruf von BeginTx()
gestartet und durch Commit()
oder Rollback()
beendet werden.
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Fatal(err)
}
The provided context is used until the transaction is committed or rolled back. If the context is canceled, the sql package will roll back the transaction. Tx.Commit will return an error if the context provided to BeginTx is canceled.
tx.Commit()
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/ibmdb/go_ibm_db"
)
func main() {
con := "HOSTNAME=127.0.0.1;DATABASE=GOTEST;PORT=50000;UID=db2ins1;PWD=superGeheimesPasswort"
db, err := sql.Open("go_ibm_db", con)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
if err := db.PingContext(ctx); err != nil {
log.Fatal(err)
}
result, err := db.ExecContext(ctx,
"INSERT INTO CUSTOMER (id, firstname) VALUES (?, ?)",
1,
"gopher")
if err != nil {
log.Fatal(err)
}
lastInsertID, err := result.LastInsertId()
rowsAffected, err := result.RowsAffected()
fmt.Println("Insert", lastInsertID, rowsAffected)
rows, err := db.QueryContext(ctx, "SELECT firstname FROM customer where id = ?", 2)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var firstname string
if err := rows.Scan(&firstname); err != nil {
log.Fatal(err)
}
log.Printf("%s", firstname)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
db.Close()
}
FROM ibmcom/db2express-c:latest
RUN su - db2inst1 -c "db2start \
&& db2 'create db GOTEST' \
&& db2 'connect to GOTEST' \
&& db2 +c 'create table customer (id integer not null, firstname varchar(256), lastname varchar(256), birthday date, data blob)' \
&& db2 LIST TABLES"
version: '2'
services:
db:
build: db-docker
ports:
- "50000:50000"
environment:
- DB2INST1_PASSWORD=superGeheimesPasswort
- LICENSE=accept
command: db2start
Das komplette Beispiel kann auch in GitHub gefunden werden.
Weitere Informationen und Dokumentation findet man z. B. hier:
25.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