fbpx

Doctrine fetch mode

Doctrine fetch mode

O Doctrinie pisałam już nie raz i pewnie jeszcze wiele razy napiszę.  To potężne narzędzie, które usprawnia pracę z bazą danych, ale ma też sporo wad. W tym tekście będzie wiele nawiązań do encji oraz do relacji między nimi. Dzisiaj chciałabym opowiedzieć Ci nieco więcej o doctrine fetch mode, czyli trybach pobierania, które możemy konfigurować w każdej relacji między encjami. Więcej szczegółów w dalszej części.

Co to?

Czym jest ORM, już pewnie wiesz. Jego głównym zadaniem jest przetwarzanie danych z bazy na obiekty. Proces ten nazywany jest hydracją. Po odczytaniu rekordu poszczególne kolumny są mapowanie na zmienne klasowe obiektu.  W przypadku gdy występuje relacja z innym obiektem, ORM doczytuje kolejnym zapytaniem dane odpowiednie obiektowi, który występuje w relacji do tego, którym się zajmujemy i wykonuje tę samą pracę z mapowaniem. Jeśli ten drugi obiekt posiada relacje, to ponownie robi to samo dla kolejnej relacji w głąb i tak w kółko.

Zastosowanie

Dopóki obiekty są proste i nie zawierają zbyt wielu relacji, nie powinniśmy mieć większych problemów z wydajnością systemu. Jednak przy wielkich obiektach, które posiadają relacje do innych, wielkich obiektów może się okazać, że nagle zamiast jednego zapytania, aby odczytać obiekt, trzeba wykonać ich kilkadziesiąt. Tutaj zaczynają się schody. Aplikacja coraz wolniej działa, tworzą się zatory, wydajność zaczyna spadać na łeb na szyję.

Co robić w takiej sytuacji? Opcji jest kilka, najlepszą jest przeanalizowanie zależności między obiektami i próba refaktoryzacji, która znacznie je uprości. W idealnym świecie właśnie tak powinieneś się zachować. Jednak dobrze wiemy jak jest. Klient się denerwuje, procesy zaczynają się wywalać, blokując pracę. Nie ma czasu na analizy i refaktoryzacje, trzeba działać już, natychmiast.

W takiej sytuacji mogą pomóc nam tryby pobierania. Jednak pamiętaj, jest to fajna funkcja, ale nie opieraj całej optymalizacji na tym rozwiązaniu, ponieważ nie zaprowadzi Cię to daleko. Warto ustalić tryby pobierania w aplikacji świadomie, jednak często są one jedynie ugraniem sobie czasu do powrotu problemów z wydajnością. W większości przypadków nie są rozwiązaniem docelowym a jedynie pomocniczym.

Doctrine fetch mode

We wszystkich typach relacji mamy dwa możliwe tryby:

  • EAGER
class Address
{
/**
 * @OneToOne(targetEntity="Event", fetch="EAGER")
 * @JoinColumn(name="event_id", referencedColumnName="id")
 */
private $event;
}

Ustawienie tego trybu oznacza, że jeśli wczytujesz obiekt Address,  który posiada relację do obiektu Event, to obiekt Event również zostanie od razu wczytany w całości.

  • LAZY
class Product
{
   /**
    * @ManyToOne(targetEntity="Cart", cascade={"all"}, fetch="LAZY")
    */
   private $cart;
}

Tryb ten powoduje, że po wczytaniu obiektu Product będzie on posiadał zmienną cart, która będzie obiektem typu proxy. Dopiero po wywołaniu $product->getCart() zostanie dociągnięta informacja o koszyku. Dzięki temu, jeśli działamy tylko na cechach obiektu, które nie wynikają z relacji, zmniejszamy ilość zapytań przy startowym wczytaniu obiektu, aby potem w razie potrzeby tylko doczytywać poszczególne relacje.

W przypadku relacji ManyToMany i OneToMany mamy jeszcze jedną, dodatkową opcję czyli tryb:

  • EXTRA_LAZY
class User
{
   /**
    * @OneToMany(targetEntity="PhoneNumber", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true, fetch="EXTRA_LAZY")
    */
   private $phoneNumbers;
}

Zauważ, że w obu tych relacjach zmienna przechowuje kolekcję. Ten tryb, jak nazwa wskazuje, że jest podobny do LAZY. Również doczytuje wartości wynikające z relacji, ale idzie o krok dalej. Wczytując klasę User pod zmienną phoneNumbers, będziemy mieć obiekt Persistance Collection, czyli coś podobnego do obiektu Proxy. Jeśli wywołamy $user->getPhoneNumbers() zmieni się on w Collection, jednak obiekty wewnątrz będą obiektami proxy. Dopiero po wczytaniu poszczególnych elementów będą one pojedynczo dociągane. Stąd możemy ustawiać ten tryb tylko na tych typach relacji, gdzie zmienna przechowuje kolekcje.

Z tym trybem trzeba jednak bardzo uważać, ponieważ nie każda próba dostania się do elementu kolekcji lub dodania nowego wywoła doczytanie elementów np.

$phoneNumbers->add($newPhoneNumber);

$phoneNumbers[] = $newPhoneNumber;

$phoneNumbers[0] = $newPhoneNumber;

W trybie EXTRA_LAZY powyższe próby dodania elementu skończą się fiaskiem.

Mam nadzieję, że udało mi się Ci trochę pomóc z problemem wydajności i podrzucić nowe rozwiązanie. Jeśli zaciekawił Cie temat doctrine fetch mode i chciałbyś go pogłębić to polecam dokumentację. Może masz jakieś pytania? Pisz śmiało pod tym postem.

Zgarnij darmowy ebook i cotygodniową dawkę wiedzy

.
Magdalena Limanówka-Kuciel
magdalena@panizkomputerem.pl

Jestem programistką, która lubi mieć ręce pełne roboty. Do życia potrzebuje komputera z internetem i kubka gorącej kawy. Więcej na stronie o mnie.