Mamy system, w którym połączenie z bazą danych jest uzyskiwane raz za pomocą wspólnej metody i przekazywane przez odpowiednią klasę, która ma być używana. Istnieją wątpliwości, że przekazanie połączenia z bazą danych jako parametru do różnych klas spowodowałoby problem, więc sprawdzam tutaj, czy jest to faktycznie wykonalne, i czy są jakieś lepsze wzorce, aby to zrobić?
Wiem, że istnieją narzędzia ORM do uporczywości, ale nie możemy się w to zagłębić, ale ..
Wszelkie opinie są mile widziane, dziękuję.
java
database
patterns-and-practices
ipohfly
źródło
źródło
Odpowiedzi:
Tak, można bezpiecznie ominąć połączenie. Obsługujesz połączenie w zewnętrznym bloku sterującym. Nie ma w tym nic niebezpiecznego.
Niebezpieczne jest pisanie kodu, który nie gwarantuje, że połączenie zostanie odpowiednio usunięte w odpowiednim czasie. Zapominanie o oczyszczeniu zasobu nie ma związku z przekazywaniem go. Równie łatwo możesz napisać kod, który pozostawia zawieszone połączenie, nie przekazując go nigdzie.
W C ++ jesteś chroniony przez RAII, jeśli alokujesz na stosie lub używasz inteligentnych wskaźników. W języku C # ustal twardą zasadę, że wszystkie obiekty jednorazowego użytku (takie jak połączenia) powinny być zadeklarowane w bloku „używającym”. W Javie posprzątaj z logiką try-wreszcie. Dokonaj recenzji kodu dla wszystkich kodów warstwy danych, aby to zapewnić.
Najczęstszym przypadkiem użycia jest kilka operacji, które można łączyć w wiele kombinacji. I każda z tych permutacji musi być transakcją atomową (wszystkie zakończone powodzeniem lub wycofaniem). następnie musisz przekazać transakcję (a zatem odpowiednie połączenie) do wszystkich metod.
Załóżmy, że mamy wiele akcji foobar (), które można łączyć na różne sposoby jako transakcje atomowe.
BTW, będziesz chciał otwierać połączenia tak późno, jak to możliwe, usuń je jak najszybciej. Twoi koledzy z drużyny mogą mieć rację, jeśli traktujesz połączenia jako elementy obiektu, wprowadzając je jako niepotrzebny stan i pozostawiając połączenia otwarte dłużej niż to konieczne. Ale przekazanie połączenia lub transakcji jako parametru nie jest z natury złe.
BTW. W zależności od wsparcia języka dla funkcji pierwszej klasy, możesz podjąć listę działań foobar (). Tak więc jedna funkcja może obsłużyć wszystkie kombinacje akcji. Eliminowanie duplikacji zewnętrznego bloku kontrolnego dla każdej permutacji.
źródło
Brzmi jakbyś chciał wstrzyknięciu zależności . Oznacza to, że połączone połączenie jest tworzone raz i wprowadzane tam, gdzie jest potrzebne. Oczywiście przekazywanie połączenia za pomocą parametru metody jest jednym ze sposobów wstrzykiwania zależności, ale kontener IoC, taki jak Guice, PicoContainer lub Spring, jest innym (bezpieczniejszym) sposobem, aby to zrobić.
Korzystanie z DI oznacza, że możesz starannie zamknąć logikę wokół tworzenia, otwierania, używania i zamykania połączenia - z dala od podstawowej logiki biznesowej.
Spring JDBC i in. Są innymi przykładami wykonywania tego rodzaju zachowania za Ciebie
źródło
Przekazywanie elementów bazy danych zamiast danych może prowadzić do problemów. Do tego stopnia, o ile jest to praktyczne, nie przechodź przez bazę danych, chyba że można zagwarantować odpowiednią higienę bazy danych.
Problem z przekazywaniem rzeczy do bazy danych polega na tym, że może być niechlujny. Widziałem więcej niż jeden błąd w kodzie, gdy ktoś przechodził wokół połączenia z bazą danych, który następnie pobiera zestaw wyników i ukrywa w obiekcie lokalnym (zestaw wyników, nadal podłączony do bazy danych), a następnie wiąże kursor w baza danych przez długi czas. W innym przypadku ktoś przekazał zestaw wyników komuś innemu (który został następnie ukryty), a następnie metoda, która przekazała zestaw wyników, zamknęła go (i instrukcję), co prowadziło do błędów, gdy inne metody próbowały pracować z zestawem wyników, który już nie był.
Wszystko to wynika z nieprzestrzegania bazy danych, połączenia, instrukcji, zestawu wyników i ich cykli życia.
Aby tego uniknąć, istnieją istniejące wzorce i struktury, które lepiej się bawią z bazami danych i nie wymagają, aby baza danych musiała wychodzić z klas, w których są zamknięte. Dane wchodzą, dane wychodzą, baza pozostaje.
źródło
Root
z Dao. Ale wtedy zdajesz sobie sprawę, że chcesz również uzyskać sposób,Node
nie wyciągając przy tym całegoRoot
obiektu. W jaki sposób sprawiasz, żeRoot
dao wywołujeNode
kod dao (tj .: ponowne użycie), ale upewnij się, żeNode
dao zamyka połączenie tylko wtedy, gdyNode
dao jest wywoływane bezpośrednio, i utrzymuje połączenie otwarte, gdyRoot
wywoływane jest dao?Przekazywanie
Connection
instancji zwykle nie stanowi problemu, chociaż w większości sytuacji tylko implementacje DAO powinny mieć z nimi coś wspólnego. Teraz, gdy problem polega na tym, że połączenia nie są zamykane po użyciu, w rzeczywistości jest to łatwe do naprawienia:Connection
obiekt musi zostać zamknięty na tym samym poziomie, na którym jest otwarty, tj. Tą samą metodą. Osobiście używam następującego wzorca kodu:W ten sposób zapewniam, że wszystkie połączenia są zawsze zamknięte, nawet jeśli wyjątek zostanie zgłoszony w bloku. Właściwie pracuję tak długo, jak używam dokładnie tego samego wzoru
Statement
iResultSet
instancji, a do tej pory wszystko przebiegało płynnie.Edytuj 2018-03-29: Jak wskazał użytkownik1156544 w poniższych komentarzach, od wersji Java 7 należy preferować konstrukcję try-with-resources. Korzystając z niego, wzór kodu podany w mojej początkowej odpowiedzi można uprościć w następujący sposób:
źródło
dataSource
a nieDataSource
(naprawię moją odpowiedź dotyczącą tego punktu). Dokładny typ tego obiektu tojavax.sql.DataSource
. W starym kodzie miałem singletona do zarządzania wszystkimi dostępnymi źródłami danych w moich aplikacjach. Moje DAO nie musiały o tym wiedzieć, ponieważDataSource
instancja jest zapewniona przez wstrzyknięcie zależności.istnieje kompromis w robieniu rzeczy w ten sposób, zamiast korzystania z singletonu, który można uzyskać w razie potrzeby. W przeszłości robiłem to na dwa sposoby.
Ogólnie rzecz biorąc, musisz pomyśleć o konsekwencjach zarządzania połączeniami z bazą danych, a może to być ortogonalne w stosunku do użycia zapytań do bazy danych. Na przykład, jeśli masz jedno połączenie db dla danej instancji aplikacji i zostanie ono zamknięte, gdy nie będzie używane, byłoby to ortogonalne. Umieść kierownictwo w klasie singleton i nie pomijaj go. Umożliwia to zarządzanie połączeniem db według potrzeb. Na przykład, jeśli chcesz zamknąć połączenie przy każdym zatwierdzeniu (i ponownie otworzyć przy następnym wywołaniu), łatwiej jest to zrobić w singletonie, ponieważ API do tego celu można scentralizować.
Z drugiej strony załóżmy, że musisz zarządzać pulą połączeń, w której dane połączenie może wymagać dowolnego dowolnego połączenia. Może się to zdarzyć na przykład podczas wykonywania transakcji rozproszonych na wielu serwerach. W takim przypadku zwykle lepiej jest przekazywać obiekt połączenia db niż praca z singletonami. Myślę, że jest to zwykle rzadszy przypadek, ale nie ma nic złego w robieniu tego, kiedy trzeba.
źródło