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