2015-10-21 6 views
16

Jak można skonfigurować ich podmioty WZP aby nie sprowadzić podmioty powiązane, chyba że jest pewien parametr wykonanie.Wiosna danych JPARepository: Jak warunkowo sprowadzić dzieci udziałów w jednostkach

Zgodnie z dokumentacją Springa, , należy użyć adnotacji @EntityGraph, aby określić zasady pobierania dla zapytań, ale to nie pozwala mi decydować w czasie wykonywania, czy chcę załadować te elementy.

Nie mam nic przeciwko umieszczaniu elementów podrzędnych w osobnym zapytaniu, ale aby to zrobić, muszę skonfigurować moje repozytorium lub obiekty, aby nie pobierać żadnych elementów podrzędnych. Niestety, nie mogę znaleźć żadnej strategii, jak to zrobić. FetchPolicy jest ignorowany, a EntityGraph jest pomocny tylko przy określaniu, które elementy chcę odzyskać z niecierpliwością.

Załóżmy na przykład, że Account jest rodzicem, a Contact jest dzieckiem, a Konto może zawierać wiele Kontaktów.

Chcę móc to zrobić:

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

Problemem jest sprężynowy dane chętnie pobiera kontakty jakikolwiek.

klasa

Konto Podmiot wygląda następująco:

@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 

} 

Klasa AccountRepository wygląda następująco:

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

post klasa podmiot. – chrylis

+0

Kolekcje w JPA domyślnie są leniwe, Spring Data JPA nic w tym nie zmienia. Jeśli jest gdzieś wezwanie do 'getContacts' w twoim kodzie, to wszystko zostanie pobrane, ponieważ jest to domyślne. –

Odpowiedz

9

Leniwy pobieraniu powinno działać poprawnie, jeśli nie ma metody obiektu wynikało z getContacts() jest wywoływana.

Jeśli wolisz bardziej ręczną pracę i naprawdę chcesz mieć nad tym kontrolę (może więcej kontekstów w zależności od przypadku użycia). Sugeruję, abyś usunął kontakty z konta i zamiast tego mapuje konto w kontaktach. Jednym ze sposobów powiadomienia hibernacji o zignorowaniu tego pola jest mapowanie go za pomocą adnotacji @Transient.

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

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

    //getters & setters 

} 

Następnie w swojej klasie usług, można zrobić coś takiego:

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

nadzieję, że to jest to, czego szukasz. Btw, kod jest nietestowany, więc powinieneś prawdopodobnie sprawdzić ponownie.

5

Proszę znaleźć przykład kursujący z JPA 2.1.

ustawić atrybut (y) chcesz tylko załadować (z listy attributeNodes):

Twoja jednostka z Entity adnotacji wykresu:

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

interfejs niestandardowy:

public interface AccountRepository extends JpaRepository<Account, String> { 

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

Tylko właściwość "accountId" będzie loa zadedykował z zapałem. Wszystkie inne właściwości zostaną załadowane leniwie na dostęp.

Pozdrawiam Andre

+0

Dzięki za poświęcenie czasu na odpowiedź. W moim przykładzie nie chcę żadnych atrybutów wypełnionych na kontakcie. Kiedy nazywam "accountRepository.findOne (5)", chcę repozytorium zwrócić podmiotowi konta bez żadnych kontaktów. – cosbor11

+1

Nie ma za co. Nie masz wielu mechanizmów kontrolujących, co jest ładowane lub nie w jednostce JPA. Możesz użyć EAGER lub LAZY Fetching, ale już to wiesz. Używanie DTO powinno być obejściem (CustomAccount na przykład, który jest opakowaniem Konta). –

+0

Wow, to fajna funkcja! Dzięki –

8

Można użyć @Transactional do tego.

Do tego trzeba przynieść Ci konto podmiotowe leniwie.

Adnotacje należy umieszczać wokół wszystkich operacji, które są nierozłączne.

Metoda zapisu w warstwie usługi akceptująca jedną flagę w celu szybkiego pobrania kontaktów.

@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 jest usługą, która może sprawić wiele połączenia w jednej transakcji bez zamykania połączenia z punktem końcowym.

Mam nadzieję, że okaże się to przydatne. :)

Więcej szczegółów refer this link

2

Dane sprężyste nie ignorują fetch=FetchType.Lazy.

Mój problem polegał na tym, że używałem dozer-mapping do ukrywania moich obiektów na wykresach. Widocznie dozer nazywa pobierające i ustawiające mapować dwóch obiektów, więc musiałem dodać niestandardową konfigurację odwzorowujący pole do ignorowania 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; 
    } 
} 
Powiązane problemy