La conversion la plus efficace de ResultSet en JSON?

109

Le code suivant convertit a ResultSeten chaîne JSON à l'aide de JSONArrayet 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?
Devin Dixon
la source
1
Un code de
Leif Gruenwoldt
1
l'implémentation de java.sql.Types.ARRAY ne fonctionnait pas pour moi en utilisant postgresql (le tableau a été mis en tant que chaîne en utilisant "{...}". A fini par changer la ligne "obj.put (nom_colonne, rs.getArray (nom_colonne) ); "to" Array array = rs.getArray (column_name); if (array! = null) obj.put (column_name, new JSONArray (array.getArray ())); "
moléculaire
Si les performances sont un problème majeur, vous ne devriez pas utiliser cette API JSON, mais plutôt utiliser une bibliothèque de streaming qui écrit simplement JSON sans avoir besoin de créer des objets mémoire de toutes les données (où vous pouvez regarder en arrière / trouver des éléments dans l'arborescence). Cela dit, je m'assurerais que vous avez réellement un problème de performance avant de faire cela.
Sebastiaan van den Broek
3
Il y a une erreur dans votre extrait. java.sql.Types.BIGINTest de 8 octets, il doit donc être lu avec rs.getLong()notrs.getInt()
polarfish
3
Merci d'avoir chargé ceci. Vous venez de m'économiser des heures de travail.
DRich

Réponses:

23

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?

Andrew White
la source
Je mets le code source dans un framework open-source, donc je ne sais pas à quoi il servira. J'essaie donc de le rendre aussi efficace que possible.
Devin Dixon
1
@DevinDixon: le framework est-il disponible? Y a-t-il quelque chose comme le code de votre question déjà disponible dans un dépôt open source quelque part?
34

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:

  • utiliser le curseur côté serveur derrière la scène
  • utilisez le jeu de résultats ouvert en lecture seule et, bien sûr, en avant uniquement ;
  • évitez de charger tout le curseur dans une liste (ou a 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:

L'API de streaming est la plus performante (surcharge la plus faible, lecture / écriture la plus rapide; 2 autres méthodes s'appuient sur elle)

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 intSQL Types.

La réponse: pas encore entièrement testé, il est basé sur Jackson 2.2 :

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.2.2</version>
</dependency>

L' ResultSetSerializerobjet 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:

SimpleModule module = new SimpleModule();
module.addSerializer(new ResultSetSerializer());

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);

[ . . . do the query . . . ]
ResultSet resultset = statement.executeQuery(query);

// Use the DataBind Api here
ObjectNode objectNode = objectMapper.createObjectNode();

// put the resultset in a containing structure
objectNode.putPOJO("results", resultset);

// generate all
objectMapper.writeValue(stringWriter, objectNode);

Et, bien sûr, le code de la classe ResultSetSerializer:

public class ResultSetSerializer extends JsonSerializer<ResultSet> {

    public static class ResultSetSerializerException extends JsonProcessingException{
        private static final long serialVersionUID = -914957626413580734L;

        public ResultSetSerializerException(Throwable cause){
            super(cause);
        }
    }

    @Override
    public Class<ResultSet> handledType() {
        return ResultSet.class;
    }

