Le code suivant convertit a ResultSet
en chaîne JSON à l'aide de JSONArray
et 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;
}
}
- Y at-il un moyen plus rapide?
- Existe-t-il un moyen qui utilise moins de mémoire?
java.sql.Types.BIGINT
est de 8 octets, il doit donc être lu avecrs.getLong()
notrs.getInt()
Réponses:
Le compilateur JIT va probablement faire cela assez rapidement puisqu'il ne s'agit que de branches et de tests de base. Vous pourriez probablement le rendre plus élégant avec une recherche HashMap pour un rappel, mais je doute que ce soit plus rapide. En ce qui concerne la mémoire, c'est assez mince en l'état.
Je doute que ce code soit en fait un goulot critique pour la mémoire ou les performances. Avez-vous une vraie raison d'essayer de l'optimiser?
la source
Je pense qu'il existe un moyen d'utiliser moins de mémoire (une quantité fixe et non linéaire en fonction de la cardinalité des données) mais cela implique de changer la signature de la méthode. En fait, nous pouvons imprimer les données Json directement sur un flux de sortie dès que nous les récupérons du ResultSet: les données déjà écrites seront récupérées car nous n'avons pas besoin d'un tableau qui les garde en mémoire.
J'utilise GSON qui accepte les adaptateurs de type. J'ai écrit un adaptateur de type pour convertir ResultSet en JsonArray et il ressemble beaucoup à votre code. J'attends la version "Gson 2.1: Ciblé le 31 décembre 2011" qui aura le "Support des adaptateurs de type streaming définis par l'utilisateur". Ensuite, je modifierai mon adaptateur pour qu'il devienne un adaptateur de streaming.
Mettre à jour
Comme promis je suis de retour mais pas avec Gson, à la place avec Jackson 2. Désolé d'être en retard (de 2 ans).
Préface: La clé pour utiliser moins de mémoire du résultat itsef se trouve dans le curseur "côté serveur". Avec ce type de curseurs (c'est-à-dire jeu de résultats pour les développeurs Java), le SGBD envoie des données de manière incrémentielle au client (également appelé pilote) au fur et à mesure que le client avance avec la lecture. Je pense que le curseur Oracle est côté serveur par défaut. Pour MySQL> 5.0.2, recherchez useCursorFetch dans le paramètre d' url de connexion . Vérifiez votre SGBD préféré.
1: Donc , pour utiliser moins de mémoire , nous devons:
JSONArray
) mais écrivez chaque ligne directement sur une ligne de sortie , où pour ligne de sortie je veux dire un flux de sortie ou un écrivain ou aussi un générateur json qui encapsule un flux de sortie ou un écrivain.2: Comme le dit Jackson Documentation:
3: Je vous vois dans votre code, utilisez getInt, getBoolean. getFloat ... de ResultSet sans wasNull . Je pense que cela peut poser des problèmes.
4: J'ai utilisé des tableaux pour mettre en cache les pensées et pour éviter d'appeler des getters à chaque itération. Bien que n'étant pas fan de la construction switch / case, je l'ai utilisé pour ce
int
SQLTypes
.La réponse: pas encore entièrement testé, il est basé sur Jackson 2.2 :
L'
ResultSetSerializer
objet indique à Jackson comment sérialiser (transformer l'objet en JSON) un ResultSet. Il utilise l'API Jackson Streaming à l'intérieur. Voici le code d'un test:Et, bien sûr, le code de la classe ResultSetSerializer:
la source
Deux choses qui rendront cela plus rapide sont:
Déplacez votre appel
rsmd.getColumnCount()
hors de la boucle while. Le nombre de colonnes ne doit pas varier d'une ligne à l'autre.Pour chaque type de colonne, vous finissez par appeler quelque chose comme ceci:
Il sera légèrement plus rapide d'utiliser l'index de colonne pour récupérer la valeur de la colonne:
la source
String column_name;
hors de la boucle while.Une solution plus simple (basée sur le code en question):
la source
Vous pouvez utiliser jOOQ pour le travail. Vous n'avez pas besoin d'utiliser toutes les fonctionnalités de jOOQ pour profiter de certaines extensions JDBC utiles. Dans ce cas, écrivez simplement:
Les méthodes API pertinentes utilisées sont:
DSLContext.fetch(ResultSet)
pour convertir un ResultSet JDBC en un résultat jOOQ.Result.formatJSON()
pour formater le résultat jOOQ en une chaîne JSON.Le formatage résultant ressemblera à ceci:
Vous pouvez également créer votre propre formatage assez facilement, via
Result.map(RecordMapper)
Cela fait essentiellement la même chose que votre code, contournant la génération d'objets JSON, "streaming" directement dans un fichier
StringBuilder
. Je dirais que la surcharge de performance devrait être négligeable dans les deux cas, cependant.(Avertissement: je travaille pour l'entreprise derrière jOOQ)
la source
"
vers\"
) afin de créer une chaîne JSON valide. Est-ce un bug de laformatJSON()
fonction? Ou est-ce que je manque quelque chose?fetch(resultSet)
? Ce n'est défini nulle part. Et si j'obtiens JDBCResultSet
avant d'aller chercher quel est le butDSL.using(connection)
? Pourquoi a-t-il besoin d'une connexion? :)ResultSet
, donc je pense qu'il n'y a aucun douteResultSet
. En effet, il ne semble pas évident pourquoi leconnection
est nécessaire ici. Si vous utilisez jOOQ, vous aurez quand même unDSLContext
(le résultat deDSL.using(connection)
ou similaire) à votre disposition.En plus des suggestions faites par @Jim Cook. Une autre pensée est d'utiliser un commutateur au lieu de if-elses:
la source
Cette réponse n'est peut-être pas la plus efficace, mais elle est certainement dynamique. En associant JDBC natif à la bibliothèque Gson de Google, je peux facilement convertir un résultat SQL en un flux JSON.
J'ai inclus le convertisseur, un exemple de fichier de propriétés DB, la génération de table SQL et un fichier de construction Gradle (avec les dépendances utilisées).
QueryApp.java
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Résultats
SELECT de base
Intermédiaire SELECT
la source
Premièrement, pré-générer les noms de colonne, deuxième utilisation à la
rs.getString(i)
place ders.getString(column_name)
.Ce qui suit est une implémentation de ceci:
la source
JSONObject json = resList.get(i);
Ensuite, vous êtes libre de manipuler l'objet JSONjson
.Si quelqu'un envisage d'utiliser cette mise en œuvre, vous voudrez peut-être vérifier ceci et ceci
Voici ma version de ce code de conversion:
la source
Juste comme un avertissement, la boucle if / then est plus efficace que le commutateur pour les enums. Si vous avez le commutateur contre l'entier enum brut, alors c'est plus efficace, mais contre la variable, si / alors est plus efficace, au moins pour Java 5, 6 et 7.
Ie, pour une raison quelconque (après quelques tests de performance)
est plus rapide que
Je vois que quelques personnes doutent de moi, donc je posterai ici le code que vous pouvez exécuter vous-même pour voir la différence, ainsi que la sortie que j'ai de Java 7. Les résultats du code suivant avec 10 valeurs d'énumération sont les suivants. Notez que la clé ici est le if / then en utilisant une valeur entière comparée aux constantes ordinales de l'énumération, contre le commutateur avec la valeur ordinale d'une énumération par rapport aux valeurs ordinales int brutes, contre un commutateur avec l'énumération contre chaque nom d'énumération. Le if / then avec une valeur entière a battu les deux autres commutateurs, même si le dernier commutateur était un peu plus rapide que le premier commutateur, il n'était pas plus rapide que le if / else.
Si / sinon a pris 23 ms Le
commutateur a pris 45 ms Le
commutateur 2 a pris 30 ms
Nombre total de matchs: 3000000
la source
intern()
pour les chaînes qui ne sont en grande partie plus nécessaires dans la plupart des versions modernes de Java.Pour tous ceux qui ont opté pour la solution de maillage if-else, veuillez utiliser:
Parce qu'en cas d'alias dans votre requête, le nom de la colonne et le libellé de la colonne sont deux choses différentes. Par exemple si vous exécutez:
Tu auras
Plutôt que:
la source
la source
la source
dans l'autre sens, j'ai utilisé ici ArrayList et Map, donc il n'appelle pas l'objet json ligne par ligne mais après l'itération du jeu de résultats terminée:
la source