Cześć! Witaj w kolejnym wpisie z Hibernate.
Zanim jednak do tego przejdę… W naszym newsletterze w ostatniej wiadomości prosiłem naszą małą społeczność o udział w ankiecie na temat Programu Java Developera. Odzew był MEGA 💪. Zaskoczyło nas to, jak dobrze został przyjęty! Dzięki!
Dopinamy ostatnie kwestie techniczne, rendery materiałów, wprowadzamy poprawki po review i za kilka tygodni oddamy w Twoje ręce Program Java Developera 🚀
12 modułów udostępnianych tydzień po tygodniu, 40 godzin lekcji video, sporo fajnych technologii i DOŻYWOTNI dostęp do materiałów.
- Pełną agendę znajdziesz TUTAJ 🔥
- A zapisać się możesz TUTAJ już dzisiaj – damy Ci znać, kiedy startujemy i dostaniesz ofertę z najlepszą ceną ever!
Do rzeczy, co z tymi anotacjami!
Wstęp
Korzystając z Hibernate czy też Java Persistence API dla konfiguracji mapowania danych i operacji przeważnie korzysta się z anotacji (jest także możliwość konfiguracji poprzez pliki XML). Korzystasz z @Entity
, @Id
dla definicji encji i ewentualnie odpowiedników @Table
i @Column
dla wskazania miejsca w bazie danych dla tych danych.
Idąc dalej, skorzystasz z auto-generacji identyfikatorów @GeneratedValue
, mapowania danych z wykorzystaniem @Convert
, @Lob
, @Enumerated
czy @Temporal
. Możesz zdefiniować relacje, użyjesz @Query
/@NamedQuery
czy @NativeQuery
dla definicji zapytań.
Ale zarówno Hibernate jak i JPA dostarczają wielu dodatkowych anotacji, z których chciałbym Ci przedstawić kilka wybranych.
Eeee tam, znowu hibernate i podstawy…
Ale może Cię zaskoczę 🙂 Przeczytaj dalej i daj znać w komentarzu, czy znasz te anotacje i czy używałeś ich w projekcie!
We wpisie przeczytasz o
@DynamicUpdate
– bardziej sprytne generowanie zapytań@Immutable
@Transient
@Formula
– i tu można coś bardzo optymalizować, jeżeli to koniecznie- Automatyczna data utworzenia i modyfikacji:
@CreationTimestamp
oraz@UpdateTimestamp
Wydajność Hibernate
Twórz szybko działające aplikacje z wydajną i zoptymalizowaną obsługą bazy danych.
@DynamicUpdate
Anotacja DynamicUpdate
pakietu org.hibernate.annotations
włącza inteligentny tryb wykrywania zmian. Zapytania generowane przez Hibernate, reprezentujące instrukcję UPDATE
będą zawierały tylko kolumny, które się zmieniły dla encji, dla której anotacja została dodana.
Istnieje również anotacja @DynamicInsert
. Dla tej anotacji instrukcje INSERT
będą zawierały tylko kolumny, których wartość jest różna od null
.
Poniżej masz przykład kodu
@Entity
@DynamicUpdate
public class Card {
@Id
String cardId;
@Convert(converter = YesNoBooleanConverter.class)
Boolean enabled;
@Enumerated(EnumType.STRING)
CardCountry cardCountry;
BigDecimal balance;
@Temporal(TemporalType.DATE)
Calendar expiresAt;
}
Jeżeli np. zmodyfikujesz wartość balance
, to wygenerowane zapytanie SQL będzie wyglądało następująco
update
cards
set
balance=?,
country=?,
enabled=?,
expires_at=?
where
card_id=?
Po dodaniu @DynamicUpdate
wygenerowane zapytanie będzie zawierało tylko zmienioną kolumnę
update
cards
set
balance=?
where
card_id=?
@Immutable
Anotacja Immutable
pakietu org.hibernate.annotations
włącza mechanizm, który nie pozwala na zmianę danych encji. Takie encje można dodawać i usuwać, ale nie można ich modyfikować. Nawet jeżeli zmienisz wartość pola w tej encji, to Hibernate zignoruje te zmiany i instrukcja UPDATE
nie zostanie wysłana.
Anotacja może także być umieszczona nad polem, wówczas zasięg ograniczony jest do pól oznaczonych tą encją.
@Transient
Jest to anotacja JPA i jest dostępna w pakiecie javax.persistence
. Można ją ustawić dla pola, przez co pole jest oznaczone jako nie persystentne. Hibernate nie obsługuje takich pól (nie będzie ich szukał ani aktualizował). Może to być wykorzystane do chwilowego wtłoczenia dodatkowych danych czy na przykład wstawienia daty odczytu encji z bazy danych.
Uwaga. Należy ostrożnie podchodzić do tego typu pól – np. co się ma stać z takim polem i encją po EntityManager.refresh
czy też po commit
(data odczytu może już nie mieć sensu).
@Formula
Anotacja znajduje się w pakiecie javax.persistence i pozwala na dodanie pola, do którego zostanie wstrzyknięty wynik natywnego zapytania SQL. Pole takie jest polem tylko do odczytu.
Przykładowo chciałbyś wyświetlić listę użytkowników z informacją o liczbie kart. Poniżej masz przykład kodu z relacją OneToMany
@Entity
public class User {
@Id
String userId;
String firstName;
String lastName;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "CARD_OWNER_ID")
List<Card> userCards;
}
Wywołując zapytanie HQL/JPQL będzie musiał uważać na problem N=1 i użyć np. left join fetch
.
var usersListNoNPlus1 = entityManager.createQuery(
"from User u left join fetch u.userCards",
User.class).getResultList();
Mając listę użytkowników, dla każdego sprawdzisz ile ma kart – ale przecież nie potrzebowałeś w tej logice dociągać danych wszystkich kart.
Możesz zrobić to z wykorzystaniem @Formula
@Entity
public class User {
@Id
String userId;
String firstName;
String lastName;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "CARD_OWNER_ID")
List<Card> userCards;
@Formula("(SELECT COUNT(1) FROM SOFTWARESKILL.CARDS C WHERE C.CARD_OWNER_ID=USER_ID)")
int cardsCount;
}
Dzięki temu do pola cardsCount
zostanie wstrzyknięta liczba kart, a relacja może być zmieniona na LAZY
.
Musisz zwrócić uwagę na to, iż zapytanie SQL dla @Formula
to natywne zapytanie SQL, i może nie zadziałać na różnych rodzajach bazy danych, jeżeli będzie wykorzystywało jakąś specyfikę danego silnika bazodanowego.
Automatyczna data utworzenia i modyfikacji
W Hibernate istnieją dwie anotacje @CreationTimestamp
oraz @UpdateTimestamp
, które mogą być użyte do automatycznego nadawania wartości dla momentu utworzenia i modyfikacji danych encji.
@Entity
public class CreditCard {
@Id
@GeneratedValue(strategy = SEQUENCE, generator = "SEQ_CARDS")
Long cardId;
@Convert(converter = YesNoBooleanConverter.class)
Boolean enabled;
@Enumerated(EnumType.STRING)
CardCountry cardCountry;
BigDecimal balance;
@Temporal(TemporalType.DATE)
Calendar expiresAt;
@Temporal(TemporalType.TIMESTAMP)
@CreationTimestamp
Calendar createdAt;
@Temporal(TemporalType.TIMESTAMP)
@UpdateTimestamp
Calendar updatedAt;
}
Uwaga. Jeżeli chciałbyś wykorzystać te dane, to musisz użyć EntityManager.flush
, gdyż wartość pola jest ustawiana dopiero przy flush
lub commit
. Inaczej będzie wartość null
.
Przydatne linki
- https://www.baeldung.com/spring-data-jpa-dynamicupdate
- https://thorben-janssen.com/hibernate-tips-calculate-entity-attributes-formula/
- https://thorben-janssen.com/persist-creation-update-timestamps-hibernate/
Wpis który czytasz to zaledwie fragment wiedzy zawartej w Programie szkoleniowym Java Developera od SoftwareSkill. Mamy do przekazania sporo usystematyzowanej wiedzy z zakresu kluczowych kompetencji i umiejętności Java Developera. Program składa się z kilku modułów w cotygodniowych dawkach wiedzy w formie video.
Wydajność Hibernate
Twórz szybko działające aplikacje z wydajną i zoptymalizowaną obsługą bazy danych.
Gdybyś potrzebował jeszcze więcej:
Jesteś Java Developerem?
Przejdź na wyższy poziom wiedzy
„Droga do Seniora” 🔥💪
Jesteś Team Leaderem? Masz zespół?
Podnieś efektywność i wiedzę swojego zespołu 👌