    @Override
    public void serialize(ResultSet rs, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {

        try {
            ResultSetMetaData rsmd = rs.getMetaData();
            int numColumns = rsmd.getColumnCount();
            String[] columnNames = new String[numColumns];
            int[] columnTypes = new int[numColumns];

            for (int i = 0; i < columnNames.length; i++) {
                columnNames[i] = rsmd.getColumnLabel(i + 1);
                columnTypes[i] = rsmd.getColumnType(i + 1);
            }

            jgen.writeStartArray();

            while (rs.next()) {

                boolean b;
                long l;
                double d;

                jgen.writeStartObject();

                for (int i = 0; i < columnNames.length; i++) {

                    jgen.writeFieldName(columnNames[i]);
                    switch (columnTypes[i]) {

                    case Types.INTEGER:
                        l = rs.getInt(i + 1);
                        if (rs.wasNull()) {
                            jgen.writeNull();
                        } else {
                            jgen.writeNumber(l);
                        }
                        break;

                    case Types.BIGINT:
                        l = rs.getLong(i + 1);
                        if (rs.wasNull()) {
                            jgen.writeNull();
                        } else {
                            jgen.writeNumber(l);
                        }
                        break;

                    case Types.DECIMAL:
                    case Types.NUMERIC:
                        jgen.writeNumber(rs.getBigDecimal(i + 1));
                        break;

                    case Types.FLOAT:
                    case Types.REAL:
                    case Types.DOUBLE:
                        d = rs.getDouble(i + 1);
                        if (rs.wasNull()) {
                            jgen.writeNull();
                        } else {
                            jgen.writeNumber(d);
                        }
                        break;

                    case Types.NVARCHAR:
                    case Types.VARCHAR:
                    case Types.LONGNVARCHAR:
                    case Types.LONGVARCHAR:
                        jgen.writeString(rs.getString(i + 1));
                        break;

                    case Types.BOOLEAN:
                    case Types.BIT:
                        b = rs.getBoolean(i + 1);
                        if (rs.wasNull()) {
                            jgen.writeNull();
                        } else {
                            jgen.writeBoolean(b);
                        }
                        break;

                    case Types.BINARY:
                    case Types.VARBINARY:
                    case Types.LONGVARBINARY:
                        jgen.writeBinary(rs.getBytes(i + 1));
                        break;

                    case Types.TINYINT:
                    case Types.SMALLINT:
                        l = rs.getShort(i + 1);
                        if (rs.wasNull()) {
                            jgen.writeNull();
                        } else {
                            jgen.writeNumber(l);
                        }
                        break;

                    case Types.DATE:
                        provider.defaultSerializeDateValue(rs.getDate(i + 1), jgen);
                        break;

                    case Types.TIMESTAMP:
                        provider.defaultSerializeDateValue(rs.getTime(i + 1), jgen);
                        break;

                    case Types.BLOB:
                        Blob blob = rs.getBlob(i);
                        provider.defaultSerializeValue(blob.getBinaryStream(), jgen);
                        blob.free();
                        break;

                    case Types.CLOB:
                        Clob clob = rs.getClob(i);
                        provider.defaultSerializeValue(clob.getCharacterStream(), jgen);
                        clob.free();
                        break;

                    case Types.ARRAY:
                        throw new RuntimeException("ResultSetSerializer not yet implemented for SQL type ARRAY");

                    case Types.STRUCT:
                        throw new RuntimeException("ResultSetSerializer not yet implemented for SQL type STRUCT");

                    case Types.DISTINCT:
                        throw new RuntimeException("ResultSetSerializer not yet implemented for SQL type DISTINCT");

                    case Types.REF:
                        throw new RuntimeException("ResultSetSerializer not yet implemented for SQL type REF");

                    case Types.JAVA_OBJECT:
                    default:
                        provider.defaultSerializeValue(rs.getObject(i + 1), jgen);
                        break;
                    }
                }

                jgen.writeEndObject();
            }

            jgen.writeEndArray();

        } catch (SQLException e) {
            throw new ResultSetSerializerException(e);
        }
    }
}
Plap
la source
27

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:

obj.put(column_name, rs.getInt(column_name));

Il sera légèrement plus rapide d'utiliser l'index de colonne pour récupérer la valeur de la colonne:

obj.put(column_name, rs.getInt(i));
Oravecz
la source
Définissez également String column_name;hors de la boucle while.
Charney Kaye
22

Une solution plus simple (basée sur le code en question):

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; i++) {
    String column_name = rsmd.getColumnName(i);
    obj.put(column_name, rs.getObject(column_name));
  }
  json.put(obj);
}
return json;
Elhanan Mishraky
la source
3
sympa, mais il y a un bug avec DATETIME et TIMESTAMP (il n'ajoute pas Apostrophes
OhadR
nice and simple
Anoop LL
10

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:

String json = DSL.using(connection).fetch(resultSet).formatJSON();

Les méthodes API pertinentes utilisées sont:

Le formatage résultant ressemblera à ceci:

{"fields":[{"name":"field-1","type":"type-1"},
           {"name":"field-2","type":"type-2"},
           ...,
           {"name":"field-n","type":"type-n"}],
 "records":[[value-1-1,value-1-2,...,value-1-n],
            [value-2-1,value-2-2,...,value-2-n]]}

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)

