Querydsl: GROUP_CONCAT๋Š” QueryDSL์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2018๋…„ 11์›” 12์ผ  ยท  6์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: querydsl/querydsl

์•ˆ๋…•,

QueryDSL์—์„œ 'GROUP_CONCAT'์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„ ๋‚ด๊ธฐ ์œ„ํ•ด ๊ณ ๊ตฐ๋ถ„ํˆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

final QDataSourceSmallEliminate eliminate = QDataSourceSmallEliminate.dataSourceSmallEliminate;
        final QLattitudeVisitor lattitude = LattitudeVisitorServiceImpl.$;
//      Expressions.stringOperation(SQLOps.GROUP_CONCAT, lattitude.tagName)

        return QuerydslUtils.newQuery(entityManager).select(Projections.constructor(EliminateLattitude.class, 
                eliminate.id, 
                JPAExpressions.selectDistinct( SQLExpressions.groupConcat(lattitude.tagName)).from(lattitude).where(lattitude.urlCrc.eq(eliminate.urlCrc))))
            .from(eliminate)
            .fetch();

์˜ˆ์™ธ:

org.springframework.dao.InvalidDataAccessApiUsageException: No pattern found for GROUP_CONCAT; nested exception is java.lang.IllegalArgumentException: No pattern found for GROUP_CONCAT

...

Caused by: java.lang.IllegalArgumentException: No pattern found for GROUP_CONCAT
    at com.querydsl.core.support.SerializerBase.visitOperation(SerializerBase.java:280)
    at com.querydsl.jpa.JPQLSerializer.visitOperation(JPQLSerializer.java:437)
    at com.querydsl.core.support.SerializerBase.visit(SerializerBase.java:231)
    at com.querydsl.core.support.SerializerBase.visit(SerializerBase.java:31)
    at com.querydsl.core.types.OperationImpl.accept(OperationImpl.java:83)
    at com.querydsl.core.support.SerializerBase.handle(SerializerBase.java:92)
    at com.querydsl.jpa.JPQLSerializer.serialize(JPQLSerializer.java:203)
    at com.querydsl.jpa.JPQLSerializer.visit(JPQLSerializer.java:358)
    at com.querydsl.jpa.JPQLSerializer.visit(JPQLSerializer.java:39)
    at com.querydsl.core.types.SubQueryExpressionImpl.accept(SubQueryExpressionImpl.java:57)
    at com.querydsl.core.support.FetchableSubQueryBase.accept(FetchableSubQueryBase.java:150)
    at com.querydsl.core.support.SerializerBase.handle(SerializerBase.java:92)
    at com.querydsl.core.support.SerializerBase.handle(SerializerBase.java:119)
    at com.querydsl.core.support.SerializerBase.visit(SerializerBase.java:225)
    at com.querydsl.core.support.SerializerBase.visit(SerializerBase.java:31)
    at com.querydsl.core.types.ConstructorExpression.accept(ConstructorExpression.java:112)
    at com.querydsl.core.support.SerializerBase.handle(SerializerBase.java:92)
    at com.querydsl.jpa.JPQLSerializer.serialize(JPQLSerializer.java:203)
    at com.querydsl.jpa.JPAQueryBase.serialize(JPAQueryBase.java:60)
    at com.querydsl.jpa.JPAQueryBase.serialize(JPAQueryBase.java:50)
    at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:98)
    at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:94)
    at com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:201)
    at com.moraydata.general.secondary.repository.impl.DataSourceSmallEliminateRepositoryImpl.findObject(DataSourceSmallEliminateRepositoryImpl.java:98)
    at com.moraydata.general.secondary.repository.impl.DataSourceSmallEliminateRepositoryImpl$$FastClassBySpringCGLIB$$b42d875d.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

QueryDSL 4.2.1 ๋ฐ JDK1.8 ๋ฐ Spring Boot2.0 ๋ฐ MySQL 5.7.

์ตœ์‹  ์ฐธ์กฐ ๋ฌธ์„œ๋ฅผ ์ฝ์—ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋‹ต์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ž‘๋™ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

๋„์™€์ฃผ์„ธ์š”.
๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
์•ˆ๋…•ํ•˜์„ธ์š”.

