Poniższy kod konwertuje ResultSet
ciąg na ciąg JSON przy użyciu JSONArray
i 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;
}
}
- Czy jest szybszy sposób?
- Czy istnieje sposób, który zużywa mniej pamięci?
java.sql.Types.BIGINT
ma rozmiar 8 bajtów, więc należy go odczytać za pomocąrs.getLong()
notrs.getInt()
Odpowiedzi:
Kompilator JIT prawdopodobnie zrobi to całkiem szybko, ponieważ to tylko gałęzie i podstawowe testy. Prawdopodobnie można by było uczynić go bardziej eleganckim za pomocą odnośnika HashMap do wywołania zwrotnego, ale wątpię, czy byłby szybszy. Jeśli chodzi o pamięć, to i tak jest dość wąska.
Z jakiegoś powodu wątpię, czy ten kod jest w rzeczywistości kluczowym elementem pamięci lub wydajności. Czy masz jakiś prawdziwy powód, aby spróbować go zoptymalizować?
źródło
Myślę, że istnieje sposób na użycie mniejszej ilości pamięci (stała i nieliniowa ilość w zależności od liczności danych), ale to oznacza zmianę sygnatury metody. W rzeczywistości możemy wydrukować dane Json bezpośrednio w strumieniu wyjściowym, gdy tylko pobierzemy je z zestawu wyników: już zapisane dane zostaną usunięte, ponieważ nie potrzebujemy tablicy, która przechowuje je w pamięci.
Używam GSON, który akceptuje adaptery typu. Napisałem adapter typu do konwersji ResultSet na JsonArray i wygląda bardzo podobnie do twojego kodu. Czekam na wydanie „Gson 2.1: Docelowe 31 grudnia 2011”, które będzie miało „Obsługę adapterów typu strumieniowego zdefiniowanego przez użytkownika”. Następnie zmodyfikuję mój adapter, aby był adapterem strumieniowym.
Aktualizacja
Zgodnie z obietnicą wróciłem, ale nie z Gsonem, ale z Jacksonem 2. Przepraszam, że się spóźniłem (o 2 lata).
Przedmowa: Klawisz pozwalający na użycie mniejszej ilości pamięci dla wyniku jest w kursorze „po stronie serwera”. Dzięki tego rodzaju kursorom (czyli zestawowi wyników dla programistów Java) DBMS wysyła dane stopniowo do klienta (czyli sterownika), gdy klient kontynuuje odczyt. Myślę, że kursor Oracle jest domyślnie po stronie serwera. Dla MySQL> 5.0.2 poszukaj useCursorFetch w parametrze adresu połączenia . Sprawdź swój ulubiony DBMS.
1: Aby więc używać mniej pamięci, musimy:
JSONArray
), ale zapisz każdy wiersz bezpośrednio w wierszu wyjściowym , gdzie dla wiersza wyjściowego mam na myśli strumień wyjściowy lub pisarza lub także generator json, który opakowuje strumień wyjściowy lub piszącego.2: Jak mówi Jackson Documentation:
3: Widzę cię w twoim kodzie używając getInt, getBoolean. getFloat ... of ResultSet bez wasNull . Spodziewam się, że może to spowodować problemy.
4: Użyłem tablic do buforowania myśli i uniknięcia wywoływania funkcji pobierających w każdej iteracji. Chociaż nie jestem fanem konstrukcji przełącznika / przypadku, użyłem go do tego
int
SQLTypes
.Odpowiedź: jeszcze nie w pełni przetestowana, jest oparta na Jackson 2.2 :
ResultSetSerializer
Obiekt instruuje Jacksona, w jaki sposób do serializacji (tranform obiekt do JSON) ResultSet. Używa wewnątrz Jackson Streaming API. Tutaj kod testu:I oczywiście kod klasy ResultSetSerializer:
źródło
Dwie rzeczy, które przyspieszą to:
Przenieś wywołanie
rsmd.getColumnCount()
poza pętlę while. Liczba kolumn nie powinna się różnić w poszczególnych wierszach.Dla każdego typu kolumny wywołujesz coś takiego:
Nieco szybciej będzie można użyć indeksu kolumny do pobrania wartości kolumny:
źródło
String column_name;
poza pętlą while.Prostsze rozwiązanie (na podstawie omawianego kodu):
źródło
Możesz użyć jOOQ do tej pracy. Nie musisz używać wszystkich funkcji jOOQ, aby skorzystać z niektórych przydatnych rozszerzeń JDBC. W takim przypadku po prostu napisz:
Odpowiednie stosowane metody API to:
DSLContext.fetch(ResultSet)
aby przekonwertować zestaw wyników JDBC na wynik jOOQ.Result.formatJSON()
sformatować wynik jOOQ w ciąg JSON.Wynikowe formatowanie będzie wyglądać następująco:
Możesz również łatwo utworzyć własne formatowanie za pomocą
Result.map(RecordMapper)
To zasadniczo robi to samo, co twój kod, omijając generowanie obiektów JSON i „przesyłając strumieniowo” bezpośrednio do pliku
StringBuilder
. Powiedziałbym jednak, że w obu przypadkach narzut wydajności powinien być znikomy.(Zastrzeżenie: pracuję dla firmy stojącej za jOOQ)
źródło
"
do\"
) w celu utworzenia prawidłowego ciągu JSON. Czy to błądformatJSON()
funkcji? A może coś mi brakuje?fetch(resultSet)
? Nigdzie nie jest zdefiniowane. A jeśli otrzymam JDBCResultSet
przed pobraniem, jaki jest celDSL.using(connection)
? Dlaczego potrzebuje połączenia? :)ResultSet
, więc myślę, że nie ma co do tego wątpliwościResultSet
. Rzeczywiście, nie wydaje się oczywiste, dlaczegoconnection
jest tu potrzebne. Jeśli używasz jOOQ, i tak będziesz mieć do dyspozycjiDSLContext
(wynikDSL.using(connection)
lub podobny) miejsce do siedzenia.Oprócz sugestii @Jima Cooka. Inną myślą jest użycie przełącznika zamiast if-elses:
źródło
Ta odpowiedź może nie jest najbardziej wydajna, ale z pewnością jest dynamiczna. Łącząc natywny JDBC z biblioteką Google Gson, mogę łatwo przekonwertować wynik SQL na strumień JSON.
Dołączyłem konwerter, przykładowy plik właściwości DB, generowanie tabeli SQL i plik kompilacji Gradle (z zastosowanymi zależnościami).
QueryApp.java
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Wyniki
Podstawowy SELECT
Pośredni WYBÓR
źródło
Pierwsze wstępnie wygenerowane nazwy kolumn, drugie użycie
rs.getString(i)
zamiastrs.getString(column_name)
.Oto implementacja tego:
źródło
JSONObject json = resList.get(i);
Następnie możesz swobodnie manipulować obiektem JSONjson
.Jeśli ktoś planuje skorzystać z tej implementacji, możesz sprawdzić to i to
To jest moja wersja tego kodu konwersji:
źródło
Podobnie jak pojedynek, pętla if / then jest wydajniejsza niż przełącznik dla wyliczeń. Jeśli masz przełącznik względem surowej liczby całkowitej wyliczenia, jest to bardziej wydajne, ale w stosunku do zmiennej, jeśli / to jest bardziej wydajne, przynajmniej dla Java 5, 6 i 7.
To znaczy z jakiegoś powodu (po kilku testach wydajności)
jest szybszy niż
Widzę, że kilka osób wątpi we mnie, więc wyślę tutaj kod, który pozwoli Ci uruchomić samodzielnie, aby zobaczyć różnicę, wraz z danymi wyjściowymi z Javy 7. Wyniki poniższego kodu z 10 wartościami wyliczenia są następujące. Zauważ, że kluczem jest tutaj jeśli / to przy użyciu wartości całkowitej porównanej ze stałymi porządkowymi wyliczenia, w porównaniu z przełącznikiem z wartością porządkową wyliczenia względem surowych wartości porządkowych wewnętrznych, a przełącznikiem z wyliczeniem względem każdej nazwy wyliczenia. Jeśli / to z wartością całkowitą pokonało oba inne przełączniki, chociaż ostatni przełącznik był nieco szybszy niż pierwszy, nie był szybszy niż if / else.
Jeśli / else zajęło 23 ms, zmiana
zajęła 45 ms, zmiana
2 zajęła 30 ms.
Łączna liczba dopasowań: 3000000
źródło
intern()
for string, które w większości nie są już potrzebne w większości nowoczesnych wersji Javy.Dla wszystkich, którzy zdecydowali się na rozwiązanie if-else mesh, użyj:
Ponieważ w przypadku aliasów w zapytaniu nazwa kolumny i etykieta kolumny to dwie różne rzeczy. Na przykład, jeśli wykonasz:
Dostaniesz
Zamiast:
źródło
źródło
źródło
w drugą stronę, tutaj użyłem ArrayList i Map, więc nie jest to wywołanie obiektu json wiersz po wierszu, ale po zakończeniu iteracji zestawu wyników:
źródło