Kiedy powinienem używać DBIx :: Class Perla?


17

DBIx :: Class to popularny interfejs Perla do dowolnej bazy danych, z którą można się połączyć za pośrednictwem DBI . Istnieje dobra dokumentacja zawierająca szczegółowe informacje techniczne, ale mało informacji na temat jego prawidłowego wykorzystania (w tym sytuacji, w których prawdopodobnie nie chcesz).

W wielu sytuacjach ludzie sięgają po niego odruchowo, ponieważ uważają, że powinni go używać do wszystkiego, co dotyczy bazy danych. Jednak najczęściej widziałem, że jest nadużywane do tego stopnia, że ​​staje się punktem bólu. Moje pytanie podczas recenzji kodu i architektury zawsze brzmi: „Jakie korzyści daje Foo?” Najczęściej programiści, których widzę w takich sytuacjach, nie mogą na to udzielić spójnej odpowiedzi. Ale często też nie rozumieją prostego SQL.

Od kilku miesięcy pytam ludzi „Dlaczego korzystasz z DBIx :: Class?” i otrzymałem tylko jedną dobrą odpowiedź (i ta osoba była również w stanie odpowiedzieć na pytanie „Kiedy nie użyjesz”). Peter Rabbitson, główny programista, zbliżył się do odpowiedzi w swoim wywiadzie dla FLOSS Weekly , ale jest to nieco zakopane w środku wywiadu.

Jak więc zdecydować, czy użycie DBIx :: Class jest odpowiednie dla projektu?


3
Piszesz kolejną książkę? :)
simbabque

1
Z mojego doświadczenia wynika, że ​​ból pojawia się, gdy DBIC jest używany w sytuacjach, w których jest on nadmierny. I pomimo tego, że jest bardzo potężny, często przesadza, ponieważ ludzie używają tylko podstawowych funkcji (generowanie i łączenie SQL) i nie potrzebują niczego więcej. Dlatego napisałem DBIx :: Lite, który zapewnia te podstawowe funkcje i nie wymaga sztywnego kodowania żadnego schematu.
Alessandro,

Odpowiedzi:


24

Zanim odpowiem na pytanie, wydaje mi się, że pewne tło jest w porządku.

Rdzeń problemu

Po latach wywiadów i zatrudniania programistów nauczyłem się dwóch rzeczy:

  1. Zdecydowana większość programistów ma bardzo małe doświadczenie w projektowaniu baz danych.

  2. Zauważyłem luźną korelację między tymi, którzy nie rozumieją baz danych, a tymi, którzy nienawidzą ORM.

(Uwaga: i tak, wiem, że są tacy, którzy bardzo dobrze rozumieją bazy danych i nienawidzą ORM)

Kiedy ludzie nie rozumieją, dlaczego klucze obce są ważne, dlaczego nie umieścić nazwę producenta w itemtabeli, ani dlaczego customer.address1, customer.address2i customer.address3pola nie są dobrym pomysłem, dodając ORM aby łatwiej dla nich pisać błędów bazy danych nic nie pomoże.

Zamiast tego, z odpowiednio zaprojektowaną bazą danych i przykładem użycia OLTP, ORM są złote. Większość pracy w chrząstce znika, a dzięki narzędziom takim jak DBIx :: Class :: Schema :: Loader mogę przejść od dobrego schematu bazy danych do działającego kodu Perla w kilka minut. Przywołałbym zasadę Pareto i powiedziałbym, że 80% moich problemów rozwiązano przy 20% pracy, ale w rzeczywistości uważam, że korzyści są jeszcze większe.

Nadużywanie rozwiązania

Innym powodem, dla którego niektórzy ludzie nienawidzą ORM, jest to, że pozwalają wyciek abstrakcji. Rozważmy typowy przypadek aplikacji internetowych MVC. Oto coś, co zwykle widzimy (pseudo-kod):

GET '/countries/offices/$company' => sub {
    my ( $app, $company_slug ) = @_;
    my $company = $app->model('Company')->find({ slug => $company_slug }) 
      or $app->redirect('/');
    my $countries = $app->model('Countries')->search(
     {
         'company.company_id' => $company->company_id,
     },
     {
         join     => [ offices => 'company' ],
         order_by => 'me.name',
     },
   );
   $app->stash({
     company   => $company,
     countries => $country,
   });
}

