2015-08-30 11 views
7

Ich möchte mit Go eine API für eine vorhandene Datenbank erstellen, die NULL-Werte ausgiebig verwendet. Go scannt Nullen nicht in leere Strings (oder gleichwertig). Daher muss ich eine Problemumgehung implementieren.Bypass-SQL null Wert Probleme in Go

Die Problemumgehungen, die ich entdeckt habe, haben mich unzufrieden gemacht. In der Tat habe ich wegen dieses Problems nach einer dynamischen Sprache gesucht, aber Go hat bestimmte Attraktionen und ich würde gerne dabei bleiben, wenn es möglich wäre. Hier sind die Problemumgehungen, die nicht erfüllt wurden:

  1. Verwenden Sie keine Nullwerte in der Datenbank. Ungeeignet, weil die Datenbank bereits existiert und ich nicht die Freiheit habe, in ihre Struktur einzugreifen. Die Datenbank ist wichtiger als meine App, nicht umgekehrt.
  2. Verwenden Sie in SQL-Abfragen COALESCE, ISNULL usw., um Nullen in leere Strings (oder Äquiv.) Umzuwandeln, bevor die Daten in meine App gelangen. Ungeeignet, da viele Felder und viele Tische vorhanden sind. Abgesehen von ein paar offensichtlichen (Primärschlüssel, Nachname) weiß ich nicht genau, auf welche Felder man sich verlassen kann, um mir keinen Nullwert zu geben, also würde ich meine SQL-Abfragen überall defensiv verstopfen.
  3. Verwenden Sie sql.NullString, sql.NullInt64, sql.NullFloat64 usw., um Nullen als Zwischenschritt in leere Strings (oder equiv) umzuwandeln, bevor Sie sie in ihren Zieltyp setzen. Dies hat das gleiche Problem wie oben, nur dass ich meinen Go-Code anstelle meiner SQL-Abfragen überflute.
  4. Verwenden Sie eine Kombination aus * pointers und [] byte, um jedes Element an einen Speicherort zu durchsuchen, ohne es an einen bestimmten Typ (anders als [] Byte) zu übergeben und dann irgendwie mit den Rohdaten zu arbeiten. Aber um etwas Sinnvolles mit den Daten zu machen, müssen Sie es in etwas Nützlicheres umwandeln, und dann sind Sie wieder bei sql.Nullstring oder wenn x == nil {handle it}, und dies geschieht wieder von Fall zu Fall für jedes Feld, mit dem ich arbeiten muss. Also, wieder, wir schauen auf überladen, chaotisch, fehleranfälligen Code und ich wiederhole mich die ganze Zeit, anstatt trocken in meiner Codierung.
  5. Suchen Sie in den Go ORM-Bibliotheken nach Hilfe. Nun, ich tat das, aber zu meiner Überraschung geht keiner von ihnen mit diesem Problem um.
  6. Erstellen Sie mein eigenes Hilfspaket, um alle Nullzeichenfolgen in "" zu konvertieren, null ints auf 0, null auf 0.00, null bools auf false usw. und machen es zu einem Teil des Scanvorgangs vom SQL-Treiber aus. was zu regelmäßigen, normalen Saiten, Inten, Floats und Bools führt.

    Leider wenn 6 die Lösung ist, habe ich nicht das Know-how. Ich vermute, dass die Lösung etwas beinhalten würde wie "wenn der beabsichtigte Typ des zu scannenden Gegenstandes ein String ist, mache es zu einem sql.NullString und extrahiere einen leeren String daraus. Aber wenn das zu scannende Item ein int ist, mach es zu einem NullInt64 und bekomme eine Null davon. Aber wenn ... (etc) "

Gibt es irgendwas was ich verpasst habe? Vielen Dank.

+3

Nein. Http://golang.org/pkg/database/sql/#NullString ist die Antwort dafür. – elithrar

+2

Eine Anzahl Ihrer Elemente setzt voraus, dass ein SQL NULL wie "" oder 0 behandelt werden soll.Das ist fast nie der Fall, wenn es die Datenbank nicht stören würde, NULL zu verwenden (was normalerweise bedeutet, "nicht gesetzt", "nicht verfügbar", "nicht anwendbar", usw.). Für Programmiersprachen, die kein Konzept von NULL-Basistypen haben (zB ein Zeiger kann nil sein, aber kein int), müssen Sie entweder zwei Variablen verwenden (zB 'value int' und' valid bool' wie bei 'sql.NullString ') pro null-fähigen Spalten- oder Flag-Feld pro Zeile/Datensatz oder einem solchen, um den Gültigkeitszustand aufzuzeichnen. Das ist einfach kein Problem, das durch einen magischen Code weggeworfen werden kann. –

+0

In der Tat. In unserer Datenbank haben einige Leute Vornamen. Der zweite Name ist wirklich nicht "", daher erscheint ein NULL. Dies rettet mich vor dem Irrtum, zu denken, dass Menschen buchstäblich ihren zweiten Vornamen haben. Und versehentlich versucht, sie an die Adresse "" zu mailen oder sie unter der Telefonnummer 0 anzurufen. Aber ernsthaft, ich denke, dass die Datenbankhersteller NULL verwendet haben, nur weil es da war, und es war etwas korrekter als eine leere Zeichenfolge. –

Antwort

1

Die Verwendung von Zeigern für die SQL-Scan-Zielvariablen der Daten ermöglicht werden gescannt, arbeiteten mit (wenn unter Kontrolle! = Nil) und vermarshallten zu json, von der API gesendet werden, ohne Hunderte von sql.Nullstring, sql.Nullfloat64 usw. überallhin zu setzen. Nullen werden auf wundersame Weise bewahrt und durch den Marshall Json ausgesandt. (Siehe Vatername am unteren Rand). Auf der anderen Seite kann der Klient mit den Nullen in Javascript arbeiten, das besser dafür gerüstet ist, damit umzugehen.

func queryToJson(db *sql.DB) []byte { 
    rows, err := db.Query(
     "select mothername, fathername, surname from fams" + 
     "where surname = ?", "Nullfather" 
    ) 
    defer rows.Close() 

    type record struct { 
     Mname, Fname, Surname *string // the key: use pointers 
    } 
    records := []record{} 

    for rows.Next() { 
     var r record 
     err := rows.Scan(r.Mname, r.Fname, r.Surname) // no need for "&" 
     if err != nil { 
      log.Fatal(err) 
     } 
     fmt.Println(r) 
     records = append(records, r) 
    } 
    j, err := json.Marshal(records) 
    if err != nil { 
     log.Fatal(err) 
    } 
    return j 
} 
j := queryToJson(db) 
fmt.Println(string(j)) // [{"Mothername":"Mary", "Fathername":null, "Surname":"Nullfather"}] 
+0

Das ist in Ordnung, wenn Sie den JSON sowieso brauchen. Marshalling ist jedoch langsam, und wenn Sie den JSON nicht benötigen, ist dies eine große Verschwendung von Berechnung. – MattyB

+0

@MattyB Sie konnten die Funktion kurz vor der Marshalling-Zeile stoppen und die "records" -Variable zurückgeben, wenn Sie das Marshalling nicht wollten, aber dann müssen Sie mit diesen potentiell Null-Zeigern umgehen, bevor Sie die Daten verwenden. Für mich scheint das Marshalling sehr schnell; Es wird über ein Netzwerk gesendet und von JavaScript in einem Webbrowser konsumiert. –