Questa domanda è in qualche modo correlata alla domanda di collocazione delle annotazioni di Hibernate .
Ma voglio sapere qual è la migliore ? Accesso tramite proprietà o accesso tramite campi? Quali sono i vantaggi e gli svantaggi di ciascuno?
Questa domanda è in qualche modo correlata alla domanda di collocazione delle annotazioni di Hibernate .
Ma voglio sapere qual è la migliore ? Accesso tramite proprietà o accesso tramite campi? Quali sono i vantaggi e gli svantaggi di ciascuno?
Risposte:
Preferisco gli accessori, poiché posso aggiungere alcune logiche aziendali ai miei accessori ogni volta che ne ho bisogno. Ecco un esempio:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Inoltre, se lanci un'altra libreria nel mix (come alcune librerie convertite in JSON o BeanMapper o Dozer o altre librerie / clonazioni di bean basate su proprietà getter / setter) avrai la garanzia che la libreria è sincronizzata con la persistenza manager (entrambi usano il getter / setter).
Ci sono argomenti per entrambi, ma la maggior parte di essi deriva da determinati requisiti dell'utente "che cosa succede se è necessario aggiungere la logica per" o "incapsulamento delle interruzioni xxxx". Tuttavia, nessuno ha veramente commentato la teoria e dato un argomento adeguatamente ragionato.
Cosa sta realmente facendo Hibernate / JPA quando persiste un oggetto - beh, sta persistendo lo STATO dell'oggetto. Ciò significa memorizzarlo in modo che possa essere facilmente riprodotto.
Che cos'è l'incapsulamento? Incapsulamento significa incapsulare i dati (o lo stato) con un'interfaccia che l'applicazione / client può utilizzare per accedere ai dati in modo sicuro, mantenendoli coerenti e validi.
Pensa a questo come a MS Word. MS Word mantiene in memoria un modello del documento: i documenti STATE. Presenta un'interfaccia che l'utente può utilizzare per modificare il documento: una serie di pulsanti, strumenti, comandi da tastiera ecc. Tuttavia, quando si sceglie di persistere (Salva) quel documento, salva lo stato interno, non l'insieme di pressioni di tasti e clic del mouse utilizzati per generarlo.
Salvare lo stato interno dell'oggetto NON rompe l'incapsulamento, altrimenti non si capisce veramente cosa significa incapsulamento e perché esiste. È proprio come la serializzazione degli oggetti.
Per questo motivo, NELLA MAGGIOR PARTE DEI CASI, è opportuno persistere nei CAMPI e non negli ACCESSORI. Ciò significa che un oggetto può essere ricreato con precisione dal database esattamente come è stato archiviato. Non dovrebbe aver bisogno di alcuna convalida, perché questo è stato fatto sull'originale al momento della sua creazione e prima che fosse archiviato nel database (a meno che, Dio non voglia, stai memorizzando dati non validi nel DB !!!!). Allo stesso modo, non dovrebbe essere necessario calcolare i valori, poiché erano già stati calcolati prima che l'oggetto fosse archiviato. L'oggetto dovrebbe apparire esattamente come prima di essere salvato. In effetti, aggiungendo elementi aggiuntivi nei getter / setter, si aumenta effettivamente il rischio di ricreare qualcosa che non è una copia esatta dell'originale.
Naturalmente, questa funzionalità è stata aggiunta per un motivo. Potrebbero esserci alcuni casi d'uso validi per il persistere degli accessori, tuttavia, saranno in genere rari. Un esempio potrebbe essere quello di evitare di persistere in un valore calcolato, anche se è possibile porre la domanda sul perché non lo si calcola su richiesta nel getter del valore, oppure inizializzarlo pigramente nel getter. Personalmente non riesco a pensare ad alcun buon caso d'uso, e nessuna delle risposte qui dà davvero una risposta "Ingegneria del software".
Preferisco l'accesso ai campi, perché in questo modo non sono costretto a fornire getter / setter per ogni proprietà.
Un rapido sondaggio tramite Google suggerisce che l'accesso sul campo è la maggioranza (ad esempio, http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Credo che l'accesso sul campo sia il linguaggio raccomandato da Spring, ma non riesco a trovare un riferimento per sostenerlo.
C'è una domanda SO correlata che ha cercato di misurare le prestazioni e ha concluso che "non c'è differenza".
Ecco una situazione in cui DEVI utilizzare gli accessori di proprietà. Immagina di avere una classe astratta GENERICA con molta bontà di implementazione da ereditare in 8 sottoclassi concrete:
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
}
Ora, esattamente come dovresti annotare questa classe? La risposta è che NON PUOI annotarlo affatto con l'accesso al campo o alla proprietà perché non puoi specificare l'entità target a questo punto. DEVI annotare le implementazioni concrete. Ma poiché le proprietà persistenti sono dichiarate in questa superclasse, DEVI usare l'accesso alle proprietà nelle sottoclassi.
L'accesso ai campi non è un'opzione in un'applicazione con superclassi generiche astratte.
abstract T getOneThing()
, abstract void setOneThing(T thing)
ee utilizzare l'accesso al campo.
Tendo a preferire e utilizzare gli accessori di proprietà:
foo.getId()
senza inizializzare un proxy (importante quando si utilizza Hibernate, fino a quando HHH-3718 non viene risolto).Inconveniente:
@Transient
giro.Dipende molto da un caso specifico: entrambe le opzioni sono disponibili per un motivo. IMO si riduce a tre casi:
Consiglio vivamente l'accesso al campo e NON le annotazioni sui getter (accesso alla proprietà) se si desidera fare qualcosa di più nei setter rispetto alla semplice impostazione del valore (ad es. Crittografia o calcolo).
Il problema con l'accesso alla proprietà è che anche i setter vengono chiamati quando l'oggetto viene caricato. Questo ha funzionato bene per me per molti mesi fino a quando non abbiamo voluto introdurre la crittografia. Nel nostro caso d'uso volevamo crittografare un campo nel setter e decrittografarlo nel getter. Il problema ora con l'accesso alle proprietà era che quando Hibernate caricava l'oggetto, stava anche chiamando il setter per popolare il campo e quindi stava crittografando di nuovo il valore crittografato. Questo post menziona anche questo: Ibernazione Java: comportamento della serie di proprietà diverse a seconda di chi lo chiama
Questo mi ha causato mal di testa fino a quando non ho ricordato la differenza tra accesso al campo e accesso alle proprietà. Ora ho spostato tutte le mie annotazioni dall'accesso alla proprietà all'accesso al campo e ora funziona perfettamente.
Preferisco utilizzare l'accesso al campo per i seguenti motivi:
L' accesso alla proprietà può portare a bug molto cattivi durante l'implementazione di egual / hashCode e il riferimento diretto ai campi (al contrario dei loro getter). Questo perché il proxy viene inizializzato solo quando si accede ai getter e un accesso diretto al campo restituisce semplicemente null.
L' accesso alla proprietà richiede di annotare tutti i metodi di utilità (ad es. AddChild / removeChild) come @Transient
.
Con l'accesso al campo possiamo nascondere il campo @Version non esponendo affatto un getter. Un getter può anche portare all'aggiunta di un setter e il version
campo non dovrebbe mai essere impostato manualmente (il che può portare a problemi molto cattivi). L'incremento della versione deve essere attivato tramite il blocco esplicito OPTIMISTIC_FORCE_INCREMENT o PESSIMISTIC_FORCE_INCREMENT .
version
campo è spesso utile nelle situazioni in cui vengono utilizzati DTO anziché entità distaccate.
Penso che l'annotazione della proprietà sia migliore perché l'aggiornamento dei campi interrompe direttamente l'incapsulamento, anche quando l'ORM lo fa.
Ecco un ottimo esempio di dove ti brucerà: probabilmente vorrai le tue annotazioni per il validatore in ibernazione e la persistenza nello stesso posto (campi o proprietà). Se si desidera testare le convalide basate sul validatore ibernato che sono annotate su un campo, non è possibile utilizzare una simulazione dell'entità per isolare il test unitario solo sul validatore. Ahia.
Credo che l'accesso alla proprietà rispetto all'accesso al campo sia leggermente diverso rispetto all'inizializzazione pigra.
Considerare i seguenti mapping per 2 bean di base:
<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>
E i seguenti test unitari:
@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();
}
Vedrai la sottile differenza nelle selezioni richieste:
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
Cioè, chiamare fb.getId()
richiede una selezione, mentre pb.getId()
non lo fa.
Vorrei provare a riassumere i motivi più importanti per la scelta dell'accesso basato sul campo. Se vuoi immergerti più in profondità, leggi questo articolo sul mio blog: Strategie di accesso in JPA e Hibernate - Qual è l'accesso migliore, sul campo o sulla proprietà?
L'accesso basato sul campo è di gran lunga l'opzione migliore. Ecco 5 motivi per questo:
Motivo 1: migliore leggibilità del codice
Se si utilizza l'accesso basato sul campo, si annotano gli attributi dell'entità con le annotazioni della mappatura. Posizionando la definizione di tutti gli attributi di entità nella parte superiore della classe, si ottiene una vista relativamente compatta di tutti gli attributi e i relativi mapping.
Motivo 2: omettere metodi getter o setter che non dovrebbero essere chiamati dall'applicazione
Un altro vantaggio dell'accesso basato sul campo è che il provider di persistenza, ad esempio Hibernate o EclipseLink, non utilizza i metodi getter e setter degli attributi dell'entità. Ciò significa che non è necessario fornire alcun metodo che non dovrebbe essere utilizzato dal codice aziendale. Questo è spesso il caso dei metodi setter di attributi di chiave primaria generati o colonne di versione. Il provider di persistenza gestisce i valori di questi attributi e non è necessario impostarli a livello di codice.
Motivo 3: implementazione flessibile dei metodi getter e setter
Poiché il provider di persistenza non chiama i metodi getter e setter, non è obbligato a soddisfare alcun requisito esterno. È possibile implementare questi metodi nel modo desiderato. Ciò consente di implementare regole di convalida specifiche dell'azienda, di attivare ulteriori logiche aziendali o di convertire l'attributo dell'entità in un diverso tipo di dati.
Ad esempio, è possibile utilizzarlo per racchiudere un'associazione o un attributo facoltativo in un Java Optional
.
Motivo 4: non è necessario contrassegnare i metodi di utilità come @Transient
Un altro vantaggio della strategia di accesso basata sul campo è che non è necessario annotare i metodi di utilità con @Transient
. Questa annotazione indica al provider di persistenza che un metodo o un attributo non fa parte dello stato persistente dell'entità. E poiché con l'accesso al tipo di campo lo stato persistente viene definito dagli attributi dell'entità, l'implementazione JPA ignora tutti i metodi dell'entità.
Motivo 5: evitare i bug quando si lavora con i proxy
Hibernate utilizza i proxy per le associazioni pigramente recuperate in modo da poter controllare l'inizializzazione di queste associazioni. Questo approccio funziona bene in quasi tutte le situazioni. Ma introduce una trappola pericolosa se si utilizza l'accesso basato sulla proprietà.
Se si utilizza l'accesso basato su proprietà, Hibernate inizializza gli attributi dell'oggetto proxy quando si chiama il metodo getter. Questo è sempre il caso se usi l'oggetto proxy nel tuo codice aziendale. Ma parecchie implementazioni uguali e hashCode accedono direttamente agli attributi. Se è la prima volta che accedi a uno degli attributi proxy, questi attributi non sono ancora inizializzati.
Per impostazione predefinita, i provider JPA accedono ai valori dei campi entità e mappano tali campi alle colonne del database utilizzando i metodi di accesso (getter) e mutatore (setter) della proprietà JavaBean dell'entità. Pertanto, i nomi e i tipi dei campi privati in un'entità non contano per l'APP. Invece, JPA esamina solo i nomi e i tipi restituiti degli accessori di proprietà JavaBean. È possibile modificarlo utilizzando l' @javax.persistence.Access
annotazione, che consente di specificare esplicitamente la metodologia di accesso che il provider JPA dovrebbe utilizzare.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Le opzioni disponibili per l'enumerazione AccessType sono PROPERTY (impostazione predefinita) e FIELD. Con PROPERTY, il provider ottiene e imposta i valori dei campi utilizzando i metodi della proprietà JavaBean. FIELD consente al provider di ottenere e impostare i valori dei campi utilizzando i campi dell'istanza. Come best practice, dovresti semplicemente attenerti all'impostazione predefinita e utilizzare le proprietà JavaBean a meno che tu non abbia un motivo convincente per fare diversamente.
È possibile inserire queste annotazioni delle proprietà sui campi privati o sui metodi di accesso pubblico. Se si utilizza AccessType.PROPERTY
(impostazione predefinita) e si annotano i campi privati anziché gli accessori JavaBean, i nomi dei campi devono corrispondere ai nomi delle proprietà JavaBean. Tuttavia, i nomi non devono corrispondere se si annotano gli accessori JavaBean. Allo stesso modo, se si utilizzano AccessType.FIELD
e annotano gli accessori JavaBean anziché i campi, anche i nomi dei campi devono corrispondere ai nomi delle proprietà JavaBean. In questo caso, non devono corrispondere se si annotano i campi. È meglio essere coerenti e annotare gli accessor JavaBean AccessType.PROPERTY
e i campi per
AccessType.FIELD
.
È importante non mescolare mai le annotazioni delle proprietà JPA e le annotazioni dei campi JPA nella stessa entità. Ciò comporta un comportamento non specificato ed è molto probabile che causi errori.
Questa è una vecchia presentazione, ma Rod suggerisce che l'annotazione sull'accesso alle proprietà incoraggia i modelli di dominio anemici e non dovrebbe essere il modo "predefinito" di annotare.
Un altro punto a favore dell'accesso ai campi è che altrimenti sei costretto a esporre setter per le raccolte e ciò che, per me, è una cattiva idea in quanto cambiare l'istanza di raccolta persistente in un oggetto non gestito da Hibernate interromperà sicuramente la coerenza dei dati.
Quindi preferisco avere raccolte come campi protetti inizializzati per svuotare le implementazioni nel costruttore predefinito ed esporre solo i loro getter. Quindi, sono possibili solo operazioni gestite come clear()
, ecc. remove()
, removeAll()
Che non renderanno mai Hibernate ignaro delle modifiche.
Preferisco i campi, ma mi sono imbattuto in una situazione che sembra costringermi a posizionare le annotazioni su getter.
Con l'implementazione di Hibernate JPA, @Embedded
non sembra funzionare sui campi. Quindi questo deve andare avanti. E una volta che lo metti sul getter, allora anche le varie @Column
annotazioni devono andare sui getter. (Penso che Hibernate non voglia mescolare campi e getter qui.) E una volta che stai mettendo @Column
su getter in una classe, probabilmente ha senso farlo dappertutto.
Prediligo gli accessor sul campo. Il codice è molto più pulito. Tutte le annotazioni possono essere inserite in una sezione di una classe e il codice è molto più facile da leggere.
Ho riscontrato un altro problema con gli accessor delle proprietà: se nella tua classe sono presenti metodi getXYZ che NON sono annotati come associati a proprietà persistenti, l'ibernazione genera sql per tentare di ottenere tali proprietà, generando alcuni messaggi di errore molto confusi. Due ore sprecate. Non ho scritto questo codice; Ho sempre usato gli accessi sul campo in passato e non ho mai riscontrato questo problema.
Versioni di ibernazione utilizzate in questa app:
<!-- 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>
È necessario scegliere l'accesso tramite i campi rispetto all'accesso tramite le proprietà. Con i campi è possibile limitare i dati inviati e ricevuti. Con le proprietà via puoi inviare più dati come host e impostare le denominazioni G (che la fabbrica imposta la maggior parte delle proprietà in totale).
Ho avuto la stessa domanda per quanto riguarda accesstype in ibernazione e ho trovato alcune risposte qui .
Ho risolto l'inizializzazione pigra e l'accesso al campo qui Hibernate one-to-one: getId () senza recuperare l'intero oggetto
Abbiamo creato bean di entità e utilizzato annotazioni getter. Il problema che abbiamo riscontrato è questo: alcune entità hanno regole complesse per alcune proprietà riguardo a quando possono essere aggiornate. La soluzione era quella di avere una logica di business in ogni setter che determina se il valore effettivo è cambiato o meno e, in tal caso, se la modifica dovrebbe essere consentita. Naturalmente, Hibernate può sempre impostare le proprietà, quindi abbiamo finito con due gruppi di setter. Abbastanza brutto.
Leggendo i post precedenti, vedo anche che fare riferimento alle proprietà dall'interno dell'entità potrebbe causare problemi con le raccolte che non si caricano.
In conclusione, mi spingerei verso l'annotazione dei campi in futuro.
Normalmente i fagioli sono POJO, quindi hanno comunque degli accessori.
Quindi la domanda non è "quale è meglio?", Ma semplicemente "quando utilizzare l'accesso al campo?". E la risposta è "quando non hai bisogno di un setter / getter per il campo!".
ci sto pensando e ho scelto il metodo accesor
perché?
perché field e methos accesses sono gli stessi ma se in seguito avrò bisogno di un po 'di logica nel campo di caricamento, salvo spostare tutte le annotazioni inserite nei campi
Saluti
Grubhart
Tutti e due :
Le specifiche EJB3 richiedono che si dichiarino le annotazioni sul tipo di elemento a cui si accederà, ovvero il metodo getter se si utilizza l'accesso alle proprietà, il campo se si utilizza l'accesso al campo.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: L'implementazione della persistenza EJB caricherà lo stato nella classe tramite i metodi "setter" JavaBean e recupererà lo stato dalla classe utilizzando i metodi "getter" JavaBean. Questo è il valore predefinito.
AccessType.FIELD: lo stato viene caricato e recuperato direttamente dai campi della classe. Non è necessario scrivere "getter" e "setter" JavaBean.