2009-11-22 17 views
10

Sono in esecuzione in eccezioni LazyLoading come la maggior parte delle persone che tentano la comunicazione remota con un ORM. Nella maggior parte dei casi, il passaggio al recupero fugace risolve il problema (caricamento lento/query non atomiche/sicurezza thread/n + 1 problema ...). Ma il recupero impaziente ha anche degli svantaggi se si ha a che fare con un grafo di oggetti veramente grande.Strategie di caricamento Lazy/Eager in casi remoti (JPA)

Il caricamento dell'intero oggetto grafico non è necessario nei casi più comuni. È brutto caricare più dati necessari (o caricarli dal db ed estrarre il sottoinsieme necessario).

Quindi quali sono i modi alternativi per risolvere questo tipo di problemi (in fase di esecuzione)?
ho visto:

  • iniettare un accesso dipendenza dati in oggetto di dominio e lasciare l'oggetto a decidere sia per caricare pigri o ansiosi: si sente male! Il livello del dominio dovrebbe essere indipendente da qualsiasi servizio. L'iniezione di dominio è anche un'operazione costosa. Il dominio dovrebbe essere l'accesso ai dati ignorante e dovrebbe essere utilizzato con o senza accesso ai dati.
  • Fetch tutto ciò che è pigro tranne casi d'uso che richiedono più dati: sembra migliore per le prestazioni, ma in questo modo impone molti client => server/database roundtrips. L'inizializzazione dei campi pigri può anche soffrire il dolore (provato con JPA). In questo modo non si sente generico ed è soggetto alle stesse restrizioni pigro menzionate sopra.
  • Persistenza incapsulare nella classe Lazy: più complessità, nessuna procedura ottimale per l'interoperabilità con ORM. Livello dei servizi di gonfiore (tanto codice "scritto a mano" si sente male).
  • Usa proiezioni complete per ogni caso d'uso: finiremo in SQL e perderemo il vantaggio di un ORM.
  • Un livello DTO/Virtual Proxy aumenta la complessità e rende il codice più difficile da mantenere (antipatia Wormhole >> Bloat).

Ho pensato molto a un altro modo. Forse la proiezione generica white./black listning è una soluzione.

Idea (lista nera): definire un elenco di nomi classe con i limiti per un'operazione di recupero. Se una proprietà corrisponde ed è pigro, rimuovere il proxy pigro (CGLIB) e compilare il valore con null. Altrimenti, evitare semplicemente di recuperare (e lasciare il valore su null). Quindi possiamo stabilire confini chiari nei nostri DAO.

Esempio: ProductDao.findByName("Soap",Boundaries.BLACKLIST,"Category, Discount") gli ultimi due parametri possono anche essere associati a un oggetto Contorni.

Idea (lista bianca): come lista nera, ma è necessario dichiarare proprietà con deve essere caricato in una lista bianca.

Cosa ne pensi di una simile soluzione? (Possibili problemi, restrizioni, vantaggi ...) Come dovrei scrivere questo in java? Forse tramite AOP per abbinare i metodi DAO (perché sono in grado di modificare il comportamento del proxy cglib lì)?

+0

Che tipo di architettura hai? Stai usando GWT, per esempio? – Kaitsu

+0

Servizi Web JAX-WS tramite implementazione di riferimento (Metro) –

risposta

6
  1. È possibile eliminare tutte le raccolte e utilizzare invece NamedQueries. Abbiamo utilizzato questo approccio in un progetto (EJB + Swing), e ha funzionato abbastanza bene, quindi è possibile determinare i dati esatti da recuperare. NamedQueries sono normali query, immaginatele come PreparedStatement-s. L'idea non è quella di creare/recuperare/aggiornare/eliminare singoli oggetti con le query.L'idea è di recuperare le tue collezioni con le query. Ad esempio, invece di mappare un elenco @ManyToMany, definire un NamedQuery che recupera tale elenco. Pertanto puoi recuperare i dati della raccolta separatamente e solo quando ne hai bisogno, non automaticamente.

  2. Utilizzare un proxy personalizzato (utilizzando CGLIB) per gli oggetti trasferiti - ogni volta che una raccolta viene referenziata (tramite il suo getter), tentare la retreival e catturare qualsiasi LazyInitializationException ed effettuare una chiamata al livello server per i dati richiesti.

  3. Proprio come il precedente, ma rende i proxy solo delle raccolte, nel modo in cui Hibernate le inoltra quando è necessaria un'inizializzazione pigra.

  4. Inoltre, dare un'occhiata al modello Value List Handler - potrebbe essere utile.

(si può anche usare hibernate.max_fetch_depth (se si utilizza Hibernate) con una combinazione di quanto sopra, se è adatto per il vostro caso.)

+0

Le query con nome terminano con la duplicazione del codice per le operazioni CRUD se vengono applicate a tutte le classi di dominio. Uso i DAO generici cablati e creati dalla primavera. È anche molto importante evitare la dipendenza da ibernazione. Decoratore/Proxy via AOP sembra carino. Ma il tuo approccio non è disaccoppiato dal livello dati (domain/service mix | dominio/data access mix) perché il getter del dominio dovrebbe effettuare chiamate al server. Cerco di evitare l'iniezione nel modello (logica per le chiamate, ecc.) Come descritto nella mia domanda. –

+1

Bene, Hibernate ad esempio utilizza la stessa idea per recuperare raccolte pigre quando la sessione è ancora lì - rende un proxy della raccolta che contiene la logica. Quindi non è un grosso problema farlo. NamedQueries non è una duplicazione dell'operazione CURD, ma al posto delle annotazioni @ * ToMany (e delle relative informazioni) - un po 'più prolisso, comunque. – Bozho

+0

P.S. Perché è importante evitare la dipendenza da Hibernate?Se si utilizza Hibernate come provider di persistenza, alla fine si finisce per utilizzare le sue annotazioni nei casi in cui JPA non ha l'opzione richiesta. :) – Bozho

1

Anche se ci vuole un po 'di lavoro e per JAX-WS/JAXB richiede versioni recenti di quelle librerie, questa è una soluzione molto elegante: creare un marshaller in grado di verificare se gli oggetti/le raccolte sono stati inizializzati.

come descritto di seguito: https://forum.hibernate.org/viewtopic.php?f=1&t=998896

1

Al giorno d'oggi (2013), è possibile mantenere lazy-caricamento se a distanza il vostro servizio con GraniteDS.

Questo dovrebbe serializzare correttamente le entità JPA o Hibernate non inizializzando le relazioni lazy e mantenendo lo stato lazy per il client. Se accedi a tali relazioni sul client, le recupererà in modo trasparente in background.

Inoltre, GraniteDS sembra essere in grado di eseguire il caricamento lazy inverso, il che significa che quando si inviano oggetti modificati sul server, non invierà entità invariate, rendendo così la comunicazione server necessaria molto efficiente.

Non sono un esperto di GraniteDS (ancora) ma sembra essere in grado di integrarsi con i livelli di servizio JEE6 e Spring e funziona con tutti i più importanti provider JPA.

Ovviamente, occorrerà nascondere il remoting basato su GraniteDS dietro un'interfaccia di servizio per massimizzare il comportamento trasparente, ma ciò può essere fatto facilmente se il client usa anche Spring (in modo da iniettare il servizio in base alle esigenze dell'ambiente) .