2010-01-16 13 views
13

Sto osservando un comportamento molto strano con una classe di entità e il caricamento di un oggetto di questa classe con JPA (hibernate entitymanager 3.3.1.ga). La classe ha un campo (incorporato), che viene inizializzato nella dichiarazione. Il setter del campo implementa un controllo nullo (cioè genera un'eccezione quando viene impostato un valore nullo).Strano comportamento JPA, campo inizializzato è nullo

... 
@Entity 
public class Participant extends BaseEntity implements Comparable<Participant> { 
    ... 
    @Embedded 
private AmsData amsData = new AmsData(); 

public void setAmsData(AmsData amsData) { 
    Checks.verifyArgNotNull(amsData, "amsdata"); 
    this.amsData = amsData; 
} 
    ... 
} 

Quando torno a questo oggetto con JPA, il campo è nullo, se non ci sono dati nel db per i campi di cui l'oggetto incorporato.

... 
public class ParticipantJpaDao implements ParticipantDao { 
@PersistenceContext 
private EntityManager em; 

@Override 
public Participant getParticipant(Long id) { 
    return em.find(Participant.class, id); 
} 
    ... 
} 

ho debug il processo con un watchpoint sul campo (dovrebbe fermare quando il campo si accede o modificato), e vedo una modifica quando il campo viene inizializzato, ma quando ottengo il risultato dalla chiamata find , il campo è nullo.

Qualcuno può spiegare, perché è così? Come posso garantire che il campo non sia nullo, anche quando non ci sono dati per i campi dell'oggetto incorporato nel db (oltre a impostarlo manualmente dopo la chiamata di ricerca).

+0

Questo potrebbe essere dovuto al tuo motore di associazioni lazy loading di persistenza? –

+0

Uso il caricamento lazy, ma questo è un campo incorporato, quindi memorizzato nella stessa tabella. C'è la possibilità di annotare questo per essere impazientemente preso? – Dominik

+0

Problema di ibernazione correlato (tratto dalla risposta cancellata): [HHH-7610: opzione per l'impostazione del campo @Embedded null quando tutte le colonne sono NULL] (https://hibernate.atlassian.net/browse/HHH-7610) – sleske

risposta

16

La specifica JPA non dice esplicitamente come gestire un set di colonne che rappresentano un oggetto incorporabile che sono tutte vuote. Potrebbe segnalare un riferimento null o un'istanza dell'oggetto con tutti i campi nulli. Hibernate sceglie un riferimento null in questo caso, anche se altre implementazioni JPA potrebbero scegliere il successivo.

Il motivo per cui il tuo setter non viene mai chiamato è perché Hibernate sta accedendo al campo attraverso la riflessione, bypassando il setter è stato implementato. Lo sta facendo perché utilizzi l'accesso basato sul campo piuttosto che l'accesso basato sulla proprietà.

La risposta di Chad's fornirebbe la funzionalità che stai cercando, ma c'è un avvertimento (vedi sotto).

" ... Lo stato persistente di un'entità si accede dal fornitore di runtime di persistenza [1] sia attraverso di accesso alle proprietà di stile o JavaBeans tramite le variabili di istanza. Una singola tipo di accesso (campo o della proprietà accesso) applica a una gerarchia entità. Quando si utilizzano annotazioni, il posizionamento dei le annotazioni di mappatura su entrambi i campi persistenti o persistenti proprietà della classe dell'entità specifica il tipo di accesso come sia campo-o R proprietà a base di accesso rispettivamente ..."[ejb3 persistenza spec]

così spostando le annotazioni verso il setter, si indica JPA che si desidera utilizzare l'accesso basato sulla proprietà invece di campo- accesso basato. Dovresti sapere, tuttavia, che l'accesso basato sul campo - come lo stai implementando attualmente - è preferibile rispetto all'accesso basato su proprietà. Ci sono un paio di ragioni per cui l'accesso basato sulla proprietà è scoraggiato, ma uno è che sono obbligati ad aggiungere getter e setter per tutti i campi di entità persistenti, ma non si può desiderare che quegli stessi campi siano suscettibili di mutazione da client esterni. In altre parole, l'utilizzo dell'accesso basato su proprietà di JPA ti costringe a indebolire l'incapsulamento della tua entità.

+0

ma perché imposta il campo su null? – Dominik

+0

Ho aggiornato la risposta per spiegare perché il campo potrebbe essere nullo. Il motivo più comune è che nel database sono presenti record che precedono l'aggiunta del campo ** amsData **. – rcampbell

+0

Ho appena archiviato una nuova entità con un oggetto AmsData non nullo (ma vuoto) e ottengo di nuovo lo stesso risultato. E sì, la classe AmsData è annotata come incorporabile. – Dominik

1

Bene, è possibile che il tuo oggetto possa essere costruito due volte dietro le quinte. Le implementazioni JPA di solito impostano questi campi direttamente.

Penso che sia necessario inserire le annotazioni sul Getter e sul setter da soli se si desidera che vengano utilizzate. Vedere questa risposta:

Empty constructors and setters on JPA Entites

+0

L'oggetto incorporato ha anche solo colonne e nessuna associazione, che può essere recuperata con entusiasmo. – Dominik

3

La risposta è (grazie a rcampell), se tutti i dati di un oggetto incorporato è nullo (nel db), l'oggetto incorporato sarà anche nullo, anche quando viene inizializzato nella dichiarazione. L'unica soluzione sembra essere, impostando l'oggetto manualmente.

@Override 
public Participant getParticipant(Long id) { 
    Participant participant = em.find(Participant.class, id); 
    if(participant != null && participant.getAmsData() == null) 
    { 
     participant.setAmsData(new AmsData()); 
    } 
    return participant; 
} 

sente ancora strano per me ...

+0

Aveva lo stesso problema alcune settimane fa. Questo farà il trucco. +1 – whiskeysierra

+0

Questa soluzione attiva l'istruzione di aggiornamento di SQL sul server di database, creando problemi come la concorrenza tra thread e problemi di prestazioni. –