Spring Данные JPARepository: как условно забирать дочерние элементы

Как настроить свои объекты JPA, чтобы не получать связанные объекты, если не указан определенный параметр выполнения.

Согласно документации Spring, 4.3.9. Конфигурируя Fetch- и LoadGraphs, вам нужно использовать аннотацию @EntityGraph, чтобы указать политику выборки для запросов, однако это не позволяет мне решить во время выполнения, хочу ли я загружать эти объекты.

Я в порядке с получением дочерних объектов в отдельном запросе, но для этого мне нужно настроить мой репозиторий или сущности, чтобы не извлекать ни одного из детей. К сожалению, я не могу найти стратегии для того, как это сделать. FetchPolicy игнорируется, а EntityGraph полезно только при определении того, какие объекты я хочу получить с нетерпением.

Например, предположим, что Account является родителем, а Contact является дочерним, а в учетной записи может быть много контактов.

Я хочу иметь возможность сделать это:

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

Проблема заключается в том, что spring -data охотно выбирает контакты.

Класс Entity Account выглядит следующим образом:

@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

}

Класс AccountRepository выглядит следующим образом:

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

Ответы

Ответ 1

Лесткая выборка должна работать правильно, если не вызваны методы объекта, полученные из getContacts().

Если вы предпочитаете более ручную работу и действительно хотите иметь контроль над этим (возможно, больше контекстов в зависимости от варианта использования). Я предлагаю вам удалить контакты из объекта учета и вместо этого сопоставить учетную запись в контактах. Один из способов сказать hibernate игнорировать это поле - сопоставить его с помощью аннотации @Transient.

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

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

    //getters & setters

}

Затем в вашем классе обслуживания вы можете сделать что-то вроде:

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

Надеюсь, это то, что вы ищете. Btw, код не тестировался, поэтому вам, вероятно, следует еще раз проверить.

Ответ 2

Вы можете использовать @Transactional для этого.

Для этого вам нужно получить вашу учетную запись Lazily.

@Transactional Аннотации должны быть размещены вокруг всех операций, которые неотделимы.

Напишите способ в своем сервисном слое, который принимает один флаг, чтобы получать контакты с нетерпением.

@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 - это Служба, которая может выполнять множественный вызов в одной транзакции без закрытия соединения с конечной точкой.

Надеюсь, вы сочтете это полезным.:)

Для получения дополнительной информации обратитесь к этой ссылке

Ответ 3

Пожалуйста, найдите пример, который работает с JPA 2.1.

Установите атрибуты, которые вы хотите загрузить (только с атрибутомNodes):

Ваша сущность с аннотациями диаграмм Entity:

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

Ваш пользовательский интерфейс:

public interface AccountRepository extends JpaRepository<Account, String> {

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

Только свойство "accountId" будет загружено с нетерпением. Все остальные свойства будут загружаться лениво при доступе.

С уважением, Андре

Ответ 4

Spring данные не игнорируют fetch=FetchType.Lazy.

Моя проблема заключалась в том, что я использовал dozer-mapping для скрытия моих объектов на графиках. Очевидно, dozer вызывает геттеры и сеттеры для сопоставления двух объектов, поэтому мне нужно было добавить настраиваемую конфигурацию картографа поля, чтобы игнорировать 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;
    }   
}