El siguiente código convierte ResultSet
a en una cadena JSON usando JSONArray
y 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;
}
}
- ¿Hay una manera mas rápida?
- ¿Hay alguna forma que use menos memoria?
java.sql.Types.BIGINT
tiene un tamaño de 8 bytes, por lo que debe leerse conrs.getLong()
notrs.getInt()
Respuestas:
El compilador JIT probablemente lo hará bastante rápido, ya que solo se trata de ramas y pruebas básicas. Probablemente podría hacerlo más elegante con una búsqueda HashMap para una devolución de llamada, pero dudo que sea más rápido. En cuanto a la memoria, es bastante escasa.
De alguna manera dudo que este código sea en realidad un cuello de botella crítico para la memoria o el rendimiento. ¿Tiene alguna razón real para intentar optimizarlo?
fuente
Creo que hay una forma de usar menos memoria (una cantidad fija y no lineal dependiendo de la cardinalidad de los datos) pero esto implica cambiar la firma del método. De hecho, podemos imprimir los datos Json directamente en un flujo de salida tan pronto como los recuperemos del ResultSet: los datos ya escritos serán recolectados como basura ya que no necesitamos una matriz que los mantenga en la memoria.
Yo uso GSON que acepta adaptadores de tipo. Escribí un adaptador de tipo para convertir ResultSet a JsonArray y se parece mucho a su código. Estoy esperando la versión "Gson 2.1: Dirigida al 31 de diciembre de 2011", que tendrá el "Soporte para adaptadores de tipo de transmisión definidos por el usuario". Luego modificaré mi adaptador para que sea un adaptador de transmisión.
Actualizar
Como prometí, estoy de regreso pero no con Gson, sino con Jackson 2. Siento llegar tarde (de 2 años).
Prefacio: La clave para utilizar menos memoria del resultado es en el cursor del "lado del servidor". Con este tipo de cursores (también conocido como conjunto de resultados para los desarrolladores de Java), el DBMS envía datos de forma incremental al cliente (también conocido como controlador) a medida que el cliente avanza con la lectura. Creo que el cursor de Oracle está en el lado del servidor de forma predeterminada. Para MySQL> 5.0.2 busque useCursorFetch en el parámetro de URL de conexión . Consulte su DBMS favorito.
1: Entonces, para usar menos memoria debemos:
JSONArray
) pero escriba cada fila directamente en una línea de salida , donde por línea de salida me refiero a un flujo de salida o un escritor o también un generador json que envuelve un flujo de salida o un escritor.2: Como dice Jackson Documentation:
3: Veo que en su código usa getInt, getBoolean. getFloat ... de ResultSet sin wasNull . Espero que esto pueda generar problemas.
4: Utilicé matrices para almacenar en caché los pensamientos y evitar llamar a los captadores en cada iteración. Aunque no soy un fanático de la construcción switch / case, lo usé para ese
int
SQLTypes
.La respuesta: aún no se ha probado completamente, se basa en Jackson 2.2 :
El
ResultSetSerializer
objeto le indica a Jackson cómo serializar (transformar el objeto a JSON) un ResultSet. Utiliza la API de transmisión de Jackson en su interior. Aquí el código de una prueba:Y, por supuesto, el código de la clase ResultSetSerializer:
fuente
Dos cosas que harán esto más rápido son:
Mueva su llamada a
rsmd.getColumnCount()
fuera del ciclo while. El recuento de columnas no debe variar entre filas.Para cada tipo de columna, terminas llamando a algo como esto:
Será un poco más rápido usar el índice de la columna para recuperar el valor de la columna:
fuente
String column_name;
fuera del ciclo while.Una solución más simple (basada en el código en cuestión):
fuente
Podrías usar jOOQ para el trabajo. No es necesario que utilice todas las funciones de jOOQ para aprovechar algunas extensiones JDBC útiles. En este caso, simplemente escriba:
Los métodos de API relevantes utilizados son:
DSLContext.fetch(ResultSet)
para convertir un ResultSet JDBC en un resultado jOOQ.Result.formatJSON()
para formatear el resultado jOOQ en una cadena JSON.El formato resultante se verá así:
También puede crear su propio formato con bastante facilidad, a través de
Result.map(RecordMapper)
Esto esencialmente hace lo mismo que su código, evitando la generación de objetos JSON, "transmitiendo" directamente a un
StringBuilder
. Sin embargo, diría que la sobrecarga de rendimiento debería ser insignificante en ambos casos.(Descargo de responsabilidad: trabajo para la empresa detrás de jOOQ)
fuente
"
a\"
) para crear una cadena JSON válida. ¿Es esto un error de laformatJSON()
función? ¿O me estoy perdiendo algo?fetch(resultSet)
? No está definido en ninguna parte. Y si obtengo JDBCResultSet
antes de buscar, ¿cuál es el propósitoDSL.using(connection)
? ¿Por qué necesita conexión? :)ResultSet
, así que creo que no hay dudaResultSet
. De hecho, no parece obvio por quéconnection
se necesita aquí. Sin embargo, si está usando jOOQ, tendrá unDSLContext
(resultado deDSL.using(connection)
o similar) disponible para usted de todos modos.Además de las sugerencias hechas por @Jim Cook. Otro pensamiento es usar un interruptor en lugar de if-elses:
fuente
Esta respuesta puede no ser la más eficiente, pero seguro que es dinámica. Al emparejar JDBC nativo con la biblioteca Gson de Google, puedo convertir fácilmente de un resultado SQL a un flujo JSON.
He incluido el convertidor, el archivo de propiedades de la base de datos de ejemplo, la generación de tablas SQL y un archivo de compilación Gradle (con las dependencias utilizadas).
QueryApp.java
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Resultados
SELECCIONAR BÁSICO
SELECCIONAR INTERMEDIO
fuente
Primero pregenere nombres de columna, segundo uso en
rs.getString(i)
lugar ders.getString(column_name)
.La siguiente es una implementación de esto:
fuente
JSONObject json = resList.get(i);
Entonces puede manipular el objeto JSONjson
.Si alguien planea usar esta implementación, es posible que desee ver esto y esto
Esta es mi versión de ese código de conversión:
fuente
Solo como un aviso, el bucle if / then es más eficiente que el cambio de enumeraciones. Si tiene el cambio contra el entero enum sin formato, entonces es más eficiente, pero contra la variable, si / entonces es más eficiente, al menos para Java 5, 6 y 7.
Es decir, por alguna razón (después de algunas pruebas de rendimiento)
es más rápido que
Veo que algunas personas dudan de mí, así que publicaré aquí un código que puede ejecutar usted mismo para ver la diferencia, junto con la salida que tengo de Java 7. Los resultados del siguiente código con 10 valores de enumeración son los siguientes. Tenga en cuenta que la clave aquí es el if / then usando un valor entero que se compara con las constantes ordinales de la enumeración, frente al interruptor con el valor ordinal de una enumeración frente a los valores ordinales int sin procesar, frente a un interruptor con la enumeración frente a cada nombre de enumeración. El if / then con un valor entero superó a los otros dos conmutadores, aunque el último cambio fue un poco más rápido que el primero, no fue más rápido que el if / else.
Si / de lo contrario tomó 23 ms El
interruptor tomó 45 ms El
interruptor 2 tardó 30 ms
Total de coincidencias: 3000000
fuente
intern()
cadenas de for que ya no son necesarias en la mayoría de las versiones modernas de Java.Para todos los que han optado por la solución de malla if-else, utilice:
Porque en el caso de los alias en su consulta, el nombre de la columna y la etiqueta de la columna son dos cosas diferentes. Por ejemplo, si ejecuta:
Conseguirás
Más bien que:
fuente
fuente
fuente
de otra manera, aquí he usado ArrayList y Map, por lo que no se llama al objeto json fila por fila, sino después de que finaliza la iteración del conjunto de resultados:
fuente