티스토리 뷰

컬럼 데이터를 합쳐서 결과값을 보여줘야할 때 쓰는 SQL함수로는 group_concat과 string_agg가 있다.

JPA(queryDsl) 환경에서 어떻게 해당 기능을 쓰는 방법이다.

사실 쿼리는 간단하게 날리고 데이터 전처리는 어플리케이션 단에서 해줘야한다고하지만 당장 어떻게 짜야할지 생각이 안나니 group_concat으로 데이터를 배열 형식으로 받아줬다.


해당 쿼리를

select p.post_id, u.nickname,count(distinct (c.comment_id)) as commentCount ,count(distinct(p2.pick_id)) as likeCount, group_concat(distinct (h.hashtag_name)) as hashtags
from  post p
join users u on p.user_id = u.user_id
join post_category_relation pcr on p.post_id = pcr.post_id
left join pick p2 on p.post_id = p2.post_id
left join comment c on p.post_id = c.post_id
left join hashtag h on p.post_id = h.post_id
group by p.post_id
;

이렇게 바꿔주어야하는데

queryFactory.select(new QFeedPost(
                        p.postId,
                        u.nickname,
                        p.content,
                        c.commentId.countDistinct(),
                        pp.pickId.countDistinct(),
                        Expressions.simpleTemplate(String.class, "group_concat({0})", h.hashtagName)))
                .from(p)
                .where(pcr.categoryCode.in(categoryCode))
                .join(u).on(p.user.eq(u))
                .join(pcr).on(pcr.post.eq(p))
                .leftJoin(pp).on(pp.post.eq(p))
                .leftJoin(c).on(c.post.eq(p))
                .leftJoin(h).on(h.post.eq(p))
                .groupBy(p.postId)
                .orderBy(p.createdDate.desc())
                .limit(pageable.getPageSize())
                .offset(pageable.getOffset())
                .fetch();

나머지는 queryDsl에서 제공하는 메서드이지만 group_concat의 기능을 사용할때 해당 기능을 따로 설정해주지 않으면 StringPath에서 해당 메서드를 사용할 수 없다고 뜨기도한다. (혹은 아래 에러가 뜬다)

java.lang.IllegalArgumentException: No pattern found for GROUP_CONCAT

 


1. 자바 ORM의 경우 어떤 DBMS를 쓰냐에 따라 데이터베이스 유형을 지정할 수 있도록 Dialect설정을 해주는데 이번 예시의 경우 MySQL을 사용했으니 버전에 따라 MySQL5Dialect , MySQL8Dialect를 상속받는 커스텀한 클래스를 작성해주고 group_concat를 사용할 수 있게 등록해준다. (해당 예시는 5지만 본인이 쓰는 SQL버전에 따라 8을 상속하기도 한다)

//  org.hibernate.dialect.MySQL5Dialect
public class CustomMysqlDialect extends MySQL5Dialect {
    public CustomMysqlDialect() {
        super();
        // register custom/inner function here
        this.registerFunction("group_concat", new SQLFunctionTemplate(StandardBasicTypes.STRING, "group_concat(?1)"));
    }
}

 

2. 그리고 해당 패키지를 인식해서 기존 Dialect말고 자신이 커스텀한 클래스를 빈이 인식할수 있도록 자신이 커스텀한 클래스의 패키지 경로를 작성해준다.

@Configuration
public class JpaConfiguration {
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setShowSql(true);
        adapter.setDatabase(Database.MYSQL);
       // package to CustomMysqlDialect
        adapter.setDatabasePlatform("com.xxx.xxx.config.CustomMysqlDialect");
        adapter.setGenerateDdl(false);
        return adapter;
    }
}

3. 쿼리DSL에서 적용됩니다.

// before do this , autowird JPAQueryFactory at first 
        QReportDoctorTag qReportDoctorTag = QReportDoctorTag.reportDoctorTag;
        SimpleTemplate<String> simpleTemplate = Expressions.simpleTemplate(String.class, "group_concat({0})", qReportDoctorTag.tag);
        JPAQuery<Tuple> tupleJPAQuery = queryFactory.select(qReportDoctorTag.reportId, simpleTemplate)
                .from(qReportDoctorTag)
                .groupBy(qReportDoctorTag.reportId);

        List<Tuple> fetch = tupleJPAQuery.fetch();

 

+) 이렇게까지만 하면 해당 값이 List가 아니라 String으로 받아질텐데 나같은 경우 따로 String을 Arry로 매핑해주는 메서드를 따로 짰다. (더 좋은방법이 있을 것 같지만)

private ArrayList<String> stringToArr(String target){
        String[] ArraysStr = target.split(" ");
        ArrayList<String> result = new ArrayList<String>();

        if(target == null){
            return null;
        }

        for (String str: ArraysStr) {
            result.add(str);
        }

        return result;
    }

참조한 글들입니다.


1. group_concat 추가


2. Pagenation


3. queryDsl where절에 IN절 적용 방법


 

 

 

 

 

 

 

 

 

 

 

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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 31
글 보관함