2013-07-12 11 views
17

Próbka:Czy ponownie używasz zasobów wydania Statement i Resultset z poprzedniego użycia? Czy muszę je jawnie zamykać przed ponownym użyciem? Kod

 aStmt = aConn.prepareStatement(aQuery); 
     aRset = aStmt.executeQuery(cQuery); 

     while (cRset.next()) { 
      //stuff to determine value of parm1 

      aStmt.setString(1, parm1);     
      aRset = aStmt.executeQuery(); 

      //more stuff 
     } 

Czy muszę zamknąć aStmt i Arset po każdej pętli wewnątrz while? Czy też ponowne wykorzystanie ich w kolejnych pętlach zwalnia pamięć/zasoby używane w poprzednich pętlach?

Odpowiedz

23

Zachowanie zestawów wyników i (przygotowanych) instrukcji jest wyraźnie udokumentowane w interfejsie API Java. Sugeruję przeczytanie aktualnej dokumentacji (i specyfikacji JDBC), aby uzyskać szczegóły.

Statement API mówi:

Domyślnie jedynym ResultSet obiekt za Statement obiektu mogą być otwarte w tym samym czasie. Dlatego, jeśli odczyt jednego obiektu jest przepleciony z odczytem innego, każdy musi zostać wygenerowany przez różne obiekty. Wszystkie metody wykonywania w interfejsie Statement domyślnie zamykają bieżący obiekt obiektu ResultSet, jeśli jest otwarty.

(podkreślenie moje).

W określonym kodzie, po wywołaniu aStmt.executeQuery(), stary ResultSet przypisany do aRset jest niejawnie zamknięty przez sterownik. To powiedziawszy, byłoby lepiej wyraźnie zamknąć to samodzielnie (lub użyć Java 7 try-with-resources), aby zapobiec zapomnieniu o zamknięciu ResultSet w ostatniej iteracji przez pętlę.

Teraz do PreparedStatement: Kiedy przygotowujesz instrukcję (na ogół implementacja może się różnić), zapytanie jest wysyłane do serwera w celu kompilacji.Podczas wykonywania parametry dla tego konkretnego wykonania są wysyłane do serwera. Wywołanie close() na aStmt spowodowałoby, że przygotowana instrukcja została zwolniona na serwerze, to jest wyraźnie to, czego chcesz tutaj, ponieważ chcesz ponownie użyć instrukcji z różnymi wartościami dla jej parametru.

Tak w skrócie

  1. Zamknięcie ResultSet nie jest konieczne ze względów technicznych tutaj (z wyjątkiem ostatniego ResultSet stworzonej), ale lepiej to zrobić jawnie
  2. Należy zamknąć tylko PreparedStatement gdy skończysz z tym.

Korzystanie try-with-resources jest jednym ze sposobów, aby usunąć część zamieszania w tych kwestiach, jak kod będzie automatycznie zwalnia zasoby, gdy jest ona z nim zrobić (na końcu zakresu stosowania):

try (
    ResultSet cRset = cStmt.executeQuery(cQuery); 
    PreparedStatement aStmt = aConn.prepareStatement(aQuery); 
) { 
    while (cRset.next()) { 
     //stuff to determine value of parm1 

     aStmt.setString(1, parm1);     
     try (ResultSet aRset = aStmt.executeQuery()) { 
      //more stuff 
     } 
    } 
} 

Na końcu tego fragmentu wszystkie zasoby JDBC są poprawnie zamknięte (we właściwej kolejności, nawet jeśli wystąpiły wyjątki itp.)

+0

W rzeczywistości, dla ostatniego zestawu wyników (wszystkich innych niezamkniętych zasobów), mam metodę, która zamyka wszystko na końcu programu. Biorąc to pod uwagę, czy pozostawienie mojej logiki jest takie, jakie jest? – heisenbergman

+0

@heisenbergman Powinieneś zamknąć zasoby, gdy już ich nie potrzebujesz. Pozostawienie ich otwartych może zwiększyć wykorzystanie pamięci w aplikacji i bazie danych. –

0

PreparedStatement API: instrukcja SQL jest prekompilowana i przechowywana w obiekcie PreparedStatement. Ten obiekt może być następnie użyty do wydajnego wykonywania tej instrukcji wiele razy.

Nie można jednak ponownie użyć obiektu ResultSet. Po wywołaniu executeQuery na obiekcie PreparedStatement po raz drugi tworzony jest nowy ResultSet, jeśli nie zamkniesz poprzedniego ResultSet, ryzykujesz wyciekiem zasobów.

+2

Specyfikacja JDBC wymaga sterowników do zamknięcia starego "ResultSet", jeśli nowy został utworzony z ten sam obiekt "Statement". –

2

Nie, możesz nie zamykać ResultSet i Statement wewnątrz pętli while.

Musisz zamknąć je po pętli.

Również jeśli chcesz ponownie użyć PreparedStatement, możesz nie zamykać go, dopóki nie będziesz gotowy do przetwarzania.

Najlepszą zasadą jest zamknięcie takich zasobów w tym samym bloku, w którym zostały utworzone. W twoim przypadku najlepszym rozwiązaniem jest zamknięcie zasobów w bloku finally po przechwyceniu SQLException.

E.g.

try { 
    aStmt = aConn.prepareStatement(aQuery); 
    cRset = cStmt.executeQuery(cQuery); 

    while (cRset.next()) { 
     //stuff to determine value of parm1 

     aStmt.setString(1, parm1); 
     try { 
      aRset = aStmt.executeQuery(); 
     } finally { 
      aRset.close(); 
     } 

     //more stuff 
    } 
} catch (SQLException ex) { 
    // Do error handling 
} finally { 
    // Close Resultset 
} 

W języku Java 7 można użyć funkcji wypróbuj z zasobami.

+0

Zapominasz o 'ResultSet' stworzonym w pętli –

+0

@MarkRotteveel Rozszerzyłem swoją odpowiedź. Dzięki. –

Powiązane problemy