Ludzie piszą takie trasy kontrolerów i klepią się po plecach, myśląc, że to dobry, czysty kod. Byliby wstrząśnięci twardym kodowaniem SQL w swoich kontrolerach, ale zrobili niewiele więcej niż ujawnienie innej składni SQL. Ich kod ORM musi zostać zepchnięty do modelu, a następnie mogą to zrobić:

GET '/countries/offices/$company' => sub {
   my ( $app, $company_slug ) = @_;
   my $result = $app->model('Company')->countries($company_slug)
     or $app->redirect('/');
   $app->stash({ result => $result });
}

Wiesz co się teraz stało? Właściwie zamknąłeś model, nie ujawniłeś ORM, a później, gdy okaże się, że możesz pobrać te dane z pamięci podręcznej zamiast z bazy danych, nie musisz zmieniać kodu kontrolera (i jest to łatwiejsze napisać dla niego testy i ponownie użyć logiki).

W rzeczywistości dzieje się tak, że ludzie wyciekają kod ORM na wszystkie kontrolery (i widoki), a kiedy napotykają problemy ze skalowalnością, zaczynają obwiniać ORM, a nie swoją architekturę. ORM dostaje zły rap (widzę to wielokrotnie dla wielu klientów). Zamiast tego ukryj tę abstrakcję, aby po prawdziwym przekroczeniu limitów ORM możesz wybrać odpowiednie rozwiązania dla swojego problemu, zamiast pozwolić, aby kod był tak ściśle powiązany z ORM, że jesteś związany.

Raportowanie i inne ograniczenia

Jak wyjaśnił powyżej Rob Kinyon, raportowanie jest zwykle słabą stroną ORM. Jest to podzbiór większego problemu, w którym skomplikowany SQL lub SQL obejmujący wiele tabel czasami nie działa dobrze z ORM. Na przykład czasami ORM wymusza typ łączenia, którego nie chcę i nie mogę powiedzieć, jak to naprawić. A może chcę użyć podpowiedzi do indeksu w MySQL, ale nie jest to łatwe . A czasem SQL jest tak cholernie skomplikowany, że fajniej byłoby napisać SQL niż dostarczoną abstrakcję.

Jest to jeden z powodów, dla których zacząłem pisać DBIx :: Class :: Report . Jak dotąd działa dobrze i rozwiązuje większość problemów, które mają tutaj ludzie (o ile są w porządku z interfejsem tylko do odczytu). I chociaż w rzeczywistości wydaje się to kulą, o ile nie przeciekasz abstrakcji (jak wyjaśniono w poprzedniej sekcji), sprawia, że ​​praca z nią jest DBIx::Classjeszcze łatwiejsza.

Kiedy więc wybrać DBIx :: Class?

Dla mnie najczęściej wybrałbym interfejs do bazy danych. Używam go od lat. Jednak mogę nie wybrać go dla systemu OLAP, a nowi programiści z pewnością będą mieli z tym problem. Często też uważam, że potrzebuję metaprogramowania i chociaż DBIx::Classzapewnia narzędzia, są one bardzo słabo udokumentowane.

Klucz do DBIx::Classprawidłowego korzystania jest taki sam, jak w przypadku większości ORM:

  1. Nie wyciekaj z abstrakcji.

  2. Napisz swoje przeklęte testy.

  3. W razie potrzeby umieć przejść do SQL.

  4. Dowiedz się, jak znormalizować bazę danych.

DBIx::Class, gdy się go nauczysz, zajmie się większością ciężkiego podnoszenia i sprawia, że ​​szybkie pisanie aplikacji jest dziecinnie proste.


1
Może możesz dodać kolejną listę, gdy nie będziesz jej używać. :)
brian d foy,

