복붙노트

[SPRING] Spring Data JPA 저장소 : 조건 적으로 자식 엔티티를 가져 오는 방법

SPRING

Spring Data JPA 저장소 : 조건 적으로 자식 엔티티를 가져 오는 방법

특정 실행 매개 변수가 제공되지 않는 한 관련 엔티티를 페치하지 않도록 어떻게 JPA 엔티티를 구성 할 수 있습니까?

Spring의 문서에 따르면, 4.3.9. Fetch 및 LoadGraphs를 구성하려면 @EntityGraph 주석을 사용하여 쿼리에 페치 정책을 지정해야하지만, 런타임시 해당 엔터티를로드할지 여부를 결정할 수는 없습니다.

자식 엔티티를 별도의 쿼리로 가져 오는 것은 괜찮지 만 그렇게하기 위해 리포지토리 나 엔티티를 구성하여 자식을 검색하지 않아야합니다. 불행히도, 나는 이것을하는 방법에 대한 어떤 전략도 찾을 수없는 것 같습니다. FetchPolicy는 무시되며 EntityGraph는 열심히 검색 할 엔터티를 지정할 때만 유용합니다.

예를 들어 계정이 상위이고 연락처가 하위이고 계정에 많은 연락처가있을 수 있다고 가정합니다.

나는 이것을 할 수 있기를 바란다.

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

문제는 스프링 데이터가 어쨌든 연락처를 열심히 가져 오는 것입니다.

Account Entity 클래스는 다음과 같습니다.

@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. ==============================

    1.getContacts ()가 호출되어 객체의 메서드가 없으면 lazy fetch가 제대로 작동해야합니다.

    getContacts ()가 호출되어 객체의 메서드가 없으면 lazy fetch가 제대로 작동해야합니다.

    더 많은 수동 작업을 선호하고 실제로이 작업을 제어하고 싶다면 (유스 케이스에 따라 더 많은 컨텍스트가 필요할 수 있습니다.) 계정 엔터티에서 연락처를 제거하고 대신 연락처에 계정을 매핑하는 것이 좋습니다. 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. ==============================

    2.이 경우 @Transactional을 사용할 수 있습니다.

    이 경우 @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();   
        }
    }
    

    희망이 유용합니다. :)

    자세한 내용은이 링크를 참조하십시오.

  3. ==============================

    3.JPA 2.1에서 실행되는 예제를 찾으십시오.

    JPA 2.1에서 실행되는 예제를 찾으십시오.

    로드하려는 속성 만 attributeNodes 목록과 함께 설정하십시오.

    엔티티 그래프 주석이있는 엔티티 :

    @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. ==============================

    4.스프링 데이터는 fetch = FetchType.Lazy를 무시하지 않습니다.

    스프링 데이터는 fetch = FetchType.Lazy를 무시하지 않습니다.

    내 문제는 도저 매핑을 사용하여 내 엔티티를 그래프로 변환하는 것이 었습니다. 분명히 도저는 getters와 setter를 두 개의 객체를 매핑하도록 호출하므로 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;
        }   
    }
    
  5. from https://stackoverflow.com/questions/33266952/spring-data-jparepository-how-to-conditionally-fetch-children-entites by cc-by-sa and MIT license