fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
8 minut

Kilka słów o tabelach w obiektowym świecie – ORM czy SQL?


24.09.2018

Jakie języki programowania przewijają się najczęściej w ofertach pracy? Cokolwiek, w czym jesteś w stanie stworzyć jakiś back-end, nieśmiertelny JS i niemalże równie wszechobecny SQL tego lub innego sortu.

Na rozmowie czasem zdarzy nam się odpowiedzieć na pytanie dotyczące where’ów, selectów i innych joinów i… bywa, że jest to ostatni raz, gdy masz do czynienia w SQL-em w tej konkretnej firmie.

Słowem wstępu

Założę, że programujesz obiektowo. W takim razie niemalże na pewno zdarzyło Ci się stanąć przed wyborem bibliotek w momencie tworzenia rozwiązania lub przeglądać stos technologiczny istniejącego projektu. Myślę, że się nie pomylę, jeżeli napiszę, że w większości, jeśli nie we wszystkich przypadkach przed Twoimi oczami mignął jakiś ORM. Czasem okazuje się też, że SQL jest wymagany tylko i wyłącznie dlatego, że ten konkretny maper stoi nad tą, a nie inną technologią bazodanową.

Pozwól jednak, że nieco odejdę od tematu i się przedstawię, bo prawdopodobnie nie mieliśmy jeszcze okazji się poznać. Nazywam się Rafał Hryniewski i na co dzień pracuję z technologiami Microsoftu – głównie o nich będę Ci opowiadał. Goszczę tu po raz drugi, wliczając cykl „Wasze Historie”. Tym razem planuję zostać na dłużej i zająć się tematyką baz danych z perspektywy programisty obiektowego lub osoby, która nigdy wcześniej nie pracowała z bazą danych. Pokażę Ci, jak ogarnąć podstawy, jakie narzędzia masz do wyboru i jakie są ich mocne oraz słabe strony, jak podejrzeć i zabrać się za optymalizację tego, co wypluje z siebie Entity Framework, i wiele innych rzeczy, o których możesz nie wiedzieć, jeśli za bardzo nie wychylasz się zza używanego przez Ciebie ORM-a. Postaram się, by co jakiś czas dać Ci coś ciekawego do przeczytania niezależnie od tego, czy dopiero zaczynasz z bazami danych, czy masz już z nimi trochę doświadczenia.

Ale ja programuję obiektowo!