Lukas Eder
la source
C'est génial mais j'ai des problèmes pour analyser la chaîne résultante. Lorsque certaines des valeurs contiennent un guillemet, l'analyseur ne peut pas fonctionner correctement: je pense que les guillemets à l'intérieur des valeurs doivent être échappés ( "vers \") afin de créer une chaîne JSON valide. Est-ce un bug de la formatJSON()fonction? Ou est-ce que je manque quelque chose?
Oneiros
@Oneiros: jOOQ devrait correctement échapper à ces guillemets ... Il vaut mieux poser une nouvelle question (avec des détails) ou signaler un bug: github.com/jOOQ/jOOQ/issues/new
Lukas Eder
Dans votre exemple, dans quoi resultSet est-il utilisé fetch(resultSet)? Ce n'est défini nulle part. Et si j'obtiens JDBC ResultSetavant d'aller chercher quel est le but DSL.using(connection)? Pourquoi a-t-il besoin d'une connexion? :)
Nikola Lošić
1
@ NikolaLošić: Eh bien, la question porte sur l'utilisation d'un JDBC ResultSet, donc je pense qu'il n'y a aucun doute ResultSet. En effet, il ne semble pas évident pourquoi le connectionest nécessaire ici. Si vous utilisez jOOQ, vous aurez quand même un DSLContext(le résultat de DSL.using(connection)ou similaire) à votre disposition.
Lukas Eder
Quelle est la bonne bibliothèque OJOQ à inclure? J'en essaie beaucoup, mais chacun me donne une erreur de classe non trouvée avec référence DSL. Thnks
Lorenzo Barbagli
7

