Zamykanie połączeń JDBC w puli


109

Nasza standardowa sekcja kodu do korzystania z JDBC to ...

Connection conn = getConnection(...);
Statement  stmt = conn.conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY);
ResultSet  rset = stmt.executeQuery (sqlQuery);

// do stuff with rset

rset.close(); stmt.close(); conn.close();

Pytanie 1: Czy w przypadku korzystania z puli połączeń należy zamknąć połączenie na końcu? Jeśli tak, to czy nie utracono celu łączenia? A jeśli nie, w jaki sposób źródło danych wie, kiedy określone wystąpienie połączenia jest zwolnione i może zostać ponownie użyte? Jestem trochę zdezorientowany w tej sprawie, doceniam wszelkie wskazówki.

Pytanie 2: Czy poniższa metoda jest zbliżona do standardowej? Wygląda na to, że próbowano uzyskać połączenie z puli, a jeśli nie można ustanowić DataSource, użyj staroświeckiego DriverManager. Nie jesteśmy nawet pewni, która część jest wykonywana w czasie wykonywania. Powtarzając powyższe pytanie, czy należy zamknąć Połączenie wychodzące z takiej metody?

Dziękuję, - MS.

synchronized public Connection getConnection (boolean pooledConnection)
                                                        throws SQLException {
        if (pooledConnection) {
                if (ds == null) {
                        try {
                                Context envCtx = (Context)
                                        new InitialContext().lookup("java:comp/env");
                                ds = (DataSource) envCtx.lookup("jdbc/NamedInTomcat");
                                return ds.getConnection();
                        } catch (NamingException e) {
                                e.printStackTrace();
                }}
                return (ds == null) ? getConnection (false) : ds.getConnection();
        }
        return DriverManager.getConnection(
                "jdbc:mysql://"+ipaddy+":"+dbPort +"/" + dbName, uName, pWord);
}

Edycja: myślę, że otrzymujemy połączenie w puli, ponieważ nie widzimy śladu stosu.

Odpowiedzi:


121

Czy w przypadku korzystania z puli połączeń należy zamknąć połączenie na końcu? Jeśli tak, to czy nie utracono celu łączenia? A jeśli nie, w jaki sposób źródło danych wie, kiedy określone wystąpienie połączenia jest zwolnione i może zostać ponownie użyte? Jestem trochę zdezorientowany w tej sprawie, doceniam wszelkie wskazówki.

Tak, z pewnością musisz również zamknąć połączenie w puli. W rzeczywistości jest to opakowanie wokół rzeczywistego połączenia. Pod kołdrą uwolni rzeczywiste połączenie z powrotem do basenu. W dalszej części pula decyduje, czy rzeczywiste połączenie zostanie faktycznie zamknięte, czy też zostanie ponownie wykorzystane do nowego getConnection()połączenia. Tak więc, niezależnie od tego, czy używasz puli połączeń, czy nie, powinieneś zawsze zamykać wszystkie zasoby JDBC w odwrotnej kolejności w finallybloku trybloku, w którym je nabyłeś. W Javie 7 można to jeszcze bardziej uprościć za pomocą try-with-resourcesinstrukcji.


Czy poniższa metoda jest zbliżona do standardowej? Wygląda na to, że próbowano uzyskać połączenie z puli, a jeśli nie można ustanowić DataSource, użyj staroświeckiego DriverManager. Nie jesteśmy nawet pewni, która część jest wykonywana w czasie wykonywania. Powtarzając powyższe pytanie, czy należy zamknąć Połączenie wychodzące z takiej metody?

Przykład jest dość przerażający. Wystarczy tylko wyszukać / zainicjować DataSourcetylko raz podczas uruchamiania aplikacji w jakimś konstruktorze / inicjalizacji klasy konfiguracyjnej DB obejmującej całą aplikację. Następnie po prostu wywołaj getConnection()to samo źródło danych przez resztę życia aplikacji. Nie ma potrzeby synchronizacji ani kontroli null.

