O código a seguir converte a ResultSet
em uma string JSON usando JSONArray
e 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?
java.sql.Types.BIGINT
tem 8 bytes de tamanho, então deve ser lido comrs.getLong()
notrs.getInt()
Respostas:
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?
fonte
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:
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:
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
int
SQLTypes
.A resposta: ainda não totalmente testado, é baseado no Jackson 2.2 :
O
ResultSetSerializer
objeto 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:E, é claro, o código da classe ResultSetSerializer:
fonte
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:
Será um pouco mais rápido usar o índice da coluna para recuperar o valor da coluna:
fonte
String column_name;
fora do loop while.Uma solução mais simples (com base no código em questão):
fonte
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:
Os métodos de API relevantes usados são:
DSLContext.fetch(ResultSet)
para converter um ResultSet JDBC em um Resultado jOOQ.Result.formatJSON()
para formatar o Resultado jOOQ em uma String JSON.A formatação resultante será semelhante a esta:
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)
fonte
"
para\"
) para criar uma string JSON válida. Isso é um bug daformatJSON()
função? Ou eu estou esquecendo de alguma coisa?fetch(resultSet)
? Não está definido em lugar nenhum. E se eu obtiver o JDBCResultSet
antes de buscar, qual é o objetivoDSL.using(connection)
? Por que ele precisa de conexão? :)ResultSet
, então acho que não há dúvidas sobreResultSet
. Na verdade, não parece óbvio por que oconnection
é necessário aqui. Se você estiver usando o jOOQ, terá umDSLContext
(o resultadoDSL.using(connection)
ou semelhante) disponível para você de qualquer maneira.Além das sugestões feitas por @Jim Cook. Outro pensamento é usar um switch em vez de if-elses:
fonte
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
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Resultados
SELECT básico
SELEÇÃO intermediária
fonte
Primeira geração de nomes de coluna, segundo uso em
rs.getString(i)
vez ders.getString(column_name)
.O seguinte é uma implementação disso:
fonte
JSONObject json = resList.get(i);
Então, você está livre para manipular o objeto JSONjson
.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:
fonte
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)
é mais rápido que
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
fonte
intern()
strings que não são mais necessárias na maioria das versões modernas de Java.Para todos que optaram pela solução de malha if-else, use:
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:
Você vai ter
Ao invés de:
fonte
fonte
fonte
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:
fonte