Wróćmy do tematu. Moim zdaniem bardzo mocno zaniedbaliśmy znajomość technologii bazodanowych. Pewnie, możemy napisać parę klasek, nadać kilka atrybutów, odpalić migrację i wszystko śmiga, baza stoi, ma nawet jakąś sensowną strukturę. To o wiele szybsze rozwiązanie niż składanie tabel do kupy ręcznie, zastanawianie się nad typami danych dla każdej kolumny i różne inne „pierdoły”. Podobnie sprawa ma się z pisaniem zapytań do naszej bazy – niby po co mielibyśmy męczyć się z czystym SQL-em w stringach, skoro możemy użyć doskonale znanego nam LINQ (w przypadku C#)?

Programujesz obiektowo? Jeśli tak, to jestem przekonany, że czujesz się o wiele bardziej komfortowo w świecie obiektów i kolekcji niż tabel i rekordów. Mogło Ci się też zdarzyć strukturyzować dane w bazie, myśląc o nich w kontekście klas, kolekcji i biorąc pod uwagę zasady pisania czystego kodu. Jak jednak mają się dobre praktyki obiektowe do dobrych praktyk bazodanowych? Najczęściej nijak i jeśli tylko wszystko będzie działać, to gdy liczba rekordów skoczy nam do kilkuset tysięcy lub więcej, możemy się obudzić z poważnymi problemami z wydajnością.

W SQL-u też należy dbać o porządek!

Wspomniałem o czystym kodzie. Niepokojącym mnie zwyczajem jest bezpośrednie mapowanie tabel na model obiektowy i działanie bezpośrednio na nim. Czy jest w tym coś złego? A nie brakuje Ci może jakiejkolwiek sensownej warstwy abstrakcji między bazą danych a DAL-em (data access layer)? Kilka drobnych zmian w bazie i dane mogą przestać się modyfikować albo odczytywać.

Co możemy zrobić? Tak samo jak w kodzie obiektowym posługujemy się np. interfejsami do wystawienia publicznych metod, tak w SQL-u możemy skorzystać z trzech różnych widoków zawierających wyłącznie dane specyficzne dla – powiedzmy – użytkownika, klienta i adresata paczki, używając w nich jednej tabeli Users. Podobnie możemy zrobić z przeróżnymi procedurami i funkcjami, a wszystko to ładnie zmapować w naszym ORM-ie.

Czy jest to bardziej pracochłonne? Tak. A co zyskujemy? Przede wszystkim to, że tak długo, jak nasz widok czy funkcja poprawnie zwraca dane w określonej strukturze, nie musimy się martwić o tym, jak to wszystko składane jest pod spodem do kupy. Mieliśmy plątaninę joinów, którą zastąpiliśmy implementacją grafów w SQL Server? Wystarczy, że przepiszemy kod naszego widoku i wszystko pozostanie w jak najlepszym porządku. Ba, możemy sobie pozwolić na całkiem spore optymalizacje, denormalizację w celu uproszczenia formy odczytywanych danych czy co jeszcze przyjdzie nam do głowy. A wiesz, co jest najpiękniejsze? Widoki, procedury i funkcje same w sobie na ogół nie przechowują danych, ich modyfikacje są błyskawiczne i nie wymagają zatrzymywania aplikacji.

Świat, którego nie znasz

Sam SQL zawiera mnóstwo narzędzi, które mogą Ci znacząco ułatwić życie. Zdarzyło Ci się kiedyś pisać skomplikowane wersjonowanie treści w kodzie obiektowym? SQL Server 2016 i wyższe mają funkcję zwaną Temporal Tables, która ogarnia wersjonowanie out of the box. Podobnie ma się sprawa z aplikacjami multi-tenant i row level security, maskowaniem danych, konstruktami znanymi z grafowych baz danych czy otwieraniem JSON-ów jako tabele. SQL potrafi zrobić naprawdę bardzo dużo z naszymi danymi, trzeba się z nim tylko troszeczkę zapoznać i przede wszystkim zgłębić jego możliwości.

O praktycznie każdym z elementów, które wymieniłem gdzieś wyżej, można stworzyć oddzielny wpis. O niektórych nawet kilka. Dlatego też nie opiszę ich wszystkich w tym miejscu – jeśli ciekawi Cię jakiś konkretny temat, daj znać w komentarzu, a postaram się napisać o tym coś więcej. Chciałem Ci tylko pokazać, że na tym poziomie istnieje naprawdę wiele rzeczy, o których można nie wiedzieć, jeśli wszystko przesłaniają Ci różowe okulary z ORM-ów. Nieco więcej na ten temat opowiadałem chociażby na swojej sesji na tegorocznym 4 Developers, nagranie możesz obejrzeć poniżej, a z prezentacją pewnie od czasu do czasu wyskoczę w Polskę, więc może nawet spotkamy się na żywo.

To w końcu używać czy nie?

Tytuł tego wpisu jest pytaniem, a ja wciąż nie udzieliłem na nie odpowiedzi. Wychwalam SQL-e pod niebiosa, płaczę, jakie te ORM-y są ubogie. Myślę, że doskonale wiesz, że odpowiedź brzmi jak zwykle: to zależy. Ja osobiście niemalże nie używam ORM-ów w swoich projektach, ale… czasem mi się zdarza i pracuję z nimi na co dzień, więc przydaje mi się wiedza o tym, co dokładnie leci do bazy danych po użyciu kilku metod w LINQ.

Używanie zarówno ORM, jak i czystego SQL ma swoje wady i zalety. Development np. z takim Entity Frameworkiem jest niesamowicie szybki, a szlifowanie procedur i widoków z każdej zbędnej milisekundy z pewnością nie jest zadaniem dla każdego (i – co ważniejsze – istotnym w każdym projekcie!) i może sprawić, że terminy realizacji staną się nierealne. Oba podejścia mają swoje wady, ale za najgorsze z nich uważam to trzecie, w którym zamykamy się na jakąś opcję dla zasady. „ORM to smutny syf i nie użyję go nawet do CRUD-a”, ”Nie dotknę SQL-a – jestem programistą obiektowym”.

Na tytułowe pytanie musisz odpowiedzieć samodzielnie. Pamiętaj jednak, że jeśli dobrze skonstruujesz swojego SQL-a, to raczej nie zaistnieje potrzeba przepisania go na jakiegokolwiek ORM-a, zwłaszcza jeśli zadbasz o odpowiednią abstrakcję po stronie bazy danych. I myślę, że to tego tematu możesz się ode mnie spodziewać w najbliższym czasie – jakich narzędzi użyć, by zbudować solidną warstwę abstrakcji między Twoją aplikacją a Twoją bazą.

To… o czym chcesz poczytać w przyszłości?

0 0 votes
Article Rating
37 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jerzy Wickowski
6 years ago

Coś czuję, że kolejne Twoje wpisy będą zdroworozsądkowo sensowne, ale z drugiej strony mięsisto techniczne. Dlaczego? Mam nadzieję, że uda Ci się spełnić takie oczekiwania :).

