Der folgende Code konvertiert a ResultSet
mit JSONArray
und in eine JSON-Zeichenfolge JSONObject
.
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
public class ResultSetConverter {
public static JSONArray convert( ResultSet rs )
throws SQLException, JSONException
{
JSONArray json = new JSONArray();
ResultSetMetaData rsmd = rs.getMetaData();
while(rs.next()) {
int numColumns = rsmd.getColumnCount();
JSONObject obj = new JSONObject();
for (int i=1; i<numColumns+1; i++) {
String column_name = rsmd.getColumnName(i);
if(rsmd.getColumnType(i)==java.sql.Types.ARRAY){
obj.put(column_name, rs.getArray(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.BIGINT){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.BOOLEAN){
obj.put(column_name, rs.getBoolean(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.BLOB){
obj.put(column_name, rs.getBlob(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.DOUBLE){
obj.put(column_name, rs.getDouble(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.FLOAT){
obj.put(column_name, rs.getFloat(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.INTEGER){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.NVARCHAR){
obj.put(column_name, rs.getNString(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.VARCHAR){
obj.put(column_name, rs.getString(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.TINYINT){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.SMALLINT){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.DATE){
obj.put(column_name, rs.getDate(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.TIMESTAMP){
obj.put(column_name, rs.getTimestamp(column_name));
}
else{
obj.put(column_name, rs.getObject(column_name));
}
}
json.put(obj);
}
return json;
}
}
- Gibt es einen schnelleren Weg?
- Gibt es einen Weg, der weniger Speicher benötigt?
java.sql.Types.BIGINT
ist 8 Bytes groß, also muss es mitrs.getLong()
notrs.getInt()
Antworten:
Der JIT-Compiler wird dies wahrscheinlich ziemlich schnell machen, da es sich nur um Verzweigungen und grundlegende Tests handelt. Sie könnten es wahrscheinlich eleganter machen, wenn Sie eine HashMap-Suche nach einem Rückruf durchführen, aber ich bezweifle, dass es schneller gehen würde. Das Gedächtnis ist so ziemlich schlank wie es ist.
Irgendwie bezweifle ich, dass dieser Code tatsächlich ein kritischer Flaschenhals für das Gedächtnis oder die Leistung ist. Haben Sie einen wirklichen Grund, es zu optimieren?
quelle
Ich denke, es gibt eine Möglichkeit, weniger Speicher zu verwenden (eine feste und nicht lineare Menge, abhängig von der Datenkardinalität), aber dies bedeutet, die Methodensignatur zu ändern. Tatsächlich können wir die Json-Daten direkt in einem Ausgabestream drucken, sobald wir sie aus dem ResultSet abrufen: Die bereits geschriebenen Daten werden durch Müll gesammelt, da wir kein Array benötigen, das sie im Speicher hält.
Ich verwende GSON, das Typadapter akzeptiert. Ich habe einen Typadapter geschrieben, um ResultSet in JsonArray zu konvertieren, und es sieht Ihrem Code sehr ähnlich. Ich warte auf die Version "Gson 2.1: Targeted Dec 31, 2011", die "Unterstützung für benutzerdefinierte Streaming-Typ-Adapter" bietet. Dann werde ich meinen Adapter so ändern, dass er ein Streaming-Adapter ist.
Aktualisieren
Wie versprochen bin ich zurück, aber nicht mit Gson, sondern mit Jackson 2. Tut mir leid, dass ich zu spät komme (von 2 Jahren).
Vorwort: Der Schlüssel, um weniger Speicher für das Ergebnis zu verwenden, befindet sich im "serverseitigen" Cursor. Mit dieser Art von Cursorn (auch als Ergebnismenge für Java-Entwickler bezeichnet) sendet das DBMS Daten inkrementell an den Client (auch als Treiber bezeichnet), während der Client mit dem Lesen fortfährt. Ich denke, Oracle-Cursor sind standardmäßig serverseitig. Suchen Sie für MySQL> 5.0.2 im Verbindungs-URL- Parameter nach useCursorFetch . Überprüfen Sie Ihr Lieblings-DBMS.
1: Um weniger Speicher zu verwenden, müssen wir:
JSONArray
) zu laden, sondern schreiben Sie jede Zeile direkt in eine Ausgabezeile , wobei ich für die Ausgabezeile einen Ausgabestream oder einen Writer oder auch einen JSON-Generator meine, der einen Ausgabestream oder einen Writer umschließt.2: Wie Jackson Documentation sagt:
3: Ich sehe dich in deinem Code, benutze getInt, getBoolean. getFloat ... von ResultSet ohne wasNull . Ich gehe davon aus, dass dies zu Problemen führen kann.
4: Ich habe Arrays verwendet, um Gedanken zwischenzuspeichern und zu vermeiden, bei jeder Iteration Getter aufzurufen. Obwohl ich kein Fan des Switch / Case-Konstrukts bin, habe ich es für dieses
int
SQL verwendetTypes
.Die Antwort: Noch nicht vollständig getestet, basiert es auf Jackson 2.2 :
Das
ResultSetSerializer
Objekt weist Jackson an, wie ein ResultSet serialisiert (das Objekt in JSON umgewandelt) wird. Es verwendet die Jackson Streaming API im Inneren. Hier der Code eines Tests:Und natürlich den Code der ResultSetSerializer-Klasse:
quelle
Zwei Dinge, die dies beschleunigen, sind:
Verschieben Sie Ihren Anruf
rsmd.getColumnCount()
aus der while-Schleife. Die Spaltenanzahl sollte nicht zwischen den Zeilen variieren.Für jeden Spaltentyp rufen Sie am Ende Folgendes auf:
Die Verwendung des Spaltenindex zum Abrufen des Spaltenwerts ist etwas schneller:
quelle
String column_name;
außerhalb der while-Schleife.Eine einfachere Lösung (basierend auf dem fraglichen Code):
quelle
Sie können jOOQ für den Job verwenden. Sie müssen nicht alle Funktionen von jOOQ verwenden, um einige nützliche JDBC-Erweiterungen nutzen zu können. In diesem Fall schreiben Sie einfach:
Relevante verwendete API-Methoden sind:
DSLContext.fetch(ResultSet)
um ein JDBC ResultSet in ein jOOQ Ergebnis zu konvertieren.Result.formatJSON()
um das jOOQ-Ergebnis in einen JSON-String zu formatieren.Die resultierende Formatierung sieht folgendermaßen aus:
Sie können auch ganz einfach Ihre eigene Formatierung erstellen
Result.map(RecordMapper)
Dies entspricht im Wesentlichen Ihrem Code, indem die Generierung von JSON-Objekten umgangen und direkt in a "gestreamt" wird
StringBuilder
. Ich würde jedoch sagen, dass der Leistungsaufwand in beiden Fällen vernachlässigbar sein sollte.(Haftungsausschluss: Ich arbeite für das Unternehmen hinter jOOQ)
quelle
"
zu\"
), um einen gültigen JSON - String zu erstellen. Ist das ein Fehler derformatJSON()
Funktion? Oder fehlt mir etwas?fetch(resultSet)
? Es ist nirgendwo definiert. Und wenn ich JDBCResultSet
vor dem Abrufen bekomme, wozu dient das dannDSL.using(connection)
? Warum braucht es eine Verbindung? :)ResultSet
, also denke ich, dass es keinen Zweifel gibtResultSet
. In der Tat sieht es nicht offensichtlich aus, warum dasconnection
hier benötigt wird. Wenn Sie jOOQ verwenden, steht Ihnen jedoch ohnehin einDSLContext
(ErgebnisDSL.using(connection)
oder ähnliches) Herumsitzen zur Verfügung.Zusätzlich zu den Vorschlägen von @Jim Cook. Ein anderer Gedanke ist, einen Schalter anstelle von if-elses zu verwenden:
quelle
Diese Antwort ist vielleicht nicht die effizienteste, aber sie ist sicher dynamisch. Durch die Kombination von nativem JDBC mit der Gson-Bibliothek von Google kann ich problemlos von einem SQL-Ergebnis in einen JSON-Stream konvertieren.
Ich habe den Konverter, eine Beispiel-DB-Eigenschaftendatei, eine SQL-Tabellengenerierung und eine Gradle-Build-Datei (mit verwendeten Abhängigkeiten) hinzugefügt.
QueryApp.java
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Ergebnisse
Grundlegende SELECT
Intermediate SELECT
quelle
Erste vorgenerierte Spaltennamen, zweite Verwendung
rs.getString(i)
anstelle vonrs.getString(column_name)
.Das Folgende ist eine Implementierung davon:
quelle
JSONObject json = resList.get(i);
Anschließend können Sie das JSON-Objekt bearbeitenjson
.Wenn jemand diese Implementierung verwenden möchte , sollten Sie dies und das überprüfen
Dies ist meine Version dieses Konvertierungscodes:
quelle
Genau wie ein Heads-Up ist die if / then-Schleife effizienter als der Schalter für Aufzählungen. Wenn Sie den Schalter gegen die rohe Enum-Ganzzahl haben, ist er effizienter, aber gegen die Variable, wenn / dann effizienter ist, zumindest für Java 5, 6 und 7.
Dh aus irgendeinem Grund (nach einigen Leistungstests)
ist schneller als
Ich sehe, dass ein paar Leute an mir zweifeln, also werde ich hier Code posten, damit Sie selbst den Unterschied sehen können, zusammen mit der Ausgabe, die ich von Java 7 habe. Die Ergebnisse des folgenden Codes mit 10 Aufzählungswerten sind wie folgt. Beachten Sie, dass der Schlüssel hier das Wenn / Dann ist, das einen ganzzahligen Wert verwendet, der mit Ordnungskonstanten der Aufzählung verglichen wird, im Vergleich zum Schalter mit dem Ordnungswert einer Aufzählung gegen die rohen Ordnungswerte im Vergleich zu einem Schalter mit der Aufzählung gegen jeden Aufzählungsnamen. Das if / then mit einem ganzzahligen Wert schlug beide anderen Schalter aus, obwohl der letzte Schalter etwas schneller als der erste Schalter war, war er nicht schneller als der if / else.
Wenn / sonst 23 ms
dauerte Switch dauerte 45 ms
Switch 2 dauerte 30 ms
Gesamtübereinstimmungen: 3000000
quelle
intern()
für Zeichenfolgen, die in den meisten modernen Java-Versionen größtenteils nicht mehr benötigt werden.Für alle, die sich für die if-else-Netzlösung entschieden haben, verwenden Sie bitte:
Denn bei Aliasen in Ihrer Abfrage sind der Spaltenname und die Spaltenbezeichnung zwei verschiedene Dinge. Zum Beispiel, wenn Sie ausführen:
Sie erhalten
Eher, als:
quelle
quelle
quelle
Umgekehrt habe ich hier ArrayList und Map verwendet, daher wird das json-Objekt nicht zeilenweise aufgerufen, sondern nach Abschluss der Iteration der Ergebnismenge:
quelle