2015-10-21 16 views
16

Wie kann man ihre JPA-Entities so konfigurieren, dass sie verbundene Entitäten nur dann abrufen, wenn ein bestimmter Ausführungsparameter angegeben ist.Spring Data JPARepository: Wie man Kinder unter bestimmten Bedingungen abruft

Gemäß der Dokumentation von Spring, 4.3.9. Configuring Fetch- and LoadGraphs, müssen Sie die Annotation @EntityGraph verwenden, um Abrufrichtlinien für Abfragen anzugeben. Dies lässt mich jedoch nicht zur Laufzeit entscheiden, ob diese Entitäten geladen werden sollen.

Ich bin in Ordnung mit dem Abrufen der untergeordneten Entitäten in einer separaten Abfrage, aber um dies zu tun, müsste ich mein Repository oder Entitäten konfigurieren, keine untergeordneten abzurufen. Leider kann ich keine Strategien finden, wie dies zu tun ist. FetchPolicy wird ignoriert, und EntityGraph ist nur hilfreich beim Festlegen der Entitäten, die ich gerne abrufen möchte.

Angenommen, Account ist die übergeordnete und Contact ist das Kind, und ein Konto kann viele Kontakte haben.

Ich möchte in der Lage sein, dies zu tun:

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

Das Problem ist feder Daten abruft eifrig die Kontakte sowieso.

Der Konto Entity-Klasse sieht wie folgt aus:

@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 

} 

Die AccountRepository Klasse sieht wie folgt aus:

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

Beitrag der Entity-Klasse. – chrylis

+0

Sammlungen in JPA sind standardmäßig faul, Spring Data JPA ändert daran nichts. Wenn irgendwo in Ihrem Code ein Aufruf von 'getContacts' erfolgt, wird alles abgerufen, da dies der Standard ist. –

Antwort

9

Die faulen Abruf sollte richtig funktionieren, wenn keine Methoden des Objekts aus dem getContacts führen() aufgerufen wird.

Wenn Sie mehr manuelle Arbeit bevorzugen und wirklich Kontrolle darüber haben möchten (vielleicht mehr Kontexte je nach Anwendungsfall). Ich würde vorschlagen, dass Sie Kontakte aus der Kontoeinheit entfernen und stattdessen das Konto in den Kontakten zuordnen. Eine Möglichkeit, Hibernate anzuweisen, dieses Feld zu ignorieren, besteht darin, es mithilfe der @Transient-Annotation zuzuordnen.

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

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

    //getters & setters 

} 

Dann in Ihrem Service-Klasse, könnten Sie so etwas wie:

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; 
} 

Hoffnung das ist, was Sie suchen. Übrigens, der Code ist noch nicht getestet, also sollten Sie es wahrscheinlich noch einmal überprüfen.

5

finden Sie ein Beispiel, das mit JPA läuft 2.1.

Set das Attribut (e) Sie (mit Attributeliste) geladen werden soll:

Ihre Einheit mit Entity Graph Anmerkungen:

@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; 
    } 
} 

Ihre individuelle Schnittstelle:

public interface AccountRepository extends JpaRepository<Account, String> { 

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

Nur die Eigenschaft "accountId" wird loa sein eifrig. Alle anderen Eigenschaften werden beim Zugriff träge geladen.

Grüße, André

+0

Danke, dass Sie sich die Zeit nehmen zu antworten. In meinem Beispiel möchte ich keine Attribute für den Kontakt angeben. Wenn ich 'accountRepository.findOne (5)' aufruft, möchte ich, dass das Repository die Account-Entität ohne Kontakte zurückgibt. – cosbor11

+1

Gern geschehen. Sie haben nicht viele Mechanismen, um zu steuern, was in einer JPA-Entität geladen ist oder nicht. Du könntest EAGER oder LAZY holen, aber das weißt du schon. Das Verwenden eines DTO sollte eine Problemumgehung (CustomAccount für Beispiel, das ein Kontowrapper ist) sein. –

+0

Wow, das ist ein cooles Feature! Vielen Dank –

8

können Sie @Transactional dafür.

Dafür müssen Sie Ihr Konto Entität Faul holen.

@Transactional Anmerkungen sollten um alle Operationen herum angeordnet werden, die untrennbar sind.

Schreiben Sie Methode in Ihrer Service-Schicht, die ein Flag akzeptiert, um Kontakte eifrig zu holen.

@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 ist ein Service, der mehrere Anruf in einzelnen Transaktion ohne Schließ Verbindung mit Endpunkt machen.

Ich hoffe, Sie finden das nützlich. :)

Weitere Details refer this link

2

Federdaten ignorieren nicht fetch=FetchType.Lazy.

Mein Problem war, dass ich dozer-mapping verwendet, um meine Entitäten in Graphen zu konvertieren. Offensichtlich ruft dozer die Getter und Setter zwei Objekte auf der Karte, so brauchte ich ein benutzerdefiniertes Feld Mapper Konfiguration hinzuzufügen PersistentCollections zu ignorieren ...

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; 
    } 
} 
Verwandte Themen