2012-11-14 7 views
5

Il mio problema è che il caricamento in sospensione e impaziente dell'associazione OneToOne esegue la selezione +1 per ogni relazione nulla.Hibernate OneToOne (facoltativo = true) con FetchMode.JOIN tenta di riselezionare i valori nulli

Entity esempio:

@Entity 
class SideBlue { 
    @Column(nullable = false) 
    private Integer timestamp; 

    @OneToOne(optional=true) 
    @JoinColumn(name="timestamp", referenceColumn="timestamp", insertable = false, updatable = false) 
    SideRed redSide; 
} 
@Entity 
class SideRed { 
    @Column(nullable = false) 
    private Integer timestamp; 
} 

(Si tratta di uno schema di database legacy, in modo da modifiche del database non è consentito)

Query esempio:

CriteriaBuilder builder... CriteriaQuery query... 
Root<SideBlue> root = query.from(SideBlue.class); 
root.fetch(SideBlue_.sideRed, JoinType.LEFT); 
entityManager().createQuery(query).getResultList(); 

Il risultato: Se tutto lato blu le entità hanno un lato rosso, tutto procede correttamente, quindi l'ibernazione esegue solo una query nel database per tutte le entità che verranno recuperate.

Tuttavia, se le entità del lato blu non hanno un'entità lato rosso associata, è necessario provare a trovare l'altra parte ancora una volta. Il commento Hibernate di sql dice '/ * carica RedSide */select ...' per ogni proprietà null redSide.

Come posso saltare questa seconda selezione?

Il problema pratico viene visualizzato quando la latenza non è estremamente bassa. Se provo a selezionare 1 milione di righe e 1/3 hanno "lati rossi" nulli, la latenza totale aggiunta è un problema reale.

EDIT:

Questo è il log di debug per la query

10:04:32.812 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0 
10:04:32.815 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[SideBlue#1269721], EntityKey[SideRed#3620564] 
10:04:32.833 [main] DEBUG org.hibernate.loader.Loader - Result set row: 1 
10:04:32.833 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[SideBlue#1269776], null 

La prima riga contiene i lati blu e rosso, ma la seconda solo il lato blu. Quindi ibernare deve sapere che il lato rosso correlato non esiste. Ma, dopo che tutte le righe dei risultati vengono elaborate ...

10:04:33.083 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [BlueSide#1269721] 
10:04:33.084 [main] DEBUG org.hibernate.loader.Loader - Loading entity: [RedSide#component[timestamp]{timestamp=1338937390}] 
10:04:33.084 [main] DEBUG org.hibernate.SQL - /* load RedSide */ select ... 
! Nothing really loaded because the previous SQL return empty result set, again ! 
10:04:33.211 [main] DEBUG org.hibernate.loader.Loader - Done entity load 
+1

L'esempio è confuso, il lato che ha l'annotazione @JoinColumn su di esso è per definizione il proprietario della relazione. – Affe

+0

Il mio errore. Io ridefinisco/migliorano la definizione dell'entità. È una versione semplificata della mia entità, ma penso che sia abbastanza. Il database non ha FK tra i timestamp di entrambi i lati. –

risposta

0

Bene, si sta tentando di non caricare SideRed quando si esegue una query per SideBlue. Penso che sia un pigro problema di carico che è in relazione con questo "limite" da Hibernate (da https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one?_sscc=t):

class B { 
    private C cee; 

    public C getCee() { 
     return cee; 
    } 

    public void setCee(C cee) { 
     this.cee = cee; 
    } 
} 

class C { 
    // Not important really 
} 

destro dopo il caricamento B, si può chiamare getCee() per ottenere C. Ma guarda, getCee() è un metodo della TUA classe e Hibernate non ha controllo su it. Hibernate non sa quando qualcuno chiamerà getCee(). Ciò significa che Hibernate deve inserire un valore appropriato nella proprietà "cee" nel momento in cui carica B dal database.

Se il proxy è abilitato per C, Hibernate può inserire un oggetto C-proxy che non è ancora stato caricato, ma verrà caricato quando qualcuno lo utilizza. Questo dà il caricamento lazy per one-to-one.

Ma ora immagina che il tuo oggetto B possa o meno avere associato C (vincolato = "falso"). Cosa deve restituire getCee() quando la specifica B non ha C? Nullo. Ma ricorda, Hibernate deve impostare il valore corretto di "cee" nel momento in cui ha impostato B (perché non si sa quando qualcuno chiamerà getCee()). Il proxy non aiuta qui perché il proxy si trova già in oggetto non null.

Così il curriculum: se il vostro B-> C mappatura è obbligatoria (vincolata = true), Hibernate userà proxy per C con conseguente inizializzazione pigra. Ma se si concede B senza C, Hibernate deve solo controllare la presenza di C nel momento in cui carica B. Ma un SELEZIONA per controllare la presenza è solo inefficiente perché lo stesso SELECT non può semplicemente controllare la presenza, ma caricare l'intero oggetto. Così pigro il caricamento va via.

Problemi correlati