Maciej Aniserowicz
Admin
6 years ago

Wow Rafał widzisz? :) Super, znając Rafała: tak właśnie będzie!

Kamil
Kamil
6 years ago

W IT pracuje już ponad 10 lat i zawsze jak tylko przejmowaliśmy jakiś projekt to był problem z wykorzystywaniem ORM do granic możliwości byleby tylko SQLa nie pisać. Zapytania, które na bazie DEV działały 1 sekundę (mało danych) na produkcji potrafiły wykonywać się ponad 60 sekund; oczywiście nie wyszło to od razu, ponieważ początkowo produkcja tez miała mało rekordów, ale już po roku…

Co więcej, jeden z największych minusów pisania zapytań tylko z poziomu ORMu – moim zdaniem – to brak możliwości prostego sprawdzenia jaki proces biznesowy w najbardziej obciąża serwer bazodanowy. Zapytania generowane przez ORM bardzo ciężko jest zdekodować, czasami to są monstra na parę stron… człowiek potem musi się habilitować, żeby namierzyć fragment kodu do optymalizacji.

Historii można by mnożyć. Od lat obserwuje trend, w którym coraz mniej się troszczymy od bazy danych, a przecież jak się spier***i bazę danych to potem nawet Summit (TOP 500) nie pomoże…

Jacek Brożyna
Jacek Brożyna
6 years ago
Reply to  Kamil

Zgadzam się w 100%. Często programiści przyzwyczajają się do używania ORMów w prostych projektach, a jak przychodzi działać z bazą mającą po kilkadziesiąt milionów rekordów i wyciągać dane z kilku tabel, okazuje się, że to nie działa… Nie po to firmy rozwijają bazy danych, żeby później programista używał tylko tabel, a widoki generował za nie jakiś ORM. Niestety, ale spotkałem się z opinią, że klient dokupi droższy serwer, zrobi klaster, jak system nie będzie dawał rady, bo to tańsze niż praca programisty… Problem w tym, że przy naprawdę dużych systemach, kiepsko napisany kod obsługujący bazę danych zwiększy tak bardzo koszty infrastruktury, że taniej będzie zatrudnić firmę, która zoptymalizuje wszystko jak trzeba :)

Marcin
Marcin
6 years ago

Ja swego czasu, gdy nie umiałem jeszcze dobrze EF-a to wszystkie zapytania pisałem w SQL-u lub procedurach czy funkcjach.
Teraz większość piszę w EF-ie, ale nie które bardziej skomplikowane zapytania piszę w procedurach i wywołuje za pomocą EF-a, tudzież robię widoki w sql-u ale też wywołuje w EF-ie.
Czy to jest słuszne podejście?, nie wiem. Ja pracuje w ten sposób bo jest to dla mnie łatwiejsze.
Może dzięki Twoim artykułom dowiem się (w końcu) jak można podejść do tego zagadnienia (ORM i/lub SQL).
Czekam na więcej.

Michał Kuliński
6 years ago

ORM or not to ORM, this the QUERY.

Michał Kuliński
6 years ago

