2015-10-21 14 views
16

Come configurare le proprie entità JPA per non recuperare le entità correlate a meno che non venga fornito un determinato parametro di esecuzione.Spring Data JPARepository: Come recuperare in modo subordinato le entrate dei bambini

In base alla documentazione di Spring, 4.3.9. Configuring Fetch- and LoadGraphs, è necessario utilizzare l'annotazione @EntityGraph per specificare il criterio di recupero per le query, tuttavia ciò non consente di decidere in fase di esecuzione se si desidera caricare tali entità.

Io sto bene con ottenere le entità figlio in una query separata, ma per farlo avrei bisogno di configurare il mio repository o entità per non recuperare alcun figlio. Sfortunatamente, non riesco a trovare alcuna strategia su come farlo. FetchPolicy viene ignorato e EntityGraph è utile solo quando si specificano le entità che si desidera recuperare con impazienza.

Ad esempio, supporre che Account sia il genitore e Contact è il figlio e un account può avere molti contatti.

Voglio essere in grado di fare questo:

if(fetchPolicy.contains("contacts")){ 
    account.setContacts(contactRepository.findByAccountId(account.getAccountId()); 
} 

Il problema è la primavera-dati avidamente recupera i contatti in ogni modo.

classe

Il Conto entità simile a questa:

@Entity 
@Table(name = "accounts") 
public class Account 
{ 
    protected String accountId; 
    protected Collection<Contact> contacts; 

    @OneToMany 
    //@OneToMany(fetch=FetchType.LAZY) --> doesn't work, Spring Repositories ignore this 
    @JoinColumn(name="account_id", referencedColumnName="account_id") 
    public Collection<Contact> getContacts() 
    { 
     return contacts; 
    } 

    //getters & setters 

} 

La classe AccountRepository si presenta così:

public interface AccountRepository extends JpaRepository<Account, String> 
{ 
    //@EntityGraph ... <-- has type= LOAD or FETCH, but neither can help me prevent retrieval 
    Account findOne(String id); 
} 
+0

Messaggio di classe l'entità. – chrylis

+0

Le raccolte in JPA sono pigre per impostazione predefinita, Spring Data JPA non cambia nulla su questo. Se c'è qualche chiamata a 'getContacts' nel tuo codice allora tutto verrà recuperato come questo è l'impostazione predefinita. –

risposta

9

L'operazione di recupero artificiale dovrebbe lavorare correttamente se esistono metodi dell'oggetto portato dalle getContacts() viene chiamato.

Se si preferisce più lavoro manuale, e si vuole veramente avere il controllo su questo (forse più contesti a seconda del caso d'uso). Ti suggerirei di rimuovere i contatti dall'entità account e mappare l'account nei contatti. Un modo per dire che l'ibernazione ignora quel campo è mapparlo usando l'annotazione @Transient.

@Entity 
@Table(name = "accounts") 
public class Account 
{ 
    protected String accountId; 
    protected Collection<Contact> contacts; 

    @Transient 
    public Collection<Contact> getContacts() 
    { 
     return contacts; 
    } 

    //getters & setters 

} 

Poi nella classe di servizio, si potrebbe fare qualcosa di simile:

public Account getAccountById(int accountId, Set<String> fetchPolicy) { 
    Account account = accountRepository.findOne(accountId); 
    if(fetchPolicy.contains("contacts")){ 
     account.setContacts(contactRepository.findByAccountId(account.getAccountId()); 
    } 
    return account; 
} 

Spero che questo è quello che stai cercando. A proposito, il codice non è stato testato, quindi dovresti controllare di nuovo.

5

Si prega di trovare un esempio che funziona con JPA 2.1.

impostare l'attributo (s) che si desidera solo caricare (con la lista attributeNodes):

vostro ente con Entity annotazioni grafico:

@Entity 
@NamedEntityGraph(name = "accountGraph", attributeNodes = { 
    @NamedAttributeNode("accountId")}) 
@Table(name = "accounts") 
public class Account { 

    protected String accountId; 
    protected Collection<Contact> contacts; 

    @OneToMany(fetch=FetchType.LAZY) 
    @JoinColumn(name="account_id", referencedColumnName="account_id") 
    public Collection<Contact> getContacts() 
    { 
     return contacts; 
    } 
} 

l'interfaccia personalizzata:

public interface AccountRepository extends JpaRepository<Account, String> { 

    @EntityGraph("accountGraph") 
    Account findOne(String id); 
} 

Solo la proprietà "accountId" sarà loa ded ardentemente. Tutte le altre proprietà verranno caricate pigramente all'accesso.

saluti, André

+0

Grazie per aver trovato il tempo di rispondere. Nel mio esempio, non voglio alcun attributo popolato sul contatto. Quando chiamo "accountRepository.findOne (5)", voglio che il repository restituisca l'entità account senza alcun contatto. – cosbor11

+1

Prego. Non hai molti meccanismi per controllare cosa viene caricato o meno in un'entità JPA. Potresti usare il recupero EAGER o LAZY, ma lo sai già. L'utilizzo di un DTO dovrebbe essere una soluzione alternativa (CustomAccount, ad esempio, che è un wrapper Account). –

+0

Wow, questa è una bella funzionalità! Grazie –

8

È possibile utilizzare @Transactional per questo.

Per questo è necessario recuperare il tuo account entità Lazily.

@Transactional Le annotazioni devono essere posizionate attorno a tutte le operazioni che sono inseparabili.

Metodo di scrittura nel livello di servizio che accetta un flag per recuperare i contatti con entusiasmo.

@Transactional 
public Account getAccount(String id, boolean fetchEagerly){ 
    Account account = accountRepository.findOne(id); 

    //If you want to fetch contact then send fetchEagerly as true 
    if(fetchEagerly){ 
     //Here fetching contacts eagerly 
     Object object = account.getContacts().size(); 
    } 
} 

@Transactional è un servizio che può effettuare chiamate multiple in un'unica transazione senza chiudere il collegamento con il punto finale.

Spero che tu trovi utile. :)

Per maggiori dettagli refer this link

2

I dati di primavera non vengono ignorati fetch=FetchType.Lazy.

Il mio problema era che stavo usando dozer-mapping per nascondere le mie entità ai grafici. Evidentemente dozer chiama i getter e setter per mappare due oggetti, quindi ho bisogno di aggiungere una configurazione di campo mapper personalizzato di ignorare PersistentCollections ...

GlobalCustomFieldMapper.java:

public class GlobalCustomFieldMapper implements CustomFieldMapper 
{ 
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) 
    { 
     if (!(sourceFieldValue instanceof PersistentCollection)) { 
      // Allow dozer to map as normal 
      return; 
     } 
     if (((PersistentCollectiosourceFieldValue).wasInitialized()) { 
      // Allow dozer to map as normal 
      return false; 
     } 

     // Set destination to null, and tell dozer that the field is mapped 
     destination = null; 
     return true; 
    } 
} 
Problemi correlati