Zobacz też:


To właśnie robi (inicjalizuj raz), prawda? ds jest zmienną instancji, a jeśli (ds == null) ... jest częścią inicjalizacyjną.
Manidip Sengupta

Wykonywanie kontroli za każdym razem w metodzie get getConnection()jest dziwne. Po prostu zrób to w c'tor lub bloku inicjalizacyjnym tej samej klasy, bez synchronizacji / kontroli null. Zostanie wywołany tylko raz. Aby uzyskać więcej wskazówek i początkowych przykładów, przydatny może być ten artykuł .
BalusC,

Świetny artykuł, BalusC. Klasa, z którą mam do czynienia, w dużej mierze implementuje warstwę danych, używając DTO. Zgadzam się z tobą, inicjalizacja powinna być w konstruktorze. Teraz ta klasa ma mnóstwo metod, każda ze zmiennymi lokalnymi conn, stmt i rset, połączenia znajdują się w bloku try, a na końcu jest wywołanie 1-liniowe csrClose (conn, stmt, rset), gdzie wszystkie 3 są zamknięte (w odwrotnej kolejności). Teraz stworzony w przykładzie DTO jest lustrzanym odbiciem wiersza tabeli DB. Mamy złożone zapytania SQL z łączeniami (i innymi klauzulami). Czy masz artykuł o tym, jak tworzyć DAO dla takich wyników?
Manidip Sengupta

2
@yat: MUSISZ wywołać ich close()wszystkich w finallybloku tego samego trybloku, w którym je nabyłeś / stworzyłeś. Dzieje się tak całkowicie niezależnie od tego, czy jest to połączenie w puli, czy nie.
BalusC,

1
@iJava: ten basen jest napisany przez amatora, który nie ma pojęcia, co robi. Zignoruj ​​to i idź do prawdziwej biblioteki. Np. HikariCP.
BalusC

22

Pule zazwyczaj zwracają opakowany obiekt Connection, w którym metoda close () jest zastępowana, zazwyczaj zwracając Connection do puli. Wywołanie funkcji close () jest w porządku i prawdopodobnie nadal jest wymagane.

Metoda close () prawdopodobnie wyglądałaby tak:

public void close() throws SQLException {
  pool.returnConnection(this);
}

W przypadku drugiego pytania możesz dodać rejestrator, aby pokazać, czy dolny blok kiedykolwiek działa. Wyobrażam sobie, że chciałbyś tylko w ten czy inny sposób do konfiguracji połączeń z bazą danych. Puli używamy wyłącznie do dostępu do naszej bazy danych. Tak czy inaczej, zamknięcie połączenia byłoby bardzo ważne, aby zapobiec wyciekom.


Zgadzam się, mamy rejestrator i można go również tutaj użyć. Muszę uczyć się trochę o tym, jak można owinąć obiektu, nadpisać metodę jego close (), ale nadal utrzymują tę samą nazwę klasy (połączenia)
Manidip Sengupta

1
Calling close() is OK and probably still required., brak wywołania close spowoduje wyciek połączenia, chyba że pula wdroży jakąś strategię odzyskiwania
svarog

0

W rzeczywistości najlepszym podejściem do zarządzania połączeniami jest nieprzesyłanie ich do żadnego kodu w dowolnym miejscu.

Utwórz klasę SQLExecutor, która jest jedyną lokalizacją, która otwiera i zamyka połączenia.

Cała reszta aplikacji pompuje instrukcje do modułu wykonawczego, zamiast pobierać połączenia z puli i zarządzać nimi (lub niewłaściwie nimi zarządzać) w dowolnym miejscu.

Możesz mieć dowolną liczbę instancji modułu wykonawczego, ale nikt nie powinien pisać kodu, który otwiera i zamyka połączenia we własnym imieniu.

Dogodnie umożliwia to również rejestrowanie całego kodu SQL z jednego zestawu kodu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.