To świetnie Rafał, że Twój główny przekaz brzmi: “to zależy.” Profesjonalny twórca oprogramowania zna przynajmniej najpopularniejsze rozwiązania do persystencji danych i wie kiedy ich użyć i wie kiedy ich nie użyć. Ostrożnie podchodzi do solennych zapewnień marketingowców tej czy innej firmy. A już na pewno trzyma swój dostęp do danych za interfejsami, żeby w zależności od kaprysu raz użyć Azura, raz SQL Serwera, a innym razem płaskich plików na dysku C.

Kamil Tkacz
Kamil Tkacz
6 years ago

W poprzedniej firmie, gdzie pracowałem ogólnie brzydzili się SQL-em i tylko ORM-o rządziło. W obecnym miejscu pracy mam naokoło takich magików od SQL-a, że EF dawno nikt nie widział. Większość operacji jest wykonywana na SQL-u a C# służy praktycznie tylko do “wyplucia” tego co procedury zmieliły. Z jednej skrajności w drugą ;)
Z perspektywy czasu, widząc efekty obu podejść sądzę, że działanie na SQL-u daje większą kontrolę i przejrzystość. Czekam na dalsze spisy ;)

Magda
6 years ago

Bardzo ciekawy artykul-moze dlatego ze moja dzialka;)
Chetnie sie dowiem:
1. jak zrobic warstwe posrednia miedzy klasa a baza danych
2. jak I czy wogole zastapic ORM SQLem [jest to moje preferowane podejscie-bo SQL jest dla mnie banalnie prosty I faktycznie cuda na kiju mozna zrobic;)]
3. jakie metody zastosujesz do walidacji danych I zapewnienie im spojnosci [np. przy kasowaniu]

Kamil Grzybek
6 years ago
Reply to  Magda

ad 3)
Zależy co walidujemy :) Najlepiej mieć walidację na każdej “warstwie” aplikacji:

a) gdy sprawdzamy dane wprowadzone przez użytkownika np. czy kwota jest nieujemna (logika aplikacji)
b) gdy sprawdzamy reguły biznesowe np. czy użytkownikowi należy się rabat (logika biznesowa)
c) gdy sprawdzamy integralność i spójność danych

Najczęściej a) i b) realizuje się w kodzie aplikacji. Można skorzystać do tego z gotowych bibliotek np. FluentValidation.

c) najczęściej jest realizowane w postaci zakładania constraintów na bazie. Czasami jednak jest to niemożliwe – np. w przypadku mikroserwisów, gdy każdy serwis posiada własną bazę danych i niemożliwe jest zakładanie FK pomiędzy bazami.

Dariusz Lenartowicz
Dariusz Lenartowicz
6 years ago

“CQRS to the rescue”. Po stronie Command ORM doskonale wczytuje nam pełny agregat, nie mniej – nie więcej. Po stronie Query proste zapytania można puszczać przez ORMa, ale w sumie proste query można szybko w SQLu napisać, natomiast cięższe query już tylko czysty SQL (wydajne i skrojone na daną potrzebę zapytanie). Zakładając, że mamy do czynienia z dwoma modelami w kodzie, nawet jeśli pod spodem jest jeden model bazodanowy można osiągnąć przyzwoitą szybkość działania jak i czystość kodu.

Radosław Maziarka
6 years ago

Dokładnie – ORM jest dobrze stosować po stronie zapisu, ale często nie daje rady po stronie odczytu, wtedy dobrze zastosować SQLa.

Kamil Grzybek
6 years ago

Wprowadzenie CQRS działającego na jednym modelu bazodanowym jesteśmy w stanie wprowadzić wszędzie, bo koszt takiego rozwiązania zawsze będzie niższy niż rozwiązania bez CQRS.

ORM jest dobry do zapisu, bo śledzi zmiany w encjach i nie trzeba pisać cały czas podobnego (trywialnego) kodu. Dodatkowo jest dobry do prostych zapytań, dlatego świetnie się sprawdza w pobieraniu agregatów, bo są to najczęściej proste joiny do tabel po FK.

Surowy SQL jest dobry do pobierania danych, bo umożliwia pełną kontrolę nad tym jak zapytanie wygląda i co zwraca, co przy większych systemach i wolumenach danych jest kluczową kwestią jeśli chodzi o wydajność. Odradzam jednak pisanie SQLi w czystym ADO.NET a polecam użycie jakiegoś microORM’a np Dappera, który działa prawie tak samo szybko jak ADO a nie musimy klepać SqlReaderów i innych klas znajdujących się zdecydowanie na niższym poziomie abstrakcji.