En plus des suggestions faites par @Jim Cook. Une autre pensée est d'utiliser un commutateur au lieu de if-elses:

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);

    switch( rsmd.getColumnType( i ) ) {
      case java.sql.Types.ARRAY:
        obj.put(column_name, rs.getArray(column_name));     break;
      case java.sql.Types.BIGINT:
        obj.put(column_name, rs.getInt(column_name));       break;
      case java.sql.Types.BOOLEAN:
        obj.put(column_name, rs.getBoolean(column_name));   break;
      case java.sql.Types.BLOB:
        obj.put(column_name, rs.getBlob(column_name));      break;
      case java.sql.Types.DOUBLE:
        obj.put(column_name, rs.getDouble(column_name));    break;
      case java.sql.Types.FLOAT:
        obj.put(column_name, rs.getFloat(column_name));     break;
      case java.sql.Types.INTEGER:
        obj.put(column_name, rs.getInt(column_name));       break;
      case java.sql.Types.NVARCHAR:
        obj.put(column_name, rs.getNString(column_name));   break;
      case java.sql.Types.VARCHAR:
        obj.put(column_name, rs.getString(column_name));    break;
      case java.sql.Types.TINYINT:
        obj.put(column_name, rs.getInt(column_name));       break;
      case java.sql.Types.SMALLINT:
        obj.put(column_name, rs.getInt(column_name));       break;
      case java.sql.Types.DATE:
        obj.put(column_name, rs.getDate(column_name));      break;
      case java.sql.Types.TIMESTAMP:
        obj.put(column_name, rs.getTimestamp(column_name)); break;
      default:
        obj.put(column_name, rs.getObject(column_name));    break;
    }
  }

  json.put(obj);
}
Vanderwyst
la source
4
La boucle vers l'arrière (comparaison de l'index zéro) est également plus rapide (que la comparaison de l'index à une expression).
Dave Jarvis
4

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

import java.io.PrintWriter;

import com.oracle.jdbc.ResultSetConverter;

public class QueryApp {
    public static void main(String[] args) {
        PrintWriter writer = new PrintWriter(System.out);
        String dbProps = "/database.properties";
        String indent = "    ";

        writer.println("Basic SELECT:");
        ResultSetConverter.queryToJson(writer, dbProps, "SELECT * FROM Beatles", indent, false);

        writer.println("\n\nIntermediate SELECT:");
        ResultSetConverter.queryToJson(writer, dbProps, "SELECT first_name, last_name, getAge(date_of_birth) as age FROM Beatles", indent, true);
    }
}

ResultSetConverter.java

package com.oracle.jdbc;

import java.io.*;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.*;

import com.google.common.reflect.TypeToken;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonWriter;

public class ResultSetConverter {
    public static final Type RESULT_TYPE = new TypeToken<List<Map<String, Object>>>() {
        private static final long serialVersionUID = -3467016635635320150L;
    }.getType();

    public static void queryToJson(Writer writer, String connectionProperties, String query, String indent, boolean closeWriter) {
        Connection conn = null;
        Statement stmt = null;
        GsonBuilder gson = new GsonBuilder();
        JsonWriter jsonWriter = new JsonWriter(writer);

        if (indent != null) jsonWriter.setIndent(indent);

        try {
            Properties props = readConnectionInfo(connectionProperties);
            Class.forName(props.getProperty("driver"));

            conn = openConnection(props);
            stmt = conn.createStatement();

            gson.create().toJson(QueryHelper.select(stmt, query), RESULT_TYPE, jsonWriter);

            if (closeWriter) jsonWriter.close();

            stmt.close();
            conn.close();
        } catch (SQLException se) {
            se.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (stmt != null) stmt.close();
            } catch (SQLException se2) {
            }
            try {
                if (conn != null) conn.close();
            } catch (SQLException se) {
                se.printStackTrace();
            }
            try {
                if (closeWriter && jsonWriter != null) jsonWriter.close();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }

    private static Properties readConnectionInfo(String resource) throws IOException {
        Properties properties = new Properties();
        InputStream in = ResultSetConverter.class.getResourceAsStream(resource);
        properties.load(in);
        in.close();

        return properties;
    }

    private static Connection openConnection(Properties connectionProperties) throws IOException, SQLException {
        String database = connectionProperties.getProperty("database");
        String username = connectionProperties.getProperty("username");
        String password = connectionProperties.getProperty("password");

        return DriverManager.getConnection(database, username, password);
    }
}

QueryHelper.java

package com.oracle.jdbc;

import java.sql.*;
import java.text.*;
import java.util.*;

import com.google.common.base.CaseFormat;

public class QueryHelper {
    static DateFormat DATE_FORMAT = new SimpleDateFormat("YYYY-MM-dd");

    public static List<Map<String, Object>> select(Statement stmt, String query) throws SQLException {
        ResultSet resultSet = stmt.executeQuery(query);
        List<Map<String, Object>> records = mapRecords(resultSet);

        resultSet.close();

        return records;
    }

    public static List<Map<String, Object>> mapRecords(ResultSet resultSet) throws SQLException {
        List<Map<String, Object>> records = new ArrayList<Map<String, Object>>();
        ResultSetMetaData metaData = resultSet.getMetaData();

        while (resultSet.next()) {
            records.add(mapRecord(resultSet, metaData));
        }

        return records;
    }

    public static Map<String, Object> mapRecord(ResultSet resultSet, ResultSetMetaData metaData) throws SQLException {
        Map<String, Object> record = new HashMap<String, Object>();

        for (int c = 1; c <= metaData.getColumnCount(); c++) {
            String columnType = metaData.getColumnTypeName(c);
            String columnName = formatPropertyName(metaData.getColumnName(c));
            Object value = resultSet.getObject(c);

            if (columnType.equals("DATE")) {
                value = DATE_FORMAT.format(value);
            }

            record.put(columnName, value);
        }

        return record;
    }

    private static String formatPropertyName(String property) {
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, property);
    }
}

database.properties

driver=com.mysql.jdbc.Driver
database=jdbc:mysql://localhost/JDBC_Tutorial
username=root
password=

JDBC_Tutorial.sql

-- phpMyAdmin SQL Dump
-- version 4.5.1
-- http://www.phpmyadmin.net
--
-- Host: 127.0.0.1
-- Generation Time: Jan 12, 2016 at 07:40 PM
-- Server version: 10.1.8-MariaDB
-- PHP Version: 5.6.14

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Database: `jdbc_tutorial`
--
CREATE DATABASE IF NOT EXISTS `jdbc_tutorial` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
USE `jdbc_tutorial`;

DELIMITER $$
--
-- Functions
--
DROP FUNCTION IF EXISTS `getAge`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `getAge` (`in_dob` DATE) RETURNS INT(11) NO SQL
BEGIN
DECLARE l_age INT;
   IF DATE_FORMAT(NOW(),'00-%m-%d') >= DATE_FORMAT(in_dob,'00-%m-%d') THEN
      -- This person has had a birthday this year
      SET l_age=DATE_FORMAT(NOW(),'%Y')-DATE_FORMAT(in_dob,'%Y');
   ELSE
      -- Yet to have a birthday this year
      SET l_age=DATE_FORMAT(NOW(),'%Y')-DATE_FORMAT(in_dob,'%Y')-1;
   END IF;
      RETURN(l_age);
END$$

DELIMITER ;

-- --------------------------------------------------------

--
-- Table structure for table `beatles`
--

DROP TABLE IF EXISTS `beatles`;
CREATE TABLE IF NOT EXISTS `beatles` (
  `id` int(11) NOT NULL,
  `first_name` varchar(255) DEFAULT NULL,
  `last_name` varchar(255) DEFAULT NULL,
  `date_of_birth` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Truncate table before insert `beatles`
--

TRUNCATE TABLE `beatles`;
--
-- Dumping data for table `beatles`
--

INSERT INTO `beatles` (`id`, `first_name`, `last_name`, `date_of_birth`) VALUES(100, 'John', 'Lennon', '1940-10-09');
INSERT INTO `beatles` (`id`, `first_name`, `last_name`, `date_of_birth`) VALUES(101, 'Paul', 'McCartney', '1942-06-18');
INSERT INTO `beatles` (`id`, `first_name`, `last_name`, `date_of_birth`) VALUES(102, 'George', 'Harrison', '1943-02-25');
INSERT INTO `beatles` (`id`, `first_name`, `last_name`, `date_of_birth`) VALUES(103, 'Ringo', 'Starr', '1940-07-07');

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

build.gradle

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'

mainClassName = 'com.oracle.jdbc.QueryApp'

repositories {
    maven  {
        url "http://repo1.maven.org/maven2"
    }
}

jar {
    baseName = 'jdbc-tutorial'
    version =  '1.0.0'
}

sourceCompatibility = 1.7
targetCompatibility = 1.7

dependencies {
    compile 'mysql:mysql-connector-java:5.1.16'
    compile 'com.google.guava:guava:18.0'
    compile 'com.google.code.gson:gson:1.7.2'
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.9'
}

Résultats

SELECT de base

[
    {
        "firstName": "John",
        "lastName": "Lennon",
        "dateOfBirth": "1940-10-09",
        "id": 100
    },
    {
        "firstName": "Paul",
        "lastName": "McCartney",
        "dateOfBirth": "1942-06-18",
        "id": 101
    },
    {
        "firstName": "George",
        "lastName": "Harrison",
        "dateOfBirth": "1943-02-25",
        "id": 102
    },
    {
        "firstName": "Ringo",
        "lastName": "Starr",
        "dateOfBirth": "1940-07-07",
        "id": 103
    }
]

Intermédiaire SELECT

[
    {
        "firstName": "John",
        "lastName": "Lennon",
        "age": 75
    },
    {
        "firstName": "Paul",
        "lastName": "McCartney",
        "age": 73
    },
    {
        "firstName": "George",
        "lastName": "Harrison",
        "age": 72
    },
    {
        "firstName": "Ringo",
        "lastName": "Starr",
        "age": 75
    }
]
M. Polywhirl
la source
3

Premièrement, pré-générer les noms de colonne, deuxième utilisation à la rs.getString(i)place de rs.getString(column_name).

Ce qui suit est une implémentation de ceci:

    /*
     * Convert ResultSet to a common JSON Object array
     * Result is like: [{"ID":"1","NAME":"Tom","AGE":"24"}, {"ID":"2","NAME":"Bob","AGE":"26"}, ...]
     */
    public static List<JSONObject> getFormattedResult(ResultSet rs) {
        List<JSONObject> resList = new ArrayList<JSONObject>();
        try {
            // get column names
            ResultSetMetaData rsMeta = rs.getMetaData();
            int columnCnt = rsMeta.getColumnCount();
            List<String> columnNames = new ArrayList<String>();
            for(int i=1;i<=columnCnt;i++) {
                columnNames.add(rsMeta.getColumnName(i).toUpperCase());
            }

            while(rs.next()) { // convert each object to an human readable JSON object
                JSONObject obj = new JSONObject();
                for(int i=1;i<=columnCnt;i++) {
                    String key = columnNames.get(i - 1);
                    String value = rs.getString(i);
                    obj.put(key, value);
                }
                resList.add(obj);
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return resList;
    }
coderz
la source
comment puis-je accéder et utiliser l'objet .... disons que je veux imprimer toute la colonne Nom ... et l'objet est-il facilement disponible pour les opérations CRUD ??
Prathamesh dhanawade
@Prathameshdhanawade Cette méthode consiste à convertir JDBC ResultSet en un tableau JSON. Les développeurs n'utilisent généralement pas le ResultSet JDBC brut, ils le convertissent généralement en une liste d'objets Java, c'est-à-dire un objet JSON. Vous voyez que la valeur de retour est un tableau d'objets JSON. Vous accédez facilement à un objet via JSONObject json = resList.get(i);Ensuite, vous êtes libre de manipuler l'objet JSON json.
coderz
En fait, j'aurai besoin de l'objet pour préparer une vue graphique. Je suis donc curieux de pouvoir manipuler l'objet. Thnx.
Prathamesh dhanawade
Pouvez-vous décrire "vue graphique"? Ou donné un exemple?
coderz
J'essaie d'afficher la valeur de la colonne comme suit: JSONObject obj = jsonList.get (1); System.out.println (obj.getString ("nom") + "\ t" + obj.getString ("société")); mais obtenant une erreur comme "org.json.JSONException: JSONObject [" nom "] introuvable."
Prathamesh dhanawade
2

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:

public class ResultSetConverter {
public static JSONArray convert(ResultSet rs) throws SQLException,
        JSONException {
    JSONArray json = new JSONArray();
    ResultSetMetaData rsmd = rs.getMetaData();
    int numColumns = rsmd.getColumnCount();
    while (rs.next()) {

        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.getLong(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.REAL) {
                obj.put(column_name, rs.getFloat(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.getDouble(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.CHAR) {
                obj.put(column_name, rs.getString(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.NCHAR) {
                obj.put(column_name, rs.getNString(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.LONGNVARCHAR) {
                obj.put(column_name, rs.getNString(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.LONGVARCHAR) {
                obj.put(column_name, rs.getString(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.TINYINT) {
                obj.put(column_name, rs.getByte(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.SMALLINT) {
                obj.put(column_name, rs.getShort(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.TIME) {
                obj.put(column_name, rs.getTime(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.TIMESTAMP) {
                obj.put(column_name, rs.getTimestamp(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.BINARY) {
                obj.put(column_name, rs.getBytes(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.VARBINARY) {
                obj.put(column_name, rs.getBytes(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.LONGVARBINARY) {
                obj.put(column_name, rs.getBinaryStream(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.BIT) {
                obj.put(column_name, rs.getBoolean(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.CLOB) {
                obj.put(column_name, rs.getClob(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.NUMERIC) {
                obj.put(column_name, rs.getBigDecimal(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.DECIMAL) {
                obj.put(column_name, rs.getBigDecimal(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.DATALINK) {
                obj.put(column_name, rs.getURL(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.REF) {
                obj.put(column_name, rs.getRef(column_name));
            } else if (rsmd.getColumnType(i) == java.sql.Types.STRUCT) {
                obj.put(column_name, rs.getObject(column_name)); // must be a custom mapping consists of a class that implements the interface SQLData and an entry in a java.util.Map object.
            } else if (rsmd.getColumnType(i) == java.sql.Types.DISTINCT) {
                obj.put(column_name, rs.getObject(column_name)); // must be a custom mapping consists of a class that implements the interface SQLData and an entry in a java.util.Map object.
            } else if (rsmd.getColumnType(i) == java.sql.Types.JAVA_OBJECT) {
                obj.put(column_name, rs.getObject(column_name));
            } else {
                obj.put(column_name, rs.getString(i));
            }
        }

        json.put(obj);
    }

    return json;
}
}
Li3ro
la source
2

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)

if (ordinalValue == 1) {
   ...
} else (ordinalValue == 2 {
   ... 
}

est plus rapide que

switch( myEnum.ordinal() ) {
    case 1:
       ...
       break;
    case 2:
       ...
       break;
}

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

package testing;

import java.util.Random;

enum TestEnum {
    FIRST,
    SECOND,
    THIRD,
    FOURTH,
    FIFTH,
    SIXTH,
    SEVENTH,
    EIGHTH,
    NINTH,
    TENTH
}

public class SwitchTest {
    private static int LOOP = 1000000;
    private static Random r = new Random();
    private static int SIZE = TestEnum.values().length;

    public static void main(String[] args) {
        long time = System.currentTimeMillis();
        int matches = 0;
        for (int i = 0; i < LOOP; i++) {
            int j = r.nextInt(SIZE);
            if (j == TestEnum.FIRST.ordinal()) {
                matches++;
            } else if (j == TestEnum.SECOND.ordinal()) {
                matches++;
            } else if (j == TestEnum.THIRD.ordinal()) {
                matches++;
            } else if (j == TestEnum.FOURTH.ordinal()) {
                matches++;
            } else if (j == TestEnum.FIFTH.ordinal()) {
                matches++;
            } else if (j == TestEnum.SIXTH.ordinal()) {
                matches++;
            } else if (j == TestEnum.SEVENTH.ordinal()) {
                matches++;
            } else if (j == TestEnum.EIGHTH.ordinal()) {
                matches++;
            } else if (j == TestEnum.NINTH.ordinal()) {
                matches++;
            } else {
                matches++;
            }
        }
        System.out.println("If / else took "+(System.currentTimeMillis() - time)+" ms");
        time = System.currentTimeMillis();
        for (int i = 0; i < LOOP; i++) {
            TestEnum te = TestEnum.values()[r.nextInt(SIZE)];
            switch (te.ordinal()) {
                case 0:
                    matches++;
                    break;
                case 1:
                    matches++;
                    break;
                case 2:
                    matches++;
                    break;
                case 3:
                    matches++;
                    break;
                case 4:
                    matches++;
                    break;
                case 5:
                    matches++;
                    break;
                case 6:
                    matches++;
                    break;
                case 7:
                    matches++;
                    break;
                case 8:
                    matches++;
                    break;
                case 9:
                    matches++;
                    break;
                default:
                    matches++;
                    break;
            }
        }
        System.out.println("Switch took "+(System.currentTimeMillis() - time)+" ms");
        time = System.currentTimeMillis();
        for (int i = 0; i < LOOP; i++) {
            TestEnum te = TestEnum.values()[r.nextInt(SIZE)];
            switch (te) {
                case FIRST:
                    matches++;
                    break;
                case SECOND:
                    matches++;
                    break;
                case THIRD:
                    matches++;
                    break;
                case FOURTH:
                    matches++;
                    break;
                case FIFTH:
                    matches++;
                    break;
                case SIXTH:
                    matches++;
                    break;
                case SEVENTH:
                    matches++;
                    break;
                case EIGHTH:
                    matches++;
                    break;
                case NINTH:
                    matches++;
                    break;
                default:
                    matches++;
                    break;
            }
        }
        System.out.println("Switch 2 took "+(System.currentTimeMillis() - time)+" ms");     
        System.out.println("Total matches: "+matches);
    }
}
Marcus
la source
Bon, ok mais ... la vérité pratique est que la différence entre ces alternatives de codage pour tout cas d'utilisation réel est si petite que, dans une application réelle, les différences de performances entre elles seraient très probablement infiniment petites. Corrigez d'abord votre code, puis (alors seulement) accélérez-le (s'il doit être plus rapide).
scottb
D'accord, mais la question principale était celle du moyen le plus rapide et le plus efficace. Il existe de nombreux styles qui rendent le code difficile à lire ou à maintenir, mais qui sont plus efficaces, même s'ils sont généralement résolus par les optimisations du compilateur à un moment donné dans le futur. Un exemple de ceci est l'utilisation de intern()pour les chaînes qui ne sont en grande partie plus nécessaires dans la plupart des versions modernes de Java.
Marcus
D'accord, mais les blocs if-then-else et les boîtiers de commutation sont eux-mêmes de mauvaises solutions aux problèmes de maintenabilité, de robustesse et de lisibilité dans tous les cas. Pour les énumérations que vous possédez (ou dont vous pouvez modifier le code), la meilleure pratique consiste très souvent à utiliser des méthodes spécifiques aux constantes dans un type enum riche, la plupart du temps, l'activation d'une énumération doit être considérée avec circonspection car il y a mieux solutions là-bas.
scottb
1

Pour tous ceux qui ont opté pour la solution de maillage if-else, veuillez utiliser:

String columnName = metadata.getColumnName(
String displayName = metadata.getColumnLabel(i);
switch (metadata.getColumnType(i)) {
case Types.ARRAY:
    obj.put(displayName, resultSet.getArray(columnName));
    break;
...

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:

select col1, col2 as my_alias from table

Tu auras

[
    { "col1": 1, "col2": 2 }, 
    { "col1": 1, "col2": 2 }
]

Plutôt que:

[
    { "col1": 1, "my_alias": 2 }, 
    { "col1": 1, "my_alias": 2 }
]
seekme_94
la source
0
package com.idal.cib;

import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class DBJsonConverter {

    static ArrayList<String> data = new ArrayList<String>();
    static Connection conn = null;
    static PreparedStatement ps = null;
    static ResultSet rs = null;
    static String path = "";
    static String driver="";
    static String url="";
    static String username="";
    static String password="";
    static String query="";

    @SuppressWarnings({ "unchecked" })
    public static void dataLoad(String path) {
        JSONObject obj1 = new JSONObject();
        JSONArray jsonArray = new JSONArray();
        conn = DatabaseConnector.getDbConnection(driver, url, username,
                password);
        try {
            ps = conn.prepareStatement(query);
            rs = ps.executeQuery();
            ArrayList<String> columnNames = new ArrayList<String>();
            if (rs != null) {
                ResultSetMetaData columns = rs.getMetaData();
                int i = 0;
                while (i < columns.getColumnCount()) {
                    i++;
                    columnNames.add(columns.getColumnName(i));
                }
                while (rs.next()) {
                    JSONObject obj = new JSONObject();
                    for (i = 0; i < columnNames.size(); i++) {
                        data.add(rs.getString(columnNames.get(i)));
                        {
                            for (int j = 0; j < data.size(); j++) {
                                if (data.get(j) != null) {
                                    obj.put(columnNames.get(i), data.get(j));
                                }else {
                                    obj.put(columnNames.get(i), "");
                                }
                            }
                        }
                    }

                    jsonArray.add(obj);
                    obj1.put("header", jsonArray);
                    FileWriter file = new FileWriter(path);
                    file.write(obj1.toJSONString());
                    file.flush();
                    file.close();
                }
                ps.close();
            } else {
                JSONObject obj2 = new JSONObject();
                obj2.put(null, null);
                jsonArray.add(obj2);
                obj1.put("header", jsonArray);
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                    rs.close();
                    ps.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    @SuppressWarnings("static-access")
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        driver = "oracle.jdbc.driver.OracleDriver";
        url = "jdbc:oracle:thin:@localhost:1521:database";
        username = "user";
        password = "password";
        path = "path of file";
        query = "select * from temp_employee";

        DatabaseConnector dc = new DatabaseConnector();
        dc.getDbConnection(driver,url,username,password);
        DBJsonConverter formatter = new DBJsonConverter();
        formatter.dataLoad(path);

    }

}




package com.idal.cib;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnector {

    static Connection conn1 = null;

    public static Connection getDbConnection(String driver, String url,
            String username, String password) {
        // TODO Auto-generated constructor stub
        try {

            Class.forName(driver);

            conn1 = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return conn1;
    }

}
Prashant Chilwant
la source
Veuillez ajouter quelques explications à votre réponse.
Sahil Mittal
0
public static JSONArray GetJSONDataFromResultSet(ResultSet rs) throws SQLException {
    ResultSetMetaData metaData = rs.getMetaData();
    int count = metaData.getColumnCount();
    String[] columnName = new String[count];
    JSONArray jsonArray = new JSONArray();
    while(rs.next()) {
        JSONObject jsonObject = new JSONObject();
        for (int i = 1; i <= count; i++){
               columnName[i-1] = metaData.getColumnLabel(i);
               jsonObject.put(columnName[i-1], rs.getObject(i));
        }
        jsonArray.put(jsonObject);
    }
    return jsonArray;
}
hitesh bariya
la source
-1

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:

 List<Map<String, String>> list = new ArrayList<Map<String, String>>();

  ResultSetMetaData rsMetaData = rs.getMetaData();  


      while(rs.next()){

              Map map = new HashMap();
              for (int i = 1; i <= rsMetaData.getColumnCount(); i++) {
                 String key = rsMetaData.getColumnName(i);

                  String value = null;

               if (rsmd.getColumnType(i) == java.sql.Types.VARCHAR) {
                           value = rs.getString(key);
               } else if(rsmd.getColumnType(i)==java.sql.Types.BIGINT)                         
                             value = rs.getLong(key);
               }                  


                    map.put(key, value);
              }
              list.add(map);


    }


     json.put(list);    
reil bhudaya
la source