To pytanie jest w pewnym stopniu związane z pytaniem o umieszczenie adnotacji Hibernate .
Ale chcę wiedzieć, co jest lepsze ? Dostęp przez właściwości czy dostęp przez pola? Jakie są zalety i wady każdego z nich?
To pytanie jest w pewnym stopniu związane z pytaniem o umieszczenie adnotacji Hibernate .
Ale chcę wiedzieć, co jest lepsze ? Dostęp przez właściwości czy dostęp przez pola? Jakie są zalety i wady każdego z nich?
Odpowiedzi:
Preferuję akcesory, ponieważ mogę dodać logikę biznesową do moich akcesorów, kiedy tylko potrzebuję. Oto przykład:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Poza tym, jeśli wrzucisz inne biblioteki do miksu (np. Niektóre biblioteki konwertujące JSON lub BeanMapper lub Dozer lub inne biblioteki mapowania / klonowania fasoli oparte na właściwościach getter / setter), będziesz mieć gwarancję, że biblioteka jest zsynchronizowana z trwałością manager (oba używają getter / setter).
Istnieją argumenty przemawiające za jednym i drugim, ale większość z nich wynika z pewnych wymagań użytkownika „a co, jeśli trzeba dodać logikę dla” lub „xxxx przerywa hermetyzację”. Jednak nikt tak naprawdę nie skomentował tej teorii i nie podał odpowiednio uzasadnionego argumentu.
Co właściwie robi Hibernate / JPA, gdy utrzymuje obiekt - cóż, utrwala STAN obiektu. Oznacza to przechowywanie go w taki sposób, aby można go było łatwo odtworzyć.
Co to jest hermetyzacja? Enkapsulacja oznacza hermetyzację danych (lub stanu) za pomocą interfejsu, z którego aplikacja / klient może korzystać w celu bezpiecznego dostępu do danych - zachowując ich spójność i aktualność.
Pomyśl o tym jak o MS Word. MS Word przechowuje w pamięci model dokumentu - STAN dokumentów. Przedstawia interfejs, za pomocą którego użytkownik może modyfikować dokument - zestaw przycisków, narzędzi, poleceń klawiaturowych itp. Jeśli jednak zdecydujesz się utrwalić (Zapisz) ten dokument, zapisuje stan wewnętrzny, a nie zestaw naciśnięć klawiszy i kliknięcia myszą użyte do jego wygenerowania.
Zapisywanie stanu wewnętrznego obiektu NIE przerywa enkapsulacji - w przeciwnym razie nie rozumiesz, co oznacza hermetyzacja i dlaczego istnieje. To jest tak, jak w rzeczywistości serializacja obiektów.
Z tego powodu W WIĘKSZOŚCI PRZYPADKÓW właściwe jest utrzymanie PÓL, a nie AKCESORÓW. Oznacza to, że obiekt można dokładnie odtworzyć z bazy danych dokładnie tak, jak był przechowywany. Nie powinno wymagać żadnej walidacji, ponieważ zostało to zrobione na oryginale, gdy został utworzony, a zanim został zapisany w bazie danych (chyba, że nie daj Boże, przechowujesz nieprawidłowe dane w DB !!!!). Podobnie nie powinno być potrzeby obliczania wartości, ponieważ zostały one obliczone przed zapisaniem obiektu. Obiekt powinien wyglądać tak, jak przed zapisaniem. W rzeczywistości, dodając dodatkowe rzeczy do getterów / seterów, w rzeczywistości zwiększasz ryzyko, że odtworzysz coś, co nie jest dokładną kopią oryginału.
Oczywiście ta funkcjonalność została dodana nie bez powodu. Mogą istnieć pewne ważne przypadki użycia utrwalania metod dostępu, jednak zazwyczaj będą one rzadkie. Przykładem może być to, że chcesz uniknąć utrwalania obliczonej wartości, chociaż możesz zadać pytanie, dlaczego nie obliczasz jej na żądanie w getterze wartości, lub leniwie inicjujesz ją w getterze. Osobiście nie mogę wymyślić żadnego dobrego przypadku użycia, a żadna z odpowiedzi tutaj nie daje odpowiedzi „Inżynierii oprogramowania”.
Preferuję dostęp do pola, ponieważ w ten sposób nie jestem zmuszony do dostarczania getter / setter dla każdej właściwości.
Szybka ankieta przeprowadzona przez Google sugeruje, że dostęp do terenów stanowi większość (np. Http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Uważam, że dostęp do pola jest idiomem zalecanym przez Spring, ale nie mogę znaleźć odniesienia, aby to potwierdzić.
Istnieje powiązane pytanie SO, które próbowało zmierzyć wydajność i doszło do wniosku, że „nie ma różnicy”.
Oto sytuacja, w której MUSISZ użyć metod dostępu do właściwości. Wyobraź sobie, że masz klasę abstrakcyjną GENERIC z wieloma dobrymi implementacjami, które można dziedziczyć w 8 konkretnych podklasach:
public abstract class Foo<T extends Bar> {
T oneThing;
T anotherThing;
// getters and setters ommited for brevity
// Lots and lots of implementation regarding oneThing and anotherThing here
}
Teraz dokładnie, jak należy opisać tę klasę? Odpowiedź brzmi: NIE MOŻESZ w ogóle dodawać adnotacji do pola lub właściwości, ponieważ w tym momencie nie możesz określić jednostki docelowej. MUSISZ opisać konkretne implementacje. Ale ponieważ utrwalone właściwości są zadeklarowane w tej nadklasie, MUSISZ użyć dostępu do właściwości w podklasach.
Dostęp do pola nie jest opcją w aplikacji z abstrakcyjnymi ogólnymi superklasami.
abstract T getOneThing()
i abstract void setOneThing(T thing)
, i używać dostępu do pola.
Preferuję i używam akcesorów własności:
foo.getId()
bez inicjowania proxy (ważne przy używaniu Hibernacji, dopóki HHH-3718 nie zostanie rozwiązany).Wada:
@Transient
pobliżu.To naprawdę zależy od konkretnego przypadku - obie opcje są dostępne z jakiegoś powodu. IMO sprowadza się do trzech przypadków:
Zdecydowanie zalecałbym dostęp do pól i NIE adnotacje na elementach pobierających (dostęp do właściwości), jeśli chcesz zrobić coś więcej w ustawiaczach niż tylko ustawienie wartości (np. Szyfrowanie lub obliczenia).
Problem z dostępem do właściwości polega na tym, że metody ustawiające są również wywoływane podczas ładowania obiektu. To działało dobrze przez wiele miesięcy, dopóki nie chcieliśmy wprowadzić szyfrowania. W naszym przypadku chcieliśmy zaszyfrować pole w seterze i odszyfrować je w getterze. Problem z dostępem do właściwości polegał teraz na tym, że gdy Hibernate ładował obiekt, wywoływał on również ustawiającego w celu wypełnienia pola, a tym samym ponownie szyfrował zaszyfrowaną wartość. W tym poście wspomniano również o tym: Java Hibernate: Różne zachowanie funkcji zestawu właściwości w zależności od tego, kto ją wywołuje
Przyprawiało mnie to o ból głowy, dopóki nie przypomniałem sobie różnicy między dostępem do pola a dostępem do nieruchomości. Teraz przeniosłem wszystkie moje adnotacje z dostępu do właściwości do dostępu do pól i teraz działa dobrze.
Wolę korzystać z dostępu do pola z następujących powodów:
Dostęp nieruchomość może prowadzić do bardzo nieprzyjemnych błędów przy wdrażaniu równe / hashCode i przedstawieniu pola bezpośrednio (a nie poprzez ich pobierające). Dzieje się tak, ponieważ proxy jest inicjowane tylko wtedy, gdy uzyskiwany jest dostęp do metod pobierających, a dostęp do pola bezpośredniego zwróciłby po prostu wartość null.
Dostęp nieruchomość wymaga opisywanie wszystkich metod użytkowych (np addChild / removeChild) jako @Transient
.
Dzięki dostępowi do pola możemy ukryć pole @Version, nie ujawniając w ogóle metody pobierającej. Getter może również prowadzić do dodania settera, a version
pole nigdy nie powinno być ustawiane ręcznie (co może prowadzić do bardzo nieprzyjemnych problemów). Cała inkrementacja wersji powinna być wyzwalana przez jawne blokowanie OPTIMISTIC_FORCE_INCREMENT lub PESSIMISTIC_FORCE_INCREMENT .
version
pola jest często przydatne w sytuacjach, w których używane są DTO zamiast odłączonych jednostek.
Myślę, że dodawanie adnotacji do właściwości jest lepsze, ponieważ aktualizowanie pól bezpośrednio przerywa hermetyzację, nawet jeśli robi to Twój ORM.
Oto świetny przykład tego, gdzie cię to spali: prawdopodobnie chcesz, aby adnotacje dla walidatora hibernacji i trwałości znajdowały się w tym samym miejscu (pola lub właściwości). Jeśli chcesz przetestować walidacje oparte na hibernacji walidatora, które są opatrzone adnotacjami w polu, nie możesz użyć makiety swojej encji, aby wyodrębnić test jednostkowy tylko do walidatora. Auć.
Uważam, że dostęp do właściwości a dostęp do pola jest nieco inny w odniesieniu do leniwej inicjalizacji.
Rozważ następujące mapowania dla 2 podstawowych fasoli:
<hibernate-mapping package="org.nkl.model" default-access="field">
<class name="FieldBean" table="FIELD_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
<hibernate-mapping package="org.nkl.model" default-access="property">
<class name="PropBean" table="PROP_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
Oraz następujące testy jednostkowe:
@Test
public void testFieldBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
FieldBean fb = new FieldBean("field");
Long id = (Long) session.save(fb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
fb = (FieldBean) session.load(FieldBean.class, id);
System.out.println(fb.getId());
tx.commit();
session.close();
}
@Test
public void testPropBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PropBean pb = new PropBean("prop");
Long id = (Long) session.save(pb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
pb = (PropBean) session.load(PropBean.class, id);
System.out.println(pb.getId());
tx.commit();
session.close();
}
Zobaczysz subtelną różnicę w wymaganym wyborze:
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
FIELD_BEAN
(message, id)
values
(?, ?)
Hibernate:
select
fieldbean0_.id as id1_0_,
fieldbean0_.message as message1_0_
from
FIELD_BEAN fieldbean0_
where
fieldbean0_.id=?
0
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
PROP_BEAN
(message, id)
values
(?, ?)
1
Oznacza to, że dzwonienie fb.getId()
wymaga wybrania, podczas pb.getId()
gdy nie.
Spróbuję podsumować najważniejsze powody wyboru dostępu w terenie. Jeśli chcesz zanurkować głębiej, przeczytaj ten artykuł na moim blogu: Strategie dostępu w JPA i hibernacji - co jest lepsze, dostęp do pola lub nieruchomości?
Dostęp w terenie jest zdecydowanie lepszą opcją. Oto 5 powodów:
Powód 1: Lepsza czytelność twojego kodu
Jeśli korzystasz z dostępu opartego na polach, opisujesz atrybuty encji za pomocą adnotacji mapowania. Umieszczając definicję wszystkich atrybutów encji na początku swojej klasy, uzyskujesz stosunkowo zwarty widok wszystkich atrybutów i ich mapowań.
Powód 2: Pomiń metody pobierające lub ustawiające, które nie powinny być wywoływane przez aplikację
Kolejną zaletą dostępu opartego na polach jest to, że Twój dostawca trwałości, np. Hibernate lub EclipseLink, nie używa metod pobierających i ustawiających atrybutów encji. Oznacza to, że nie musisz podawać żadnej metody, której nie powinien używać kod Twojej firmy. Dzieje się tak najczęściej w przypadku metod ustawiających generowane atrybuty klucza podstawowego lub kolumny wersji. Twój dostawca trwałości zarządza wartościami tych atrybutów i nie należy ustawiać ich programowo.
Powód 3: Elastyczna implementacja metod pobierających i ustawiających
Ponieważ Twój dostawca trwałości nie wywołuje metod pobierających i ustawiających, nie są one zmuszane do spełnienia żadnych wymagań zewnętrznych. Możesz zaimplementować te metody w dowolny sposób. Dzięki temu można zaimplementować reguły walidacji specyficzne dla firmy, wyzwolić dodatkową logikę biznesową lub przekonwertować atrybut jednostki na inny typ danych.
Możesz na przykład użyć tego do zawijania opcjonalnego skojarzenia lub atrybutu w Javie Optional
.
Powód 4: Nie ma potrzeby oznaczania metod użytkowych jako @Transient
Inną zaletą strategii dostępu opartego na polach jest to, że nie musisz dodawać adnotacji do metod narzędziowych @Transient
. Ta adnotacja informuje dostawcę trwałości, że metoda lub atrybut nie jest częścią trwałego stanu jednostki. A ponieważ w przypadku dostępu do pól trwały stan jest definiowany przez atrybuty encji, implementacja JPA ignoruje wszystkie metody encji.
Powód 5: unikaj błędów podczas pracy z serwerami proxy
Hibernate używa serwerów proxy do leniwie pobieranych skojarzeń do jednego, dzięki czemu może kontrolować inicjalizację tych skojarzeń. Takie podejście działa dobrze w prawie wszystkich sytuacjach. Ale wprowadza niebezpieczną pułapkę, jeśli korzystasz z dostępu opartego na własności.
Jeśli korzystasz z dostępu opartego na właściwościach, Hibernate inicjuje atrybuty obiektu proxy, gdy wywołasz metodę pobierającą. Dzieje się tak zawsze, jeśli używasz obiektu proxy w kodzie biznesowym. Ale całkiem sporo implementacji równań i hashCode uzyskuje bezpośredni dostęp do atrybutów. Jeśli jest to pierwszy raz, gdy uzyskujesz dostęp do któregokolwiek z atrybutów proxy, te atrybuty nadal nie są zainicjowane.
Domyślnie dostawcy JPA uzyskują dostęp do wartości pól encji i mapują te pola na kolumny bazy danych przy użyciu metod dostępu (getter) i mutatora (setter) właściwości JavaBean jednostki. W związku z tym nazwy i typy pól prywatnych w jednostce nie mają znaczenia dla WZP. Zamiast tego JPA sprawdza tylko nazwy i typy zwracane metod dostępu do właściwości JavaBean. Możesz to zmienić za pomocą @javax.persistence.Access
adnotacji, która umożliwia jawne określenie metodologii dostępu, którą powinien stosować dostawca JPA.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Dostępne opcje wyliczenia AccessType to PROPERTY (wartość domyślna) i FIELD. W przypadku PROPERTY dostawca pobiera i ustawia wartości pól przy użyciu metod właściwości JavaBean. FIELD sprawia, że dostawca pobiera i ustawia wartości pól przy użyciu pól instancji. Najlepszą praktyką jest trzymanie się wartości domyślnych i używanie właściwości JavaBean, chyba że masz istotny powód, aby postąpić inaczej.
Możesz umieścić te adnotacje właściwości w polach prywatnych lub publicznych metodach dostępu. Jeśli używasz AccessType.PROPERTY
(domyślnie) i opisujesz pola prywatne zamiast metod dostępu JavaBean, nazwy pól muszą być zgodne z nazwami właściwości JavaBean. Jednak nazwy nie muszą być zgodne, jeśli dodasz adnotacje do metod dostępu JavaBean. Podobnie, jeśli AccessType.FIELD
zamiast pól używasz metod dostępu JavaBean i dodasz do nich adnotacje, nazwy pól również muszą być zgodne z nazwami właściwości JavaBean. W takim przypadku nie muszą się one zgadzać, jeśli dodasz adnotacje do pól. Najlepiej jest po prostu zachować spójność i dodawać adnotacje do metod dostępu JavaBean dla AccessType.PROPERTY
i pól dla
AccessType.FIELD
.
Ważne jest, aby nigdy nie mieszać adnotacji właściwości JPA i adnotacji pól JPA w tej samej encji. Takie postępowanie skutkuje nieokreślonym zachowaniem i jest bardzo prawdopodobne, że spowoduje błędy.
To stara prezentacja, ale Rod sugeruje, że adnotacje dotyczące dostępu do właściwości zachęcają do anemicznych modeli domen i nie powinny być „domyślnym” sposobem tworzenia adnotacji.
Innym argumentem przemawiającym za dostępem do pola jest to, że w przeciwnym razie jesteś zmuszony ujawniać setery dla kolekcji, co dla mnie jest złym pomysłem, ponieważ zmiana instancji trwałej kolekcji na obiekt nie zarządzany przez Hibernate z pewnością zepsuje spójność danych.
Dlatego wolę mieć kolekcje jako chronione pola zainicjowane do pustych implementacji w domyślnym konstruktorze i ujawniać tylko ich metody pobierające. Wtedy tylko operacje zarządzane, takie jakclear()
, remove()
, removeAll()
możliwe, że nigdy nie będzie Hibernate są nieświadomi zmian itp.
Preferuję pola, ale napotkałem jedną sytuację, która wydaje się zmuszać mnie do umieszczania adnotacji na getterach.
Z implementacją Hibernate JPA, @Embedded
wydaje się , że nie działa na polach. Więc to musi iść dalej. A kiedy już umieścisz to w getterze, wtedy różne @Column
adnotacje też muszą trafić do getterów. (Myślę, że Hibernate nie chce tutaj mieszać pól i getterów). A kiedy @Column
już umieścisz gettery w jednej klasie, prawdopodobnie ma to sens.
Preferuję akcesoria do pól. Kod jest znacznie bardziej przejrzysty. Wszystkie adnotacje można umieścić w jednej sekcji klasy, a kod jest znacznie łatwiejszy do odczytania.
Znalazłem inny problem z metodami dostępu do właściwości: jeśli masz metody getXYZ w swojej klasie, które NIE są przypisane do trwałych właściwości, hibernacja generuje sql, aby spróbować uzyskać te właściwości, co powoduje pewne bardzo mylące komunikaty o błędach. Zmarnowane dwie godziny. Nie napisałem tego kodu; W przeszłości zawsze korzystałem z metod dostępu do pól i nigdy nie napotkałem tego problemu.
Wersje hibernacji używane w tej aplikacji:
<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>
Należy wybrać dostęp za pośrednictwem pól zamiast dostępu za pośrednictwem właściwości. Za pomocą pól możesz ograniczyć ilość wysyłanych i odbieranych danych. Dzięki właściwościom możesz wysłać więcej danych jako host i ustawić nominały G (które fabrycznie ustawiają w sumie większość właściwości).
Miałem to samo pytanie dotyczące typu dostępu w trybie hibernacji i znalazłem tutaj kilka odpowiedzi .
Rozwiązałem tutaj leniwą inicjalizację i dostęp do pola Hibernacja jeden do jednego: getId () bez pobierania całego obiektu
Stworzyliśmy fasolki encji i użyliśmy adnotacji pobierających. Problem, który napotkaliśmy, jest następujący: niektóre encje mają złożone reguły dotyczące niektórych właściwości, określające, kiedy można je zaktualizować. Rozwiązaniem było posiadanie logiki biznesowej w każdym programie ustawiającym, która określa, czy rzeczywista wartość uległa zmianie, a jeśli tak, to czy zmiana powinna być dozwolona. Oczywiście Hibernate zawsze może ustawić właściwości, więc otrzymaliśmy dwie grupy ustawiaczy. Dość brzydki.
Czytając poprzednie posty, widzę również, że odwoływanie się do właściwości z wnętrza jednostki może prowadzić do problemów z ładowaniem kolekcji.
Podsumowując, skłaniałbym się do dodawania adnotacji do pól w przyszłości.
Zwykle fasola to POJO, więc i tak mają akcesory.
Zatem pytanie nie brzmi „który jest lepszy?”, Ale po prostu „kiedy używać dostępu do pola?”. A odpowiedź brzmi: „kiedy nie potrzebujesz ustawiacza / pobierającego dla pola!”.
myślę o tym i wybieram metodę accesor
czemu?
ponieważ pole i metoda accesor są takie same, ale jeśli później będę potrzebować logiki w polu ładowania, zapisuję przenieść wszystkie adnotacje umieszczone w polach
pozdrowienia
Grubharta
Obie :
Specyfikacja EJB3 wymaga zadeklarowania adnotacji dla typu elementu, do którego będzie uzyskiwany dostęp, tj. Metody pobierającej, jeśli korzystasz z dostępu do właściwości, pola, jeśli używasz dostępu do pola.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: implementacja trwałości EJB załaduje stan do Twojej klasy za pośrednictwem metod „ustawiających” w języku JavaBean i pobierze stan z klasy za pomocą metod „pobierających” języka JavaBean. To jest ustawienie domyślne.
AccessType.FIELD: Stan jest ładowany i pobierany bezpośrednio z pól Twojej klasy. Nie musisz pisać "getters" i "setters" JavaBean.