添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
重情义的鸡蛋  ·  Spring Data ...·  6 天前    · 
玩命的拖把  ·  Spring JPA - ...·  1 周前    · 
火爆的薯片  ·  成都康特·  1 月前    · 
乐观的芒果  ·  エラー:Argument of type ...·  3 月前    · 
唠叨的火车  ·  Change box width in ...·  7 月前    · 
年轻有为的电脑桌  ·  How to use MAMP's ...·  11 月前    · 
文质彬彬的熊猫  ·  奥迪Q4 ...·  1 年前    · 

Database에 Sales에 대한 Table이 있다고 가정할 때

만들고자 하는 DTO는 다음과 같습니다.

(필드는 DB의 애트리뷰트의 자료형과 맞추어서 만들었습니다.)

@Getter @NoArgs Constructor @AllArgsConstructor public class SalesCountDTO { private long id ; private String name ; private Long sales ;

Java Stream 활용

. . . @Transactional public List < SalesCountDTO > getSalesByStream ( ) { return saleRepo . findAll ( ) . parallelStream ( ) . map ( sale -> new SalesCountDTO ( sale . getId ( ) , sale . getName ( ) , sale . getSales ( ) ) ) . collect ( Collectors . toMap ( sum -> sum . getId ( ) , Function . identify ( ) , ( sum1 , sum2 ) -> new SaleSum ( sum1 . getId ( ) , sum1 . getSales . add ( sum2 . getSales ( ) ) . values ( ) . stream ( ) . sorted ( Comparator . comparing ( SalesCountDTO : : getsales ) ) . collect ( Collectors . toList ( ) )

collect 를 이용하면 다양한 groupBy 를 사용할 수 있습니다.
Collectors.toMap 을 이용하여 Sale.getId() 를 기준으로 SaleSum 을 생성하는 방식으로 사용하였습니다.

Stream 을 사용하게 되면 어플리케이션 단에서 구현된 내용을 쉽게 수정하거나 파악할 수 있는 장점이 있지만 전체 Data를 어플리케이션에서 group by 를 하기 때문에 데이터가 많아지면 느려지고 메모리 사용이 많아지기에 적절한 방식이 아닙니다.

JPQL 활용

JPA JPQL Projection 을 이용해서 DTO Query 결과값을 담는다.

@Repository public interface SalesRepository extends JpaRepository < Sales , Long > { @Query ( "SELECT " + "new study.jpa.sales.dto.SalesCountDTO(s.Id, s.name, SUM(s.sale)) " + "FROM Sale s " + "GROUP BY s.ID" List < SalesCountDTO > findSalesCountDtoJPQL ( ) ;

JPQL 을 이용하면 Query 에 대한 타입 안정성과 Query 안정성을 바로 확인 할수 있지만 Projection 을 사용할 때 new 를 통해 생성자를 쿼리 내에서 호출해 줘야하는 단점이 있습니다.

Native Query 활용

Native Query 에 대해 Entity 가 아닌 Retury 값을 반환받기 위해서는 Interface based Projection 을 활용해야 합니다.

Interface 선언

public interface SalesCountInterface{
	Long getId();
    String getName();
    Long getSales();

Interface에 맞게 Native Query를 작성

@Repository
public interface SalesRepository extends JpaRepository<Sales, Long>{
	@Query(
    	"SELECT "
        + "s.Id AS id " 
        + ", s.Name As name " 
        + ", SUM(Sales) AS sales " 
        + "FROM Sale s "
        + "GROUP BY s.ID"
      , nativeQuery = true
    List<SalesCountDTO> findSalesCountDtoNativeQuery();

JPQL과 유사하지만 nativeQuery=true를 통해 순수 Query로 작성되어 있으며, SELECT된 각 칼럼을 Interface 명에 맞춰 Alias를 설정해야 됩니다.

Native Query를 활용하게 되면 쿼리 자체를 Database에서 빠르게 검증 가능하지만, 반환된 Interface를 별도의 DTO로 변환해야 할 필요성이 생깁니다.

QueryDSL

QueryDSL을 활용한 Group By 쿼리는 JPAQuery를 직접 활용해야 합니다.

@Service
@Slf4j
@RequiredArgsConstructor
public class SalesServiceImpl implements SalesService {
    @PersistenceContext
    private EntityManager entityManager;
	@Transactional
	public List<SalesCountDTO> getSalesDTOByQueryDSL(){
        QSale qSale = QSale.sale;
        JPAQueryFactory qf = new JPAQueryFactory(entityManager);
        JPAQuery<SalesCountDTO> query = qf.from(qSale)
        								.groupBy(qSale.id)
                                        .select(
                                        	Projections.bean(
                                            	SalesCountDTO,
                                                qSale.id,
                                                qSale.name,
                                                qSale.sales.sum().as("sales")
		return query.fetch();                                        

QueryDSL 에서 Group By를 사용하기 위해서는 EntityManager를 통해 JPAQuery를 직접 생성해서 사용해야 하는 단점이 있지만, QueryDSL의 장점인 다양한 조건의 쿼리를 생성할 수 있다는 장점이 있습니다. (Optional 한 쿼리 조건을 각각 적용 가능)

230건의 데이터를 각 100회씩 호출했을 때 응답 시간은
Java Stream이 가장 속도가 느리며 나머지는 실제 쿼리가 비슷하기 때문에 평균 응답 시간도 같게 나왔습니다.

다만 JPQLQueryDSL은 초기 쿼리 파싱 때문에 Max 속도가 다른 것을 볼 수 있습니다.