Conversão mais eficiente de ResultSet para JSON?

109

O código a seguir converte a ResultSetem uma string JSON usando JSONArraye 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;
  }
}
  • Existe uma maneira mais rápida?
  • Existe uma maneira que usa menos memória?
Devin Dixon
fonte
1
Uma implementação diferente code.google.com/p/bonex-homerunning/source/browse/trunk/…
Leif Gruenwoldt
1
implementação para java.sql.Types.ARRAY não funcionou para mim usando postgresql (array foi colocado como String usando "{...}". Acabou mudando a linha "obj.put (column_name, rs.getArray (column_name) ); "para" Array array = rs.getArray (column_name); if (array! = null) obj.put (column_name, new JSONArray (array.getArray ())); "
molecular
Se o desempenho for um problema importante, você não deve usar esta API JSON, mas sim uma biblioteca de streaming que apenas grava JSON sem a necessidade de criar objetos de memória de todos os dados (onde você pode olhar para trás / encontrar coisas na árvore). Dito isso, eu verificaria se você realmente tem um problema de desempenho antes de fazer isso.
Sebastiaan van den Broek,
3
Há um erro no seu snippet. java.sql.Types.BIGINTtem 8 bytes de tamanho, então deve ser lido com rs.getLong()notrs.getInt()
polarfish
3
Obrigado por carregar isso. Você acabou de me poupar horas de trabalho.
DRich

Respostas:

23

O JIT Compiler provavelmente tornará isso bem rápido, pois são apenas ramificações e testes básicos. Você provavelmente poderia torná-lo mais elegante com uma consulta HashMap para um retorno de chamada, mas duvido que seja mais rápido. Quanto à memória, já é muito pequena.

De alguma forma, eu duvido que esse código seja realmente um gargalo crítico para memória ou desempenho. Você tem algum motivo real para tentar otimizá-lo?

Andrew White
fonte
Estou colocando o código-fonte em uma estrutura de código aberto, então não sei para que será usado. Portanto, estou tentando torná-lo o mais eficiente possível.
Devin Dixon
1
@DevinDixon: o framework está disponível? Existe algo como o código em sua pergunta já disponível em um repositório de código aberto em algum lugar?
34

Acho que há uma maneira de usar menos memória (uma quantidade fixa e não linear dependendo da cardinalidade dos dados), mas isso implica em alterar a assinatura do método. Na verdade, podemos imprimir os dados Json diretamente em um fluxo de saída assim que os buscarmos no ResultSet: os dados já gravados serão coletados como lixo, pois não precisamos de um array que os mantenha na memória.

Eu uso GSON que aceita adaptadores de tipo. Eu escrevi um adaptador de tipo para converter ResultSet em JsonArray e se parece muito com o seu código. Estou esperando o lançamento do "Gson 2.1: Targeted Dec 31, 2011", que terá o "Suporte para adaptadores de tipo de streaming definidos pelo usuário". Então, modificarei meu adaptador para ser um adaptador de streaming.


Atualizar

Como prometido, estou de volta, mas não com Gson, ao invés com Jackson 2. Desculpe o atraso (de 2 anos).

Prefácio: A chave para usar menos memória do próprio resultado está no cursor "do lado do servidor". Com este tipo de cursores (também conhecido como conjunto de resultados para desenvolvedores Java), o SGBD envia dados de forma incremental para o cliente (também conhecido como driver) conforme o cliente avança com a leitura. Acho que o cursor Oracle está do lado do servidor por padrão. Para MySQL> 5.0.2, procure useCursorFetch no parâmetro de URL de conexão . Verifique sobre seu DBMS favorito.

1: Portanto, para usar menos memória, devemos:

  • use o cursor do lado do servidor atrás da cena
  • use o conjunto de resultados aberto como somente leitura e, é claro, somente encaminhamento ;
  • evite carregar todo o cursor em uma lista (ou a JSONArray), mas escreva cada linha diretamente em uma linha de saída , onde para linha de saída quero dizer um fluxo de saída ou um gravador ou também um gerador json que envolve um fluxo de saída ou um gravador.

2: Como diz a documentação de Jackson:

A API de streaming tem o melhor desempenho (menor sobrecarga, leitura / gravação mais rápida; outros 2 métodos baseados nela)

3: Vejo você em seu código, use getInt, getBoolean. getFloat ... de ResultSet sem wasNull . Espero que isso possa gerar problemas.

4: Usei arrays para pensar em cache e evitar chamar getters a cada iteração. Embora não seja um fã da construção switch / case, usei-o para esse intSQL Types.

A resposta: ainda não totalmente testado, é baseado no Jackson 2.2 :

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

O ResultSetSerializerobjeto instrui Jackson sobre como serializar (transformar o objeto em JSON) um ResultSet. Ele usa a API Jackson Streaming interna. Aqui está o código de um teste:

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

E, é claro, o código da 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
fonte
27

Duas coisas que tornarão isso mais rápido são:

Mova sua chamada para rsmd.getColumnCount()fora do loop while. A contagem de colunas não deve variar entre as linhas.

Para cada tipo de coluna, você acaba chamando algo assim:

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

Será um pouco mais rápido usar o índice da coluna para recuperar o valor da coluna:

obj.put(column_name, rs.getInt(i));
oravecz
fonte
Defina também String column_name;fora do loop while.
Charney Kaye de
22

Uma solução mais simples (com base no código em questão):

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
fonte
3
bom, mas há um bug com DATETIME e TIMESTAMP (não adiciona apóstrofos
OhadR
agradável e simples
Anoop LL
10

Você pode usar o jOOQ para o trabalho. Você não precisa usar todos os recursos do jOOQ para tirar proveito de algumas extensões JDBC úteis. Nesse caso, basta escrever:

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

Os métodos de API relevantes usados ​​são:

A formatação resultante será semelhante a esta:

{"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]]}

Você também pode criar sua própria formatação com bastante facilidade, por meio Result.map(RecordMapper)

Isso essencialmente faz o mesmo que seu código, evitando a geração de objetos JSON, "transmitindo" diretamente para um StringBuilder. Eu diria que a sobrecarga de desempenho deve ser insignificante em ambos os casos.

(Isenção de responsabilidade: eu trabalho para a empresa por trás do jOOQ)

Lukas Eder
fonte
Isso é incrível, mas estou tendo problemas para analisar a string resultante. Quando alguns dos valores contêm aspas, o analisador não pode funcionar corretamente: acho que as aspas dentro dos valores devem ter escape ( "para \") para criar uma string JSON válida. Isso é um bug da formatJSON()função? Ou eu estou esquecendo de alguma coisa?
Oneiros
@Oneiros: jOOQ deve escapar corretamente dessas aspas ... Melhor fazer uma nova pergunta (com detalhes) ou relatar um bug: github.com/jOOQ/jOOQ/issues/new
Lukas Eder
Em seu exemplo, em que resultSet é usado fetch(resultSet)? Não está definido em lugar nenhum. E se eu obtiver o JDBC ResultSetantes de buscar, qual é o objetivo DSL.using(connection)? Por que ele precisa de conexão? :)
Nikola Lošić
1
@ NikolaLošić: Bem, a questão é sobre como usar um JDBC ResultSet, então acho que não há dúvidas sobre ResultSet. Na verdade, não parece óbvio por que o connectioné necessário aqui. Se você estiver usando o jOOQ, terá um DSLContext(o resultado DSL.using(connection)ou semelhante) disponível para você de qualquer maneira.
Lukas Eder
qual é a biblioteca JOOQ correta a ser incluída? Estou tentando muitos, mas todos me dão erro de classe não encontrada com referência de DSL. Thnks
Lorenzo Barbagli
7

Além das sugestões feitas por @Jim Cook. Outro pensamento é usar um switch em vez 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
fonte
4
O loop para trás (comparando o índice zero) também é mais rápido (do que comparar o índice com uma expressão).
Dave Jarvis
4

Essa resposta pode não ser a mais eficiente, mas com certeza é dinâmica. Emparelhando o JDBC nativo com a biblioteca Gson do Google, posso facilmente converter de um resultado SQL em um fluxo JSON.

Incluí o conversor, arquivo de propriedades de banco de dados de exemplo, geração de tabela SQL e um arquivo de compilação do Gradle (com dependências usadas).

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'
}

Resultados

SELECT básico

[
    {
        "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
    }
]

SELEÇÃO intermediária

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

Primeira geração de nomes de coluna, segundo uso em rs.getString(i)vez de rs.getString(column_name).

O seguinte é uma implementação disso:

    /*
     * 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
fonte
como faço para acessar e usar o objeto ... digamos, eu quero imprimir toda a coluna Name ... e o objeto está facilmente disponível para operações CRUD ??
Prathamesh dhanawade
@Prathameshdhanawade Este método é converter JDBC ResultSet em um array JSON. Os desenvolvedores geralmente não usam JDBC ResultSet bruto, eles geralmente o convertem em uma lista de objetos Java, ou seja, objeto JSON. Você vê que o valor de retorno é uma matriz de objetos JSON. Você pode acessar facilmente um objeto por meio do JSONObject json = resList.get(i);Então, você está livre para manipular o objeto JSON json.
coderz
Na verdade vou precisar do objeto para preparar uma visualização gráfica. Então, eu só estava curioso em poder manipular o objeto. Thnx.
Prathamesh dhanawade
Você poderia descrever "visualização gráfica"? Ou deu um exemplo?
coderz
Estou tentando exibir o valor da coluna como: JSONObject obj = jsonList.get (1); System.out.println (obj.getString ("nome") + "\ t" + obj.getString ("empresa")); mas obtendo um erro como "org.json.JSONException: JSONObject [" nome "] não encontrado."
Prathamesh dhanawade
2

Se alguém planeja usar esta implementação, você pode querer verificar isto e este

Esta é minha versão desse código de conversão:

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
fonte
2

Apenas como um alerta, o loop if / then é mais eficiente do que a troca de enums. Se você tiver a opção em relação ao inteiro enum bruto, então é mais eficiente, mas em relação à variável, se / então é mais eficiente, pelo menos para Java 5, 6 e 7.

Ou seja, por algum motivo (após alguns testes de desempenho)

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

é mais rápido que

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

Vejo que algumas pessoas estão duvidando de mim, então postarei aqui um código que você mesmo pode executar para ver a diferença, junto com a saída que tenho do Java 7. Os resultados do código a seguir com 10 valores de enum são os seguintes. Observe que a chave aqui é if / then usando um valor inteiro comparando com as constantes ordinais do enum, vs. a opção com o valor ordinal de um enum com os valores ordinais internos brutos, vs. uma opção com o enum com cada nome de enum. O if / then com um valor inteiro superou os outros switches, embora o último switch fosse um pouco mais rápido do que o primeiro, não era mais rápido do que o if / else.

If / else levou 23 ms
Switch levou 45 ms
Switch 2 levou 30 ms
Total de correspondências: 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
fonte
Bem, ok, mas ... a verdade prática é que a diferença entre essas alternativas de codificação para qualquer caso de uso real é tão pequena que, em um aplicativo real, as diferenças de desempenho entre elas provavelmente seriam incomensuravelmente pequenas. Faça seu código correto primeiro e então (só então) torne-o rápido (se precisar ser mais rápido).
scottb
Concordo, mas a questão principal era em relação à maneira mais rápida e eficiente. Existem muitos estilos que tornam o código difícil de ler ou manter, mas são mais eficientes, embora normalmente sejam resolvidos por otimizações do compilador em algum momento no futuro. Um exemplo disso é o uso de intern()strings que não são mais necessárias na maioria das versões modernas de Java.
Marcus
Concordo, mas os blocos if-then-else e switch-case são soluções ruins para problemas de manutenção, robustez e legibilidade em qualquer caso. Para enums que você possui (ou cujo código você pode modificar), a prática recomendada é muitas vezes usar métodos específicos de constantes em um tipo de enum rico. Na maioria das vezes, ativar um enum deve ser visto com cautela porque há melhores soluções lá fora.
scottb de
1

Para todos que optaram pela solução de malha if-else, use:

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

Porque no caso de apelidos em sua consulta, o nome da coluna e o rótulo da coluna são duas coisas diferentes. Por exemplo, se você executar:

select col1, col2 as my_alias from table

Você vai ter

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

Ao invés de:

[
    { "col1": 1, "my_alias": 2 }, 
    { "col1": 1, "my_alias": 2 }
]
seeme_94
fonte
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
fonte
Por favor, acrescente alguma explicação à sua resposta.
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
fonte
-1

da outra forma, aqui eu usei ArrayList e Map, então não é chamado de objeto json linha por linha, mas depois que a iteração do conjunto de resultados terminou:

 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
fonte