티스토리 뷰

 

백엔드 개발중에 DB에서 필드를 조작한 후 조작한 값에 따라 groupBy로 그룹핑을 해야하고 집계를 내야하는데

JPA queryDSL로 짜여져서 필드조작이 자유롭지 않은 상태가 발생했다. 

필드를 subString으로 조작해보려고 하니 사용자정의함수를 추가해주고, QClass등 추가해줘야하는 것들이 많았다. 

아직 JPA가 익숙하지 않은 상태라 무작정 따라하기보다, 로직단에서 그룹핑할 수 있는 방법을 찾아보던 중에 스트림처리로 groupby처럼 키값을 기준으로 그룹핑할 수 있는 방법을 찾았다.

GroupingKey를 나타내는 class를 만들어주고 Java1.8의 stream + groupingBy 기능을 활용하면 groupingBy를 수행할 수 있다.

 

1. 먼저 Grouping 에 활용될 GroupKey 클래스를 만들어준다.

package kr.miryang.ews.external.obsv.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import java.util.Objects;

@Builder
@Getter
@Setter
public class GroupKey {

    String color;
    String width;

    @Override
    public int hashCode(){
//        return this.color.length() + this.width.length();
        return Objects.hash(color, width);
    }

    @Override
    public boolean equals(Object obj){
        GroupKey other = (GroupKey) obj;

        if (other.getColor().equals(this.color)
                && other.getWidth().equals(this.width)) {
            return true;
        }
        return false;
    }
}

위 GroupKey 클래스에서 Stream groupingBy에서 활용할 equals, hashCode 함수를 반드시 override 해주어야 한다.

equals메서드에서 groupKey가 모두 같을때 비교 true 임을 명시해준다. equals를 override했으면  hashCode도 반드시 override해주어야 한다. 만일 없으면 groupingBy가 동작하지 않는다.

hashCode는 객체 비교를 위해 유일값이 나오도록 구현해야 하며, 나는 객체간 비교를 해야하기 때문에 필드값들로 해시코드를 생성해주었다. (혹시 객체간 비교 해시코드 생성의 좋은 예시가 있다면 알려주세요.)

 

2. 그룹핑 대상이 되는 클래스에 그룹키 객체를 추가해준다.

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Builder
@Getter
@Setter
public class Box {

    String name;
    String color;
    String width;
    String height;

    GroupKey groupKey;
}

 

나는 클래스가 JPA에서 사용하기위해 지정된 @Entity클래스여서 그룹키를 추가해주니 context환경에 문제가 생겨 해당 엔티티 클래스 안에 아래와 같이 @Transient 어노테이션을 붙여 추가해주었다.

@Transient
GroupKey groupKey;
public void setGroupKey(GroupKey groupKey) {
	this.groupKey = groupKey;
}

 

그리고 아래와같이 테스트를 해보면

import static java.util.stream.Collectors.groupingBy;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class TestStreamGrouping {

    @Test
    public void name() {
        List<Box> box = Lists.newArrayList(
                Box.builder().no(1).name("Box1").color("red").width("1").height("2").build(),
                Box.builder().no(2).name("Box2").color("yellow").width("1").height("2").build(),
                Box.builder().no(3).name("Box3").color("red").width("1").height("5").build(),
                Box.builder().no(4).name("Box4").color("yellow").width("2").height("3").build(),
                Box.builder().no(5).name("Box5").color("blue").width("1").height("2").build()
        );

        List<Box> boxsWithGroupsKey = Optional.ofNullable(box).map(list -> list.stream()
                .peek(box -> box.setGroupKey(GroupKey.builder()
                        .color(box.getColor())
                        .width(box.getWidth()).build()))
                .collect(Collectors.toList())).orElse(Lists.newArrayList());

        Map<GroupKey, List<Box>> collectResult = boxsWithGroupsKey.stream().collect(groupingBy(Box::getGroupKey));

        assertThat(collectResult).isNotNull();
    }
}

위 테스트 케이스 결과를 찍어보면
color= "red", width= "1" 에 대해서 count  2 개로 그룹핑이되서 결과가 나온것을 볼 수 있다.

collectResult = {HashMap@1058}  size = 4
 {GroupKey@1079}  -> {ArrayList@1080}  size = 2
  key = {GroupKey@1079} 
   color = "red"
   width = "1"
  value = {ArrayList@1080}  size = 2
 {GroupKey@1081}  -> {ArrayList@1082}  size = 1
  key = {GroupKey@1081} 
   color = "yellow"
   width = "1"
  value = {ArrayList@1082}  size = 1
 {GroupKey@1083}  -> {ArrayList@1084}  size = 1
  key = {GroupKey@1083} 
   color = "yellow"
   width = "2"
  value = {ArrayList@1084}  size = 1
 {GroupKey@1085}  -> {ArrayList@1086}  size = 1
  key = {GroupKey@1085} 
   color = "blue"
   width = "1"
  value = {ArrayList@1086}  size = 1
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함