[SPRING] Spring 데이터 JPA. findAll () 메소드에서 ID 목록 만 얻는 방법
SPRINGSpring 데이터 JPA. findAll () 메소드에서 ID 목록 만 얻는 방법
나는 매우 복잡한 모델을 가지고있다. 엔티티는 많은 관계를 가지고 있습니다.
Spring Data JPA를 사용하려고 시도하고 저장소를 준비했습니다.
하지만 개체를 매우 큰 때문에 성능 문제가있는 개체에 대한 사양 가진 findAll () 메서드를 호출 할 때. 나는 내가 다음과 같은 메소드를 호출 할 때 이것을 알기 때문에 :
@Query(value = "select id, name from Customer ")
List<Object[]> myFindCustomerIds();
성능에 문제가 없었습니다.
하지만 내가 호출 할 때
List<Customer> findAll();
성능에 큰 문제가있었습니다.
문제는 내가 고객을위한 사양으로 findAll 메소드를 호출해야하는데, 이는 왜 객체 배열 목록을 반환하는 메소드를 사용할 수 없는지입니다.
고객 개체에 대한 사양을 가진 모든 고객을 찾는 방법을 작성하는 방법이지만 ID 만 반환합니다.
이렇게 :
List<Long> findAll(Specification<Customer> spec);
도와주세요.
해결법
-
==============================
1.@Query 어노테이션을 사용하지 않는 이유는 무엇입니까?
@Query 어노테이션을 사용하지 않는 이유는 무엇입니까?
@Query ( "# {# entityName} p에서 p.id 선택") List
getAllIds (); 내가 볼 수있는 유일한 단점은 속성 ID가 변경 될 때이지만 매우 일반적인 이름이고 변경할 가능성이 없으므로 (id = 기본 키) 확인해야합니다.
-
==============================
2.이것은 이제 Projections를 사용하는 Spring 데이터에 의해 지원됩니다 :
이것은 이제 Projections를 사용하는 Spring 데이터에 의해 지원됩니다 :
interface SparseCustomer { String getId(); String getName(); }
고객 저장소보다
List<SparseCustomer> findAll(Specification<Customer> spec);
편집하다: Redouane ROUND에서 지적한 바와 같이 현재 사양과 함께 ROUND Projections는 버그로 인해 작동하지 않습니다.
그러나이 Spring Data Jpa 결함을 해결할 수있는 spec-with-projection 라이브러리를 사용할 수 있습니다.
-
==============================
3.나는 그 문제를 해결했다.
나는 그 문제를 해결했다.
(결과적으로 우리는 ID와 이름만을 가진 희소 고객 객체를 가질 것입니다)
public interface SparseCustomerRepository { List<Customer> findAllWithNameOnly(Specification<Customer> spec); }
@Service public class SparseCustomerRepositoryImpl implements SparseCustomerRepository { private final EntityManager entityManager; @Autowired public SparseCustomerRepositoryImpl(EntityManager entityManager) { this.entityManager = entityManager; } @Override public List<Customer> findAllWithNameOnly(Specification<Customer> spec) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<Tuple> tupleQuery = criteriaBuilder.createTupleQuery(); Root<Customer> root = tupleQuery.from(Customer.class); tupleQuery.multiselect(getSelection(root, Customer_.id), getSelection(root, Customer_.name)); if (spec != null) { tupleQuery.where(spec.toPredicate(root, tupleQuery, criteriaBuilder)); } List<Tuple> CustomerNames = entityManager.createQuery(tupleQuery).getResultList(); return createEntitiesFromTuples(CustomerNames); } private Selection<?> getSelection(Root<Customer> root, SingularAttribute<Customer, ?> attribute) { return root.get(attribute).alias(attribute.getName()); } private List<Customer> createEntitiesFromTuples(List<Tuple> CustomerNames) { List<Customer> customers = new ArrayList<>(); for (Tuple customer : CustomerNames) { Customer c = new Customer(); c.setId(customer.get(Customer_.id.getName(), Long.class)); c.setName(customer.get(Customer_.name.getName(), String.class)); c.add(customer); } return customers; } }
-
==============================
4.불행히도 투영법은 사양과 호환되지 않습니다. JpaSpecificationExecutor는 리포지토리 (List
findAll (Specification var1);)가 관리하는 집계 루트로 형식화 된 List 만 반환합니다. 불행히도 투영법은 사양과 호환되지 않습니다. JpaSpecificationExecutor는 리포지토리 (List
findAll (Specification var1);)가 관리하는 집계 루트로 형식화 된 List 만 반환합니다. 실제 해결 방법은 Tuple을 사용하는 것입니다. 예 :
@Override public <D> D findOne(Projections<DOMAIN> projections, Specification<DOMAIN> specification, SingleTupleMapper<D> tupleMapper) { Tuple tuple = this.getTupleQuery(projections, specification).getSingleResult(); return tupleMapper.map(tuple); } @Override public <D extends Dto<ID>> List<D> findAll(Projections<DOMAIN> projections, Specification<DOMAIN> specification, TupleMapper<D> tupleMapper) { List<Tuple> tupleList = this.getTupleQuery(projections, specification).getResultList(); return tupleMapper.map(tupleList); } private TypedQuery<Tuple> getTupleQuery(Projections<DOMAIN> projections, Specification<DOMAIN> specification) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Tuple> query = cb.createTupleQuery(); Root<DOMAIN> root = query.from((Class<DOMAIN>) domainClass); query.multiselect(projections.project(root)); query.where(specification.toPredicate(root, query, cb)); return entityManager.createQuery(query); }
여기서 투영은 루트 투영을위한 기능적 인터페이스입니다.
@FunctionalInterface public interface Projections<D> { List<Selection<?>> project(Root<D> root); }
SingleTupleMapper 및 TupleMapper는 TupleQuery 결과를 반환 할 객체에 매핑하는 데 사용됩니다.
@FunctionalInterface public interface SingleTupleMapper<D> { D map(Tuple tuple); } @FunctionalInterface public interface TupleMapper<D> { List<D> map(List<Tuple> tuples); }
Projections<User> userProjections = (root) -> Arrays.asList( root.get(User_.uid).alias(User_.uid.getName()), root.get(User_.active).alias(User_.active.getName()), root.get(User_.userProvider).alias(User_.userProvider.getName()), root.join(User_.profile).get(Profile_.firstName).alias(Profile_.firstName.getName()), root.join(User_.profile).get(Profile_.lastName).alias(Profile_.lastName.getName()), root.join(User_.profile).get(Profile_.picture).alias(Profile_.picture.getName()), root.join(User_.profile).get(Profile_.gender).alias(Profile_.gender.getName()) ); Specification<User> userSpecification = UserSpecifications.withUid(userUid); SingleTupleMapper<BasicUserDto> singleMapper = tuple -> { BasicUserDto basicUserDto = new BasicUserDto(); basicUserDto.setUid(tuple.get(User_.uid.getName(), String.class)); basicUserDto.setActive(tuple.get(User_.active.getName(), Boolean.class)); basicUserDto.setUserProvider(tuple.get(User_.userProvider.getName(), UserProvider.class)); basicUserDto.setFirstName(tuple.get(Profile_.firstName.getName(), String.class)); basicUserDto.setLastName(tuple.get(Profile_.lastName.getName(), String.class)); basicUserDto.setPicture(tuple.get(Profile_.picture.getName(), String.class)); basicUserDto.setGender(tuple.get(Profile_.gender.getName(), Gender.class)); return basicUserDto; }; BasicUserDto basicUser = findOne(userProjections, userSpecification, singleMapper);
도움이되기를 바랍니다.
from https://stackoverflow.com/questions/30331767/spring-data-jpa-how-to-get-only-a-list-of-ids-from-findall-method by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] DELETE 요청 본문에 데이터 전달 (0) | 2019.03.13 |
---|---|
[SPRING] Spring Rest 서비스와 Jersey Rest Service 및 Spring + Jersey 솔루션의 차이점은 무엇입니까? (0) | 2019.03.13 |
[SPRING] 스프링의 공유 EntityManager에서 트랜잭션을 수동으로 시작하는 방법은 무엇입니까? (0) | 2019.03.13 |
[SPRING] 어떻게 봄 부팅 응용 프로그램을 다시 시작하지 않고 런타임에 로그 수준을 변경합니까 (0) | 2019.03.13 |
[SPRING] 스프링 MVC (async) 대 스프링 WebFlux (0) | 2019.03.13 |