Mam metodę pobierania użytkowników z bazy danych za pomocą JDBC:
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<User>();
try {
Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
W jaki sposób powinienem korzystać z zasobów Try-with- Java 7, aby ulepszyć ten kod?
Próbowałem z poniższym kodem, ale używa on wielu try
bloków i nie poprawia zbytnio czytelności . Czy powinienem używać try-with-resources
w inny sposób?
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<>();
try {
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery();) {
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
java
jdbc
java-7
try-with-resources
Jonas
źródło
źródło
try (ResultSet rs = ps.executeQuery()) {
ponieważ obiekt ResultSet zostaje automatycznie zamknięty przez obiekt oświadczenie, że generowanegoConnection
,PreparedStatement
iResultSet
zbyt. Nie ma powodu, aby tego nie robić, ponieważ próba z zasobami sprawia, że jest to tak łatwe i sprawia, że nasz kod jest bardziej samodokumentujący co do naszych intencji.Odpowiedzi:
W twoim przykładzie nie ma potrzeby wykonywania zewnętrznej próby, więc możesz przynajmniej zejść z 3 do 2, a także nie musisz zamykać
;
na końcu listy zasobów. Zaletą używania dwóch bloków try jest to, że cały kod jest obecny na początku, więc nie musisz odwoływać się do oddzielnej metody:źródło
Connection::setAutoCommit
? Takie połączenie nie jest dozwolone w okresietry
międzycon =
aps =
. Podczas uzyskiwania połączenia ze źródła danych, które może być obsługiwane przez pulę połączeń, nie możemy założyć, jak ustawiono funkcję autoCommit.DriverManager.getConnection(myConnectionURL)
do metody, która również ustawia flagę autoCommit i zwraca połączenie (lub ustawia je w odpowiednikucreatePreparedStatement
metody z poprzedniego przykładu ...)DataSource
gdziegetConnection
metoda działa tak, jak mówisz, uzyskaj połączenie i skonfiguruj je zgodnie z potrzebami, a następnie przekaż połączenie.Zdaję sobie sprawę, że odpowiedź była dawno temu, ale chcę zasugerować dodatkowe podejście, które pozwala uniknąć zagnieżdżonego podwójnego bloku próbującego z zasobami.
źródło
createPreparedStatement
jest niebezpieczny, niezależnie od tego, jak go używasz. Aby to naprawić, musiałbyś dodać try-catch wokół setInt (...), złapać dowolnySQLException
, a kiedy to się stanie, wywołaj ps.close () i ponownie wyrzuć wyjątek. Ale to spowodowałoby, że kod byłby prawie tak długi i nieelegancki, jak kod, który OP chciał ulepszyć.Oto zwięzły sposób wykorzystania lambd i dostawcy JDK 8, aby dopasować wszystko do zewnętrznej próby:
źródło
A co z utworzeniem dodatkowej klasy opakowania?
Następnie w klasie wywołującej można zaimplementować metodę readyStatement jako:
źródło
Jak powiedzieli inni, twój kod jest w zasadzie poprawny, chociaż zewnętrzna część
try
jest niepotrzebna. Oto kilka innych myśli.DataSource
Inne odpowiedzi tutaj są poprawne i dobre, takie jak zaakceptowana odpowiedź bpgergo. Ale żaden z nich nie używa
DataSource
, powszechnie zalecanego używania,DriverManager
we współczesnej Javie.Tak więc, aby uzyskać kompletność, oto kompletny przykład, który pobiera bieżącą datę z serwera bazy danych. Użyta tutaj baza danych to Postgres . Każda inna baza danych działałaby podobnie. Zastąpiłbyś użycie
org.postgresql.ds.PGSimpleDataSource
implementacjąDataSource
odpowiedniej dla Twojej bazy danych. Implementacja jest prawdopodobnie dostarczana przez określony sterownik lub pulę połączeń, jeśli wybierzesz tę trasę.DataSource
Realizacja musi nie być zamknięte, ponieważ nigdy nie jest „otwarty”. ADataSource
nie jest zasobem, nie jest połączony z bazą danych, więc nie utrzymuje połączeń sieciowych ani zasobów na serwerze bazy danych. ADataSource
to po prostu informacje potrzebne podczas nawiązywania połączenia z bazą danych, z nazwą lub adresem sieciowym serwera bazy danych, nazwą użytkownika, hasłem użytkownika i różnymi opcjami, które mają być określone po ostatecznym nawiązaniu połączenia. Tak więcDataSource
obiekt implementacji nie jest umieszczany w nawiasach próbnych z zasobami.Zagnieżdżone try-with-resources
Twój kod prawidłowo wykorzystuje zagnieżdżone instrukcje try-with-resources.
Zauważ, że w poniższym przykładowym kodzie używamy również składni try-with-resources dwukrotnie , jedna zagnieżdżona w drugiej. Zewnętrzny
try
definiuje dwa zasoby:Connection
iPreparedStatement
. Wewnętrznatry
określaResultSet
zasób. To jest wspólna struktura kodu.Jeśli wyjątek zostanie wyrzucony z wewnętrznego i nie zostanie tam przechwycony,
ResultSet
zasób zostanie automatycznie zamknięty (jeśli istnieje, nie jest zerowy). NastępniePreparedStatement
zostanie zamknięty, a na koniecConnection
zamknięty. Zasoby są automatycznie zamykane w odwrotnej kolejności, w jakiej zostały zadeklarowane w instrukcjach try-with-resource.Przykładowy kod jest zbyt uproszczony. Jak napisano, można to wykonać za pomocą jednej instrukcji try-with-resources. Ale w prawdziwej pracy prawdopodobnie wykonasz więcej pracy między zagnieżdżonymi parami
try
wywołań. Na przykład możesz wyodrębniać wartości z interfejsu użytkownika lub POJO, a następnie przekazywać je w celu wypełnienia?
symboli zastępczych w języku SQL za pośrednictwem wywołańPreparedStatement::set…
metod.Uwagi dotyczące składni
Końcowy średnik
Zwróć uwagę, że średnik kończący ostatnią instrukcję zasobu w nawiasach próby z zasobami jest opcjonalny. Uwzględniam go w mojej pracy z dwóch powodów: spójności i wygląda na kompletny, a także ułatwia kopiowanie i wklejanie mieszanki linii bez martwienia się o średniki końca wiersza. Twoje IDE może oznaczyć ostatni średnik jako zbędny, ale nie ma nic złego w pozostawieniu go.
Java 9 - Użyj istniejących zmiennych w próbach z zasobami
Nowością w Javie 9 jest ulepszenie składni try-with-resources. Teraz możemy zadeklarować i wypełnić zasoby poza nawiasami w
try
instrukcji. Nie znalazłem jeszcze tego przydatnego dla zasobów JDBC, ale miej to na uwadze w swojej własnej pracy.ResultSet
powinien się zamknąć, ale nie możeW idealnym świecie
ResultSet
zamknęłaby się, jak obiecuje dokumentacja:Niestety, w przeszłości niektóre sterowniki JDBC niesławnie nie spełniały tej obietnicy. W rezultacie, wielu programistów JDBC nauczył się wyraźnie blisko wszystkich swoich zasobów JDBC tym
Connection
,PreparedStatement
iResultSet
też. Nowoczesna składnia try-with-resources sprawiła, że jest to łatwiejsze i bardziej kompaktowe. Zauważ, że zespół Java zajął się oznaczaniemResultSet
jakoAutoCloseable
i sugeruję, abyśmy to wykorzystali. Korzystanie z zasobów typu „try-with-resources” wokół wszystkich zasobów JDBC sprawia, że kod jest bardziej samodokumentujący, jeśli chodzi o intencje.Przykład kodu
źródło