Kode berikut mengubah a ResultSet
menjadi string JSON menggunakan JSONArray
dan 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;
}
}
- Apakah ada cara yang lebih cepat?
- Apakah ada cara yang menggunakan lebih sedikit memori?
java.sql.Types.BIGINT
berukuran 8 byte, jadi harus dibaca denganrs.getLong()
notrs.getInt()
Jawaban:
Compiler JIT mungkin akan membuat ini cukup cepat karena ini hanya percobaan cabang dan dasar. Anda mungkin bisa membuatnya lebih elegan dengan pencarian HashMap ke panggilan balik tetapi saya ragu itu akan lebih cepat. Mengenai memori, ini cukup ramping.
Entah bagaimana saya meragukan kode ini sebenarnya adalah leher botol penting untuk memori atau kinerja. Apakah Anda punya alasan kuat untuk mencoba mengoptimalkannya?
sumber
Saya pikir ada cara untuk menggunakan lebih sedikit memori (jumlah tetap dan tidak linier tergantung pada kardinalitas data) tetapi ini menyiratkan untuk mengubah tanda tangan metode. Sebenarnya kita dapat mencetak data Json secara langsung pada arus keluaran segera setelah kita mengambilnya dari ResultSet: data yang sudah ditulis akan dikumpulkan sampahnya karena kita tidak memerlukan array yang menyimpannya di memori.
Saya menggunakan GSON yang menerima adaptor tipe. Saya menulis adaptor tipe untuk mengubah ResultSet ke JsonArray dan itu terlihat sangat mirip dengan kode Anda. Saya sedang menunggu rilis "Gson 2.1: Targeted Dec 31, 2011" yang akan memiliki "Dukungan untuk adaptor jenis streaming yang ditentukan pengguna". Kemudian saya akan memodifikasi adaptor saya menjadi adaptor streaming.
Memperbarui
Seperti yang dijanjikan aku kembali tapi tidak dengan Gson, tapi dengan Jackson 2. Maaf terlambat (2 tahun).
Pendahuluan: Kunci untuk menggunakan lebih sedikit memori dari hasil itsef ada di kursor "sisi server". Dengan jenis kursor ini (alias resultet ke Java devs) DBMS mengirimkan data secara bertahap ke klien (alias driver) saat klien melanjutkan pembacaan. Saya pikir kursor Oracle adalah sisi server secara default. Untuk MySQL> 5.0.2, cari useCursorFetch di parameter url koneksi . Periksa DBMS favorit Anda.
1: Jadi untuk menggunakan lebih sedikit memori kita harus:
JSONArray
) tetapi tulis setiap baris langsung pada baris keluaran , di mana untuk baris keluaran yang saya maksud adalah arus keluaran atau penulis atau juga generator json yang membungkus aliran keluaran atau penulis.2: Seperti yang dikatakan Dokumentasi Jackson:
3: Saya melihat Anda dalam kode Anda, gunakan getInt, getBoolean. getFloat ... dari ResultSet tanpa wasNull . Saya berharap ini bisa menimbulkan masalah.
4: Saya menggunakan array untuk membuat cache berpikir dan menghindari panggilan getter setiap iterasi. Meskipun bukan penggemar konstruksi switch / case, saya menggunakannya untuk
int
SQL ituTypes
.Jawabannya: Belum sepenuhnya teruji, ini berdasarkan Jackson 2.2 :
The
ResultSetSerializer
objek menginstruksikan Jackson tentang bagaimana cerita bersambung (transform objek untuk JSON) sebuah ResultSet. Ia menggunakan Jackson Streaming API di dalamnya. Berikut kode ujiannya:Dan, tentu saja, kode kelas ResultSetSerializer:
sumber
Dua hal yang akan membuatnya lebih cepat adalah:
Pindahkan panggilan Anda ke
rsmd.getColumnCount()
luar loop sementara. Jumlah kolom tidak boleh bervariasi di seluruh baris.Untuk setiap jenis kolom, Anda akhirnya memanggil sesuatu seperti ini:
Akan sedikit lebih cepat menggunakan indeks kolom untuk mengambil nilai kolom:
sumber
String column_name;
dari loop sementara.Solusi yang lebih sederhana (berdasarkan kode yang dimaksud):
sumber
Anda bisa menggunakan jOOQ untuk pekerjaan itu. Anda tidak harus menggunakan semua fitur jOOQ untuk memanfaatkan beberapa ekstensi JDBC yang berguna. Dalam kasus ini, cukup tulis:
Metode API relevan yang digunakan adalah:
DSLContext.fetch(ResultSet)
untuk mengubah JDBC ResultSet menjadi Hasil jOOQ.Result.formatJSON()
untuk memformat Hasil jOOQ menjadi String JSON.Format yang dihasilkan akan terlihat seperti ini:
Anda juga dapat membuat pemformatan Anda sendiri dengan lebih mudah, melalui
Result.map(RecordMapper)
Ini pada dasarnya melakukan hal yang sama seperti kode Anda, menghindari pembuatan objek JSON, "streaming" langsung ke file
StringBuilder
. Saya akan mengatakan bahwa overhead kinerja harus dapat diabaikan dalam kedua kasus.(Penafian: Saya bekerja untuk perusahaan di belakang jOOQ)
sumber
"
ke\"
) untuk membuat string JSON yang valid. Apakah ini bugformatJSON()
fungsi? Atau apakah saya melewatkan sesuatu?fetch(resultSet)
? Itu tidak ditentukan di mana pun. Dan jika saya mendapatkan JDBCResultSet
sebelum mengambil, apa tujuannyaDSL.using(connection)
? Mengapa itu membutuhkan koneksi? :)ResultSet
, jadi saya pikir tidak ada keraguan tentangResultSet
. Memang, tidak terlihat jelas mengapa halconnection
itu dibutuhkan di sini. Jika Anda menggunakan jOOQ, Anda akan memilikiDSLContext
(hasil dariDSL.using(connection)
atau serupa) tetap tersedia untuk Anda.Selain saran yang dibuat oleh @Jim Cook. Satu pemikiran lain adalah menggunakan sakelar alih-alih if-elses:
sumber
Jawaban ini mungkin bukan yang paling efisien, tetapi pasti dinamis. Memasangkan JDBC asli dengan pustaka Google Gson, saya dapat dengan mudah mengonversi dari hasil SQL ke aliran JSON.
Saya telah menyertakan konverter, contoh file properti DB, pembuatan tabel SQL, dan file build Gradle (dengan dependensi yang digunakan).
QueryApp.java
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Hasil
PILIH Dasar
PILIH Menengah
sumber
Nama kolom praproduksi pertama, penggunaan kedua,
rs.getString(i)
bukanrs.getString(column_name)
.Berikut ini adalah implementasi dari ini:
sumber
JSONObject json = resList.get(i);
Kemudian Anda bebas memanipulasi objek JSONjson
.Jika ada yang berencana untuk menggunakan penerapan ini, Anda mungkin ingin memeriksanya dan ini
Ini adalah versi saya dari kode konversi itu:
sumber
Sama seperti head up, loop if / then lebih efisien daripada sakelar untuk enum. Jika Anda memiliki sakelar terhadap integer enum mentah, maka itu lebih efisien, tetapi terhadap variabel, if / then lebih efisien, setidaknya untuk Java 5, 6, dan 7.
Yaitu, untuk beberapa alasan (setelah beberapa tes kinerja)
lebih cepat dari
Saya melihat bahwa beberapa orang meragukan saya, jadi saya akan memposting kode di sini yang dapat Anda jalankan sendiri untuk melihat perbedaannya, beserta output yang saya miliki dari Java 7. Hasil kode berikut dengan 10 nilai enum adalah sebagai berikut. Perhatikan kuncinya di sini adalah jika / lalu menggunakan nilai integer yang dibandingkan dengan konstanta ordinal enum, vs. sakelar dengan nilai ordinal enum terhadap nilai ordinal int mentah, vs. sakelar dengan enum terhadap setiap nama enum. Jika / maka dengan nilai integer mengalahkan kedua sakelar lainnya, meskipun sakelar terakhir sedikit lebih cepat daripada sakelar pertama, itu tidak lebih cepat daripada sakelar if / else.
If / else butuh 23 ms
Switch butuh 45 ms
Switch 2 butuh 30 ms
Total kecocokan: 3000000
sumber
intern()
string yang sebagian besar tidak lagi diperlukan di sebagian besar versi Java modern.Untuk semua yang telah memilih solusi mesh if-else, silakan gunakan:
Karena dalam kasus alias dalam kueri Anda, nama kolom dan label kolom adalah dua hal yang berbeda. Misalnya jika Anda menjalankan:
Kamu akan mendapatkan
Daripada:
sumber
sumber
sumber
sebaliknya, di sini saya telah menggunakan ArrayList dan Map, jadi tidak memanggil objek json baris demi baris tetapi setelah iterasi resultet selesai:
sumber