将 ResultSet 转换为 JSON 的效率最高?

下面的代码使用 JSONArrayJSONObjectResultSet转换为 JSON 字符串。

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;
}
}
  • 有更快的方法吗?
  • 有没有一种方法可以减少内存的使用?
196925 次浏览

JIT 编译器可能会让这个过程变得非常快,因为它只是分支和基本测试。使用 HashMap 查找回调可能会使它更加优雅,但我怀疑它不会更快。至于记忆,这是非常薄弱的。

不知为何,我怀疑这段代码实际上是内存或性能的关键瓶颈。你有什么真正的理由去优化它吗?

有两件事可以让这个过程更快:

Move your call to rsmd.getColumnCount() out of the while loop. The column count should not vary across rows.

对于每个列类型,您最终都会调用如下内容:

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

使用列索引检索列值会稍微快一些:

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

我认为有一种方法可以使用更少的内存(根据数据基数的不同,内存是固定的,而不是线性的) ,但这意味着需要更改方法签名。实际上,只要我们从 ResultSet 中获取 Json 数据,我们就可以直接在输出流上打印它们: 已经写入的数据将被垃圾收集,因为我们不需要将它们保存在内存中的数组。

我使用接受类型适配器的 GSON。我编写了一个类型适配器来将 ResultSet 转换为 JsonArray,它看起来非常像您的代码。我正在等待“ Gson 2.1: Targeted Dec 31,2011”的发布,它将有“支持用户定义的流类型适配器”。然后,我将把适配器修改为流适配器。


更新

正如我承诺的那样,我回来了,但不是和格森一起,而是和杰克逊2。抱歉迟到了(两年)。

前言: 减少结果本身内存使用的关键在“服务器端”游标中。使用这种类型的游标(也称为 Java 开发人员的结果集) ,当客户机继续进行读取时,DBMS 会增量地向客户机(也称为驱动程序)发送数据。我认为 Oracle 光标默认是服务器端的。对于 MySQL > 5.0.2,在 连接 url 参数连接 url 参数中查找 useCursorFetch。查看您最喜欢的数据库管理系统。

为了减少内存使用,我们必须:

  • 在后台使用服务器端光标
  • 将结果集打开为 只读,当然还有 只能前进;
  • 避免加载列表(或 JSONArray)中的所有游标,但是将每一行直接写入 output line,其中输出行指的是包装输出流或写入器的输出流或写入器或 json 生成器。

正如杰克逊文档所说:

流式 API 性能最好(开销最低,读/写速度最快; other 2 methods build on it)

3: 我看到你在代码中使用 getInt,getBoolean。没有 是无效的的 ResultSet 的 getFloat..。我希望这会产生问题。

4: 我使用数组缓存思想,避免在每次迭代中调用 getter。虽然我不喜欢 switch/case 结构,但是我将它用于 int SQL Types

答案: 还没有完全测试,它是基于 Jackson 2.2的:

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

ResultSetSerializer对象指导 Jackson 如何序列化 ResultSet (将对象转换为 JSON)。它在内部使用 Jackson Streaming API。下面是测试代码:

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

当然,还有 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);
}
}
}

除了@Jim Cook 的建议之外,还有一个想法是使用开关而不是 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);
}

提醒一下,if/then 循环比枚举的开关更有效。如果对原始枚举整数有开关,那么它的效率更高,但对变量,If/then 的效率更高,至少对于 Java5、6和7来说是这样。

例如,出于某种原因(在一些性能测试之后)

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

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

I see that a few people are doubting me, so I'll post code here that you can run yourself to see the difference, along with output I have from Java 7. The results of the following code with 10 enum values are as follows. Note the key here is the if/then using an integer value comparing against ordinal constants of the enum, vs. the switch with an enum's ordinal value against the raw int ordinal values, vs. a switch with the enum against each enum name. The if/then with an integer value beat out both other switches, although the last switch was a little faster than the first switch, it was not faster than the if/else.

如果/否则需要23毫秒
转换花了45毫秒
开关2花了30毫秒
比赛总数: 3000000 < br >

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

如果有人计划使用这个实现,您可能需要 check this out 还有这个

这是我的转换代码:

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

对 JSON 导出使用第三方库

你可以使用 JooQ来完成这项工作。您不必使用 jOOQ 的所有特性来利用一些有用的 JDBC 扩展。在这种情况下,只需写下:

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

使用的相关 API 方法包括:

产生的格式如下:

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

您还可以通过 Result.map(RecordMapper)轻松地创建自己的格式

这实际上与您的代码一样,绕过了 JSON 对象的生成,直接“流”到 StringBuilder中。但是,在这两种情况下,性能开销都应该可以忽略不计。

(免责声明: 我为 jOOQ 背后的公司工作)

改用 SQL/JSON 特性

当然,您不必使用中间件将 JDBC 结果集映射到 JSON。这个问题没有提到需要使用哪种 SQL 方言,但是许多 SQL 方言支持标准的 SQL/JSON 语法,或者类似的语法,例如。

神使

SELECT json_arrayagg(json_object(*))
FROM t

SQL Server

SELECT *
FROM t
FOR JSON AUTO

PostgreSQL

SELECT to_jsonb(array_agg(t))
FROM t

另一方面,这里我使用了 ArrayList 和 Map,所以它不是逐行调用 json 对象,而是在结果集迭代完成之后调用:

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

首先预先生成列名,其次使用 rs.getString(i)而不是 rs.getString(column_name)

以下是这项工作的实施情况:

    /*
* 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;
}

这个答案可能不是最有效的,但肯定是动态的。将本地 JDBC 与 Google 的 Gson 库结合起来,我可以轻松地将 SQL 结果转换为 JSON 流。

我已经包含了转换器、示例 DB 属性文件、 SQL 表生成和一个 Gradle 构建文件(使用了依赖项)。

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

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

查询助手.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'
}

结果

基本选择

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

中级选择

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

A simpler solution (based on code in 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;
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;
}


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

For all who've opted for the if-else mesh solution, please use:

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

因为在查询中使用别名时,列名和列标签是两件不同的事情。例如,如果执行:

select col1, col2 as my_alias from table

You will get

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

而不是:

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