Wyobrażam sobie tylko jeden przypadek, gdzie architektura aplikacji powinna iść w jedną stronę (ORM lub surowy SQL) – gdy nasz zespół nie zna jednego z podejść. Jest to wg mnie jednak sytuacja marginalna, gdyż moim zdaniem każdy programista backend’owy powinien znać bardzo dobrze zarówno ORM (ogólnie, nie konkretny framework – zasady są te same), SQL i bazy danych (przynajmniej te relacyjne).

Dariusz Lenartowicz
Dariusz Lenartowicz
6 years ago

Tak jak napisał Kamil cqrs jest trywialny. Pooglądaj video Maćka na ten temat. :)

Jacek
Jacek
6 years ago

@Dariusz Lenartowicz
CQRS jest trywialny tylko w takich trywialnych przykładach jak Maćka.

Jacek
Jacek
6 years ago

A kto komu zabrania mieszać w różnych miejscach aplikacji ORM z SQLem?
Może nie “ORM czy SQL” a kiedy jedno a kiedy drugie bo to się nie wyklucza. Zwłaszcza przy rozsądnej architekturze.
Wynik z SQLa i tak często (zwykle) trzeba przerobić na jakieś obiekty.

Dariusz Lenartowicz
Dariusz Lenartowicz
6 years ago
Reply to  Jacek

Od transformacji danych z bazy na “jakieś obiekty” są tzw. MicroORMy (nie mylić właśnie z “pełnymi” ORMami). :)

Jacek
Jacek
6 years ago

A dlaczego? Poza wydajnością ale to trzeba by sprawdzić.
Masz EF DbContext bez żadnych dbsetów
Pytasz tylko sqlem, który mapuje na obiekty. Będzie dużo wolniej niż jakimś mikro?

Adrian
Adrian
6 years ago

Czekam na więcej artykułów. Ostatnio bardzo mnie interesuje temat ORMów i chętnie poczytam coś więcej na ten temat. Stosunkowo niedawno dowiedziałem się o tym. Jakiś czas temu miałem okazję poznać Entity Framework, teraz zapoznaję się z Jpa i springiem. Bardzo fajny tekst

Pan Z
Pan Z
6 years ago

Dobry artykuł, ciekawie się czyta, a i niesie ze sobą dobrą dawkę wiedzy :)

Odpowiadając na pytanie z artykułu, czym byłbym zainteresowany, to otwieraniem JSON-ów jako tabele. Brzmi ciekawie, a nie spotkałem się jeszcze z tym. Słyszałem o w spraciu dla jsonów w Postgres’ie, ale to wydaje mi się nie to :)

Jacek
Jacek
6 years ago
Reply to  Pan Z

Ale po co?

Jacek
Jacek
6 years ago
Reply to  Jacek

Ja po wysłaniu to się chwilę zastanawiałem o co chodzi z “otwieraniem JSONów jako tabele”.
Chyba założyłem, że chodzi o zserializowanie tabeli do JSON-a.

To co piszesz to raczej jest jakieś Object DB. Ja rozumiem sens zapisu JSONów nawet np logowania całej komunikacji przychodzącej tylko nie w tabelach i raczej bez SQL-a.

Oskar Dudycz
6 years ago

A dlaczego niby nie powinny być? Postgres świetnie wspiera JSON. Polecam zapoznać się z Martenem (https://github.com/JasperFx/marten). Zamienia Postgres w bazę dokumentową zachowując możliwość dalszego korzystania z uroków baz relakcyjnych (transakcyjność) jeśli trzeba oraz da się integrować z EFem. Takie podejście zmniejsza konieczność korzystania z custom sql oraz pozbywa nas większości boilerplate ORMów.

Czy masz jakieś konkretne dane, że takie podejście w Postgres jest mało wydajne? Bardzo bym wdzięczny za takie dane jako użytkownik i kontrybutor Martena ;) Mała sugestia z mojej strony: przed wydaniem kategorycznych sądów, szczególnie występując z pozycji “eksperta” dobrze zbadać sprawę, a nie mówić z głowy, bo można ludzi wprowadzić w błąd.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również