jpa question

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š”.

์Šคํ”„๋ง ๋ถ€ํŠธ ๋ฐ์ดํ„ฐ jpa ๋ฐ querydsl-jpa ๊ฒฝ์šฐ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž ์ง€์ • DB ๋ฐฉ์–ธ ์ƒ์„ฑ ๋ฐ ๊ธฐ๋Šฅ ๋“ฑ๋ก
//  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)"));
    }
}
  1. JpaVendorAdapter ๊ตฌ์„ฑ
<strong i="15">@Configuration</strong>
public class JpaConfiguration {
    <strong i="16">@Bean</strong>
    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;
    }
}
  1. query-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();

๋ชจ๋“  6 ๋Œ“๊ธ€

JPAExpressions์™€ SQLExpressions๋ฅผ ํ•ญ์ƒ ํ˜ผํ•ฉ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

JPAExpressions์™€ SQLExpressions๋ฅผ ํ•ญ์ƒ ํ˜ผํ•ฉ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

๋„ค, ๋‹น์‹  ๋ง์ด ๋งž์•„์š”. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ๋‹จ์„œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์œ ์šฉํ•œ ๊ฒƒ์„ ์‹œ๋„ํ•ด๋ณด์„ธ์š”.
๊ทธ๋ ‡๋‹ค๋ฉด JPAExpressions๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ GROUP_CONCAT์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ โ€‹โ€‹์žˆ์Šต๋‹ˆ๊นŒ?

์•ˆ๋…•ํ•˜์„ธ์š”.

์Šคํ”„๋ง ๋ถ€ํŠธ ๋ฐ์ดํ„ฐ jpa ๋ฐ querydsl-jpa ๊ฒฝ์šฐ ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž ์ง€์ • DB ๋ฐฉ์–ธ ์ƒ์„ฑ ๋ฐ ๊ธฐ๋Šฅ ๋“ฑ๋ก
//  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)"));
    }
}
  1. JpaVendorAdapter ๊ตฌ์„ฑ
<strong i="15">@Configuration</strong>
public class JpaConfiguration {
    <strong i="16">@Bean</strong>
    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;
    }
}
  1. query-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();

SQL์˜ ๊ฒฝ์šฐ SQLExpressions.groupConcat . JPA์˜ ๊ฒฝ์šฐ GROUP_CONCAT ๋Š” JPQL์— ๋Œ€ํ•ด ์ •์˜ ๋œ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” ๊ธฐ๋Šฅ์„ ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋ก ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ORM ๊ณต๊ธ‰ ์—…์ฒด ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ์ด๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (์œ„ ์„ค๋ช…์˜ ์˜ˆ ์ฐธ์กฐ).

# 2020๊ณผ ์ค‘๋ณต๋ฉ๋‹ˆ๋‹ค.

@ giraffe-tree ์•ˆ๋…•ํ•˜์„ธ์š”. "GROUP_CONCAT ({0} ORDER BY {1})"๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ๋“ฑ๋ก์€ ์–ด๋–ป๊ฒŒํ•˜๋‚˜์š”? ๋ถ€๋””.

JPA์—์„œ๋Š” ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋จผ์ € ORM ๊ณต๊ธ‰ ์—…์ฒด์—์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ์š”์ฒญํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค (์˜ˆ : Hibernate).

blaze-persistence-querydsl ํ™•์žฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ณต๋ฐฑ์„ ๋ฉ”์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. https://persistence.blazebit.com/documentation/1.5/core/manual/en_US/index.html#querydsl -integration

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • JPQLNextExpressions.groupConcat(Expression<?> expression, String separator, OrderSpecifier<?>... orderSpecifiers)
  • JPQLNextExpressions.groupConcat(Expression<?> expression, Expression<String> separator, OrderSpecifier<?>... orderSpecifiers)
  • JPQLNextExpressions.groupConcat(boolean distinct, Expression<?> expression, String separator, OrderSpecifier<?>... orderSpecifiers)
  • JPQLNextExpressions.groupConcat(boolean distinct, Expression<?> expression, Expression<String> separator, OrderSpecifier<?>... orderSpecifiers)
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