1
Jest to dla ciebie oczywiste, ale prawdopodobnie nie jest oczywiste dla wielu czytelników (mówię, że spędziłem lata w #dbix-classi #catalyst) - kluczem do bitu „nie wyciekać abstrakcji” jest to, że każda rzecz, nad którą pracujesz w DBIC, to podklasa czegoś, co zapewnia zachowanie obcinania plików cookie. Zdecydowanie zachęca się do dodawania metod do podklas i jeśli nie wykonujesz zadania Q&D, tylko metody, które napisałeś, powinny stanowić część interfejsu publicznego.
hobbs

@ Hobbs: Rzeczywiście, właśnie tam widzę, że ludzie najbardziej się mylą z tym i utknęli w DBIC. Często zakładamy, że ludzie wiedzą, co robią w małym, ale dowiadują się w dużym, że nie.
brian d foy,

9

Aby wiedzieć, kiedy użyć czegoś, ważne jest, aby zrozumieć, jaki jest cel rzeczy. Jaki jest cel tego produktu.

DBIx :: Class jest ORM - Object-Relational Mapper. ORM pobiera struktury danych relacyjnej bazy danych oparte na relacjach / zestawach i mapuje je na drzewo obiektów. Tradycyjne mapowanie to jeden obiekt na wiersz, przy użyciu struktury tabeli jako opisu klasy. Relacje rodzic-dziecko w bazie danych są traktowane jako zawieranie relacji między obiektami.

To są jej kości. Ale to nie pomaga ci zdecydować, czy powinieneś użyć ORM. ORM są przede wszystkim przydatne, gdy spełnione są następujące warunki:

  • Używasz relacyjnej bazy danych.
  • Twoje dane są w dużej mierze wykorzystywane do OLTP (Online Transaction Processing).
  • Nie piszesz raportów w swojej aplikacji.

Największą siłą ORM jest konstruowanie dobrego SQL-a, aby przejść wykres drzewa nałożony na relacyjną strukturę danych. SQL jest często włochaty i skomplikowany, ale taka jest cena zarządzania niedopasowaniem impedancji.

Podczas gdy ORM są bardzo dobre w pisaniu SQL do ekstrakcji wierszy, są bardzo słabe w pisaniu SQL do zestawiania. Jest to typ SQL, na którym budowane są raporty. Ten rodzaj zestawiania jest budowany przy użyciu różnych narzędzi, a nie ORM.

Istnieje wiele ORM w różnych językach, kilka w Perlu. Inni twórcy map to Class :: DBI i Rose :: DB. DBIx :: Class jest często uważany za lepszy od innych, w dużej mierze na podstawie jego zestawów wyników. Jest to koncepcja, w której generowanie SQL jest oddzielone od wykonywania SQL.


Aktualizacja : W odpowiedzi na Ovid, DBIx :: Class (poprzez SQL :: Abstract) zapewnia możliwość zarówno określenia, które kolumny mają zostać zwrócone, jak i jakich wskazówek indeksu użyć.

Ogólnie jednak, jeśli chcesz to zrobić, lepiej nie używać ORM dla tego konkretnego zapytania. Pamiętaj - głównym celem ORM jest odwzorowanie wierszy w tabeli na obiekty klasy, których atrybutami są kolumny tabeli. Jeśli wypełniasz tylko niektóre atrybuty, potencjalni użytkownicy tego obiektu nie będą wiedzieli, które atrybuty są wypełnione, czy nie. Prowadzi to do przerażającego programowania obronnego i / lub ogólnej nienawiści do ORM.

Niemal zawsze chęć użycia wskazówek indeksu lub ograniczenia zwracanych kolumn jest albo optymalizacją szybkości i / lub zapytaniem agregującym.

  • Zapytania agregujące to przypadek użycia, dla którego ORM NIE są przeznaczone. Podczas gdy DBIx :: Class może tworzyć zapytania agregujące, nie tworzysz wykresu obiektowego, więc po prostu używaj DBI bezpośrednio.
  • Wykorzystywane są optymalizacje wydajności, ponieważ wyszukiwane dane są zbyt duże dla bazowej bazy danych, niezależnie od sposobu dostępu do niej. Większość relacyjnych baz danych jest idealna dla tabel zawierających do 1-3 MM wierszy z dyskami SSD, w których większość danych + indeksów mieści się w pamięci RAM. Jeśli Twoja sytuacja jest większa, każda relacyjna baza danych będzie miała problemy.

Tak, świetny DBA może tworzyć tabele z funkcją wierszy 100MM + w Oracle lub SQL * Server. Jeśli to czytasz, nie masz świetnego DBA na personel.

Wszystko to mówi, że dobry ORM nie tylko tworzy wykresy obiektowe - zapewnia także introspektywną definicję twojej bazy danych. Możesz to wykorzystać do tworzenia zapytań ad-hoc i korzystania z nich tak, jak w przypadku DBI, bez tworzenia wykresu obiektowego.


Myślę, że prawie wszystko, co widzę, zapisuje raporty, i prawdopodobnie dlatego ludzie muszą sprowadzać się do ręcznych zapytań (i to jest ból).
brian d foy,

1
Nie rozumiem, dlaczego musisz sprowadzać się do ręcznych zapytań dotyczących raportów. Zbudowałem kilka dość złożonych raportów za pomocą DBIC. Oczywiście często wiąże się to z budowaniem ogromnego, spersonalizowanego zestawu wyników z częstym użyciem „pobierania wstępnego” i „dołączania”.
Dave Cross,

Dave: ręczne SQL może być znacznie łatwiejsze do napisania i upewnienia się, że pobierasz tylko siedem potrzebnych pól z trzech tabel i reprezentujesz je w jednym rzędzie. Ponadto o wiele łatwiej jest udzielać wskazówek podczas pisania surowego SQL.
Curtis Poe,

> aby upewnić się, że pobierasz tylko siedem pól, których potrzebujesz Tak, właśnie do tego służą atrybuty „kolumny” dla wyszukiwań ResultSet. Jedynymi poprawnymi argumentami, które słyszałem lub widziałem przy robieniu surowego SQL, są: 1. Ogromnie złożone zapytania cząstkowe, które zwykle są wynikiem źle zaprojektowanej tabeli / DB 2. Uruchamianie DDL lub innych operacji „do”, których DBIC nie jest tak naprawdę zbudowany. 3. Próba włamania się do wskazówek indeksu. Ponownie, jest to raczej słabość RDBMS, ale czasami trzeba to zrobić. Możliwe jest po prostu dodanie tego rodzaju funkcjonalności do DBIC.
Brendan Byrd

8

Jako jeden z głównych programistów platformy e-commerce Interchange6 (i podstawowej piły łańcuchowej schematu) mam dość dogłębne doświadczenie z DBIC. Oto kilka rzeczy, dzięki którym jest to świetna platforma:

  • Generator zapytań pozwala napisać jeden raz dla wielu silników baz danych (i wielu wersji każdego z nich). Obecnie obsługujemy Interchange6 z MySQL, PostgreSQL i SQLite i dodamy obsługę większej liczby silników, gdy tylko zdobędziemy czas i zasoby. Obecnie w całym projekcie są tylko dwie ścieżki kodu, które mają dodatkowy kod uwzględniający różnice między silnikami, co wynika wyłącznie z braku konkretnej funkcji bazy danych (brakuje miejsc SQLite) lub z powodu idiotyzmu MySQL, który się zmienił sposób, w jaki jego funkcja LEAST obsługuje wartości NULL między dwiema mniejszymi wersjami.

  • Wstępnie zdefiniowane zapytania oznaczają, że mogę budować proste metody, które można wywoływać (z argumentami lub bez) z kodu aplikacji, dzięki czemu moje zapytania są głównie zawarte w definicji schematu zamiast zaśmiecać kod aplikacji.

  • Komponowalne generowanie zapytań umożliwia podział zapytań na małe, zrozumiałe, predefiniowane zapytania, a następnie połączenie ich w celu utworzenia złożonych zapytań, które byłyby trudne do utrzymania w długim okresie w DBIC (jeszcze gorzej w czystym SQL), gdyby zostały zbudowane w jednym kroku.

  • Schema :: Loader pozwolił nam używać DBIC ze starszymi aplikacjami, dając nowy okres życia i znacznie prostszą ścieżkę do przyszłości.

  • Wtyczki DBIC, DeploymentHandler & Migration ogromnie dodają się do zestawu narzędzi, które upraszczają moje życie.

Jedną z ogromnych różnic między DBIC i większością innych platform podobnych do ORM / ORM jest to, że chociaż stara się prowadzić Cię przez sposób robienia rzeczy, nie powstrzymuje Cię również od robienia szalonych rzeczy, które lubisz:

  • Możesz korzystać z funkcji SQL i procedur przechowywanych, których DBIC nie zna, po prostu podając nazwę funkcji jako klucz w zapytaniu (może również prowadzić do dobrej zabawy, gdy spróbujesz użyć NAJMNIEJ w MySQL ^^, ale to nie wina DBIC) .

  • Dosłowny SQL może być użyty, gdy nie ma „sposobu DBIC” na zrobienie czegoś, a zwracany wynik jest nadal zawinięty w ładne klasy z akcesoriami.

TL; DR Prawdopodobnie nie zadałbym sobie trudu, aby użyć go do naprawdę prostych aplikacji z zaledwie kilkoma tabelami, ale kiedy muszę zarządzać czymś bardziej złożonym, szczególnie gdy kluczowa jest kompatybilność między silnikami i długoterminowa konserwacja, wtedy DBIC jest ogólnie moja preferowana ścieżka.


7

(Zanim zacznę, powinienem powiedzieć, że to po prostu porównuje opakowania DBI, DBI i oparte na Mojo. Nie mam doświadczenia z żadną inną Perlową ORM, więc nie będę komentował ich bezpośrednio).

DBIC robi wiele rzeczy bardzo dobrze. Nie jestem jej dużym użytkownikiem, ale znam teorię. Wykonuje całkiem niezłą robotę w zakresie generowania SQL, a zwłaszcza (jak już powiedziano) obsługi sprzężeń itp. Może także całkiem ładnie pobierać inne powiązane dane.

Główną zaletą, jaką widzę, jest możliwość BEZPOŚREDNIEGO wykorzystania wyników jako klasy modelu. Jest to również znane jako „dodawanie metod zestawu wyników”, w którym można uzyskać wyniki i wywołać metody na tych wynikach. Typowym przykładem jest pobranie obiektu użytkownika z DBIC, a następnie wywołanie metody w celu sprawdzenia, czy hasło jest prawidłowe.

Pewne wdrożenie schematu może być trudne, ale zawsze jest trudne. DBIC ma narzędzia (niektóre w modułach zewnętrznych), które ułatwiają i prawdopodobnie łatwiej niż ręczne zarządzanie własnymi schematami.

Po drugiej stronie monety znajdują się inne narzędzia, które odwołują się do innych wrażliwości, takich jak opakowania DBI o smaku mojo. Mają urok bycia szczupłym, a jednak wciąż użytecznym. Większość z nich również wzięła wskazówkę z Mojo :: Pg (oryginał) i dodała przydatne funkcje, takie jak zarządzanie schematem w plikach płaskich i integracja z pubsub.

Te moduły o smaku Mojo wyrosły z jednego innego słabego punktu DBIC, a mianowicie, że nie jest (jeszcze) zdolny do wykonywania asynchronicznych zapytań. Autorzy zapewniali mnie, że jest to technicznie możliwe, być może nawet szybko, ale istnieją problemy z zaprojektowaniem odpowiedniego interfejsu API. (Trzeba przyznać, że nawet poproszono mnie o pomoc i chociaż tak zrobię, po prostu nie wiem, jak poruszyć igłą w czasie, który muszę temu poświęcić).

TL; DR używaj DBIC, chyba że kochasz SQL lub potrzebujesz asynchronizacji, w takim przypadku sprawdź opakowania DBI o smaku Mojo.


6

Moje przemyślenia na ten temat napisałem w DBIC vs. DBI trzy lata temu. Podsumowując, wymieniłem dwa główne powody:

  1. DBIC oznacza, że ​​nie muszę myśleć o całym trywialnym SQL, który jest potrzebny do prawie każdej aplikacji, którą piszę.
  2. DBIC zwraca mi obiekty z bazy danych zamiast głupich struktur danych. Oznacza to, że mam wszystkie standardowe zalety OO do zabawy. W szczególności uważam, że bardzo przydatne jest dodawanie metod do moich obiektów DBIC.

Jeśli chodzi o anty-wzory, to jedyne, co mogę wymyślić, to wydajność. Jeśli naprawdę chcesz wycisnąć każdy cykl zegara z procesora, być może DBIC nie jest odpowiednim narzędziem do tego zadania. Ale z pewnością w przypadku aplikacji, które piszą, przypadki te są coraz rzadsze. Nie pamiętam, kiedy ostatni raz napisałem nową aplikację, która rozmawiała z bazą danych i nie używała DBIC. Oczywiście pomaga, jeśli wiesz trochę o dostrajaniu zapytań generowanych przez DBIC.


2
Nie mogę naprawić literówek, ponieważ nie zmieniam wystarczającej liczby znaków („righ ttool”). Ciekawie kulawy. Ale to mnie zastanawia. Myślę, że w twoim poście PerlHacks odnosisz się do jednej rzeczy wskazanej przez Roba, ale nie rozważ drugiej. W wielu przypadkach ludzie wracają do ręcznego SQL.
brian d foy,

1

Sposób, w jaki go skaluję:

  1. utwórz jedną klasę, która udostępnia konstruktor gniazda DBI i metody testowania.

  2. wyprowadzić tę klasę do klas zapytań SQL (jedna klasa na tabelę sql) i przetestować gniazdo w czasie konstruktora.

  3. użyj zmiennych o zasięgu klasowym do przechowywania nazwy tabeli i nazw kolumn indeksu podstawowego.

  4. Napisz wszystkie swoje tabele interpolujące SQL i główną kolumnę indeksu z tych zmiennych zamiast definiować je statycznie w SQL.

  5. użyj makr edytora, aby umożliwić tworzenie podstawowych par metod DBI (przygotowanie i wykonywanie) podczas wpisywania TYLKO instrukcji sql.

Jeśli możesz to zrobić, możesz pisać czysty kod API na DBI przez cały dzień ze względną łatwością.

Przekonasz się, że wiele zapytań będzie można przenosić na wiele tabel. W tym momencie możesz wyciąć i wkleić do klasy EXPORTER i wrzucić je tam, gdzie to konieczne. Tutaj zaczyna się interpretacja klasowa interpolacji nazwy tabeli i nazw głównych kolumn indeksu.

Zastosowałem to podejście do skalowania do setek metod DBI o stosunkowo dobrej wydajności. Nie chciałbym próbować utrzymywać kodu DBI w żaden inny sposób.

Kiedy używać DBI: Zawsze.

Nie sądzę jednak, żeby to było twoje prawdziwe pytanie. Twoje prawdziwe pytanie brzmiało: „To wygląda jak wielka PITA, proszę powiedz mi, że nie muszę tego robić?”

Ty nie. Rozłóż to poprawnie, a część DBI staje się wystarczająco redundantna, aby móc ją w większości zautomatyzować.


Czy jest jakaś szansa, że ​​masz projekt open source, którym możesz się podzielić, a może po prostu gistub na githubie z przykładem każdej klasy? Myślę, że pomysły, które mówisz, są interesujące i prawdopodobnie byłyby wykonalne dla projektów wielu ludzi, ale byłoby trochę łatwiej zacząć od kilku przykładów.
msouth

0

Nie jestem ekspertem od Perla, ale często go używam. Jest wiele rzeczy, których nie wiem lub nie wiem, jak zrobić lepiej; pewnych rzeczy, których po prostu nie jestem jeszcze w stanie zrozumieć, pomimo dokumentacji.

Zaczynam od DBI, ponieważ myślę: „Och, to prosty projekt, nie potrzebuję nadmiaru ORM i nie chcę mieć problemów z konfiguracją i modułami schematu”. Ale bardzo szybko - prawie za każdym razem - szybko zaczynam przeklinać się za tę decyzję. Kiedy chcę zacząć być kreatywny w moich zapytaniach SQL (zapytania dynamiczne, a nie tylko symbole zastępcze porównania), staram się zachować zdrowie psychiczne przy użyciu DBI. SQL :: Abstract bardzo pomaga i zazwyczaj to mi wystarcza. Ale moja kolejna mentalna walka polega na utrzymywaniu dużej ilości kodu SQL w moim kodzie. Bardzo rozpraszające jest dla mnie to, że linie i wiersze osadzonego SQL są wewnątrz brzydkich heredoków. Może muszę użyć wysokiej jakości IDE.

W końcu, częściej niż nie, trzymam się prostej DBI. Ale wciąż żałuję, że nie ma lepszego sposobu. DBIx :: Class ma kilka naprawdę fajnych cech i korzystałem z niego kilka razy, ale wydaje się tak przesadny w przypadku wszystkich projektów poza największymi. Nie jestem nawet pewien, które zarządzanie jest bardziej kłopotliwe: DBI z rozproszonym SQL lub DBIC z rozproszonymi modułami Schema.

(Och, takie rzeczy jak ograniczenia i wyzwalacze to dla mnie ogromny plus dla DBIC.)


4
Ta odpowiedź nie trafia w sedno - na pewno masz problemy podczas korzystania z DBIx, ale dlaczego masz takie problemy? Czy DBI nie jest elastyczne, zbyt ściśle powiązane z DB, skalowalne, czy co?
Jay Elston,
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.