2012-10-18 10 views
12

Podczas korzystania z JDBC w Javie, ogólnie przyjętą metodą sprawdzania bazy danych jest uzyskanie połączenia, utworzenie instrukcji z tego połączenia, a następnie wykonanie zapytania z tej instrukcji.Preferowany sposób wielokrotnego wysyłania zapytań do bazy danych?

// load driver 
Connection con = DriverManager.getConnection(..); 
Statement stmt = con.createStatement(); 
ResultSet result = stmt.executeQuery("SELECT.."); 
// ... 

Jednak nie jestem pewien, jak potraktować drugie zapytanie do tej samej bazy danych.

  1. Czy kolejna kwerenda być wykonywane bezpiecznie na tym samym Statement obiektu lub inny rachunek musi zostać utworzony z obiektu Connection w celu wykonania innej kwerendy?

  2. Jeżeli ten sam Statement przedmiot mogą być wykorzystywane do wielu zapytań, co jest celem tej klasy Statement (ponieważ byłoby to wtedy więcej sensu dla metody Connection.executeQuery() istnieć)?

+4

Po pierwsze, czy próbowałeś to zrobić samemu? Czy otrzymałeś jakieś wyjątki lub niechciane wydruki? –

+1

Istnieje mnóstwo przykładów w Internecie. Możesz tam je obejrzeć, spróbować samemu, a jeśli dostaniesz wyjątek lub problemy rozwiążą twoje pytanie, możemy pomóc ci bardziej precyzyjnie. – user

+0

@RohitJain Nie mam, ale jestem prawie pewny, że kilka zapytań może być wykonanych z tej samej opcji Oświadczenia, co prowadzi mnie do zastanowienia się, czy są jakieś wady lub wątpliwości dotyczące bezpieczeństwa (robienie tego) (pytanie 1), aw rezultacie , jaki jest sens klasy Statement (pytanie 2). – Vulcan

Odpowiedz

7

Tak można ponownie użyć obiektu Statement, ale ResultSet obiektów zwracanych przez executeQuery zamyka już otwarty resultsets.

Zobacz javadoc za wyjaśnienie

Domyślnie tylko jeden obiekt ResultSet za Oświadczenie obiektu może być otwarty w tym samym czasie. Dlatego jeśli odczyt jednego obiektu ResultSet jest przeplatany z odczytem innego, każdy musi zostać wygenerowany przez różne obiekty Statement. Wszystkie metody wykonywania w interfejsie Statement niejawnie zamykają bieżący obiekt ResultSet, który istnieje, jeśli otwarty jest obiekt otwarty .

więc następujące zdarzenia:

// load driver 
Connection con = DriverManager.getConnection(..); 
Statement stmt = con.createStatement(); 
ResultSet result = stmt.executeQuery("select .."); 
// do something with result ... or not 
ResultSet result2 = stmt.executeQuery("select ..."); 
// result is now closed, you cannot read from it anymore 
// do something with result2 
stmt.close(); // will close the resultset bound to it 

Na przykład można znaleźć implementację open source o stwierdzenie w projekcie jTDS. W Statement.executeQuery() method widać wywołanie initialize() że closes all the resultsets już otwarty

protected void initialize() throws SQLException { 
    updateCount = -1; 
    resultQueue.clear(); 
    genKeyResultSet = null; 
    tds.clearResponseQueue(); 
    // FIXME Should old exceptions found now be thrown instead of lost? 
    messages.exceptions = null; 
    messages.clearWarnings(); 
    closeAllResultSets(); 
} 
+0

Jeśli wszystkie metody wykonywania niejawnie zamykają bieżący zestaw wyników instrukcji (jeśli jest otwarty), czy konieczne jest jawne zamknięcie zestawu wyników przed wykonaniem innego zapytania? – Vulcan

+0

@Vulcan masz rację, jak zauważyłeś i jak to jest powiedziane w javadoc, metody 'execute *()' na 'Statement' wydają się zamykać każdy otwarty' ResultSet' który z niego pochodzi, więc w praktyce jest nie jest obowiązkowe, aby wywołać 'close()' na nim przed wydaniem nowego wykonania, ale dobrą praktyką jest zamknięcie zasobów, które otworzyłeś. – Alex

+0

Zobacz moją edycję dla implementacji open source jTDS jako przykładu. – Alex

1

No to dlaczego mamy koncepcję klas w programowaniu obiektowym. Klasa definiuje członków składowych, które umożliwiają jej instancjom stan i zachowanie. Tutaj oświadczenie dotyczy wszystkiego, co dotyczy instrukcji sql. Istnieje o wiele więcej funkcji, które można wykonywać jak zapytania wsadowe itp.

3

Programowo można ponownie użyć tego samego połączenia i tej samej instrukcji dla więcej niż jednego zapytania i zamknąć instrukcję i połączenie na końcu.

Jednak nie jest to dobra praktyka. Wydajność aplikacji jest bardzo wrażliwa na sposób uzyskiwania dostępu do bazy danych. W idealnym przypadku każde połączenie powinno być otwarte przez możliwie najmniej czasu. Następnie połączenia muszą być połączone. Przechodząc przez to, należy ująć każde zapytanie w bloku {open connection, create a prepared statement, run query, close statement, close connection}. Jest to również sposób implementacji większości szablonów SQL. Jeśli pozwala na to współbieżność, możesz zwolnić kilka takich kwerend jednocześnie, używając puli wątków.

+0

Brzmi rozsądnie, ale jaki jest powód utrzymywania połączenia otwartego przez możliwie najmniej czasu? Czy lepiej jest pozostawić jedno połączenie otwarte, aby obsłużyć 10 zapytań na minutę, lub otworzyć nowe połączenie średnio raz na 6 sekund? – Vulcan

+0

@Vulcan To nie to samo. Masz połączenie otwarte przez najmniej czasu, a następnie zwracasz je z powrotem do puli. Pula pozostawi otwarte połączenie dla Ciebie. Ponieważ "zamykasz" połączenie za każdym razem, gdy masz pewność, że zawsze powracało do puli. Jeśli sam sobie poradzisz, ryzyko wzrasta, że ​​brakuje ci zamknięcia i uruchamiasz sesje bazy danych lub problemy z porami pamięci. –

1

Zazwyczaj jest to jedna instrukcja dla jednego zapytania.Może to nie być konieczne, ale przy pisaniu prawdziwej aplikacji nie chcesz powtarzać tych samych kroków raz za razem. To jest wbrew dyrekcji DRY, a także będzie bardziej skomplikowana, gdy aplikacja będzie się rozwijać.

Dobrze jest pisać obiekty, które poradzą sobie z takimi niskopoziomowymi (powtarzalnymi) materiałami i zapewnić różne metody uzyskiwania dostępu do bazy danych poprzez dostarczanie zapytań.

1

Nie martwię się o tworzenie nowych wyciągów. Jednak otwieranie połączenia z bazą danych może wymagać dużego nakładu zasobów, a otwieranie i zamykanie połączeń ma wpływ na wydajność.

Opuszczanie połączeń w pewien sposób samodzielnego zarządzania zwykle jest dość złe.

Powinieneś rozważyć użycie connection pooling. Zwykle wydajesz komendę close, ale tylko ty przekazujesz to połączenie z powrotem do puli. Gdy poprosisz o nowe połączenie, ponownie wykorzystasz połączenie, które otrzymałeś wcześniej.

Możesz chcieć mieć różne instrukcje dla jednego połączenia. Oświadczenie jest implementacją i interfejsem. W zależności od tego, czego potrzebujesz, czasami potrzebujesz użyć CallableStatment. Niektóre logiki mogą być ponownie wykorzystane, gdy jest to wymagane.

+0

Dzięki, nigdy wcześniej nie spotkałem się z łączeniem połączeń i wydaje się, że jest to niezwykle przydatna koncepcja, której będę używać w mojej aplikacji na serwerze. – Vulcan

+0

@Vulcan Jeśli aplikacja serwera jest aplikacją internetową, serwer aplikacji może wykonać pulę połączeń. Konfiguracja powinna zostać opisana na serwerze aplikacji. W przeciwnym razie możesz użyć http://commons.apache.org/dbcp/ lub innego wybranego zestawu narzędzi. –

+0

Moje przeprosiny, nie wierzę, że było jasne z tym stwierdzeniem; to po prostu aplikacja Java obsługująca gniazdo serwera dla gniazd klienta, z którymi można się połączyć. – Vulcan

2

Mam jedną rzecz do dodania, jeśli korzystasz z połączenia i instrukcji w środowisku z wątkami. Moje doświadczenie pokazuje, że stmt.executeQuery (..) jest zapisywany do użycia w środowisku równoległym, ale z konsekwencją, że każde zapytanie jest serializowane i w ten sposób przetwarzane sekwencyjnie, nie przynosząc żadnych przyspieszeń. Więc lepiej jest użyć nowego połączenia (nie oświadczenia) dla każdego wątku.

Dla standardowego środowiska sekwencyjnego moje doświadczenie pokazało, że ponowne użycie instrukcji nie stanowi żadnego problemu, a ResultSet nie musi być zamykany ręcznie.

+0

Znalazłem to bardzo pomocne, ponieważ * jestem * za pomocą połączenia i oświadczenia w środowisku wątku. W połączeniu z odpowiedzią udzieloną przez * Udo Held *, czuję, że nauczyłem się tego, czego potrzebuję w przypadku wielowątkowych połączeń z bazą danych. – Vulcan

Powiązane problemy