본문 바로가기
Spring

[Spring] JPA Querydsl 사용하기 (group_concat 함수)

by clolee 2022. 10. 19.

1. Querydsl 사용 설정

 

1-1) build.gradle 설정

  • querydsl-jpa : 실제 애플리케이션에서 Querydsl 을 사용할때 필요한 라이브러리
  • querydsl-apt : Q 클래스를 만드는 용도
// querydsl 버전
buildscript {
    ext {
        queryDslVersion = "5.0.0"
    }
}
plugins {
    id 'org.springframework.boot' version '2.7.3'
    id 'io.spring.dependency-management' version '1.0.13.RELEASE'
    id 'java'
    // querydsl 플러그인
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}

group = 'com.sto.sale'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'mysql:mysql-connector-java'
    implementation 'org.projectlombok:lombok'
    implementation 'javax.validation:validation-api:2.0.1.Final'
    implementation 'org.modelmapper:modelmapper:3.1.0'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
	
    //querydsl 관련 dependency 추가
    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
    implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
}

tasks.named('test') {
    useJUnitPlatform()
}

// querydsl 사용할 경로 지정합니다. 현재 지정한 부분은 .gitignore에 포함되므로 git에 올라가지 않습니다.
def querydslDir = "$buildDir/generated/'querydsl'"

// JPA 사용여부 및 사용 경로 설정
querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

// build시 사용할 sourceSet 추가 설정
sourceSets {
    main.java.srcDir querydslDir
}


// querydsl 컴파일 시 사용할 옵션 설정
compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

// querydsl이 compileClassPath를 상속하도록 설정
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    querydsl.extendsFrom compileClasspath
}

 

1-2) Config 설정

JPAQueryFactory를 @Bean으로 등록

 

back-sto-sale/src/main/java/com/sto/sale/backstosale/config/SpringConfig.java

    private final EntityManager em;

    @Autowired
    public SpringConfig(ProductRepository productRepository, SaleRepository saleRepository, UserRepository userRepository, HoldingRepository holdingRepository, TransactionRepository transactionRepository, EntityManager em) {
        this.productRepository = productRepository;
        this.saleRepository = saleRepository;
        this.userRepository = userRepository;
        this.holdingRepository = holdingRepository;
        this.transactionRepository = transactionRepository;
        this.em = em;
    }
    
	@Bean
    public JPAQueryFactory jpaQueryFactory(EntityManager em) {
        return new JPAQueryFactory(em);
    }

 

1-3) group_concat 함수 사용 설정

 

back-sto-sale/src/main/java/com/sto/sale/backstosale/sql/CustomMysqlDialect.java

public class CustomMysqlDialect implements MetadataBuilderContributor {
	@Override
	public void contribute(MetadataBuilder metadataBuilder) {
		metadataBuilder.applySqlFunction("group_concat",
				new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));
	}
}

 

1-4) application.properties 에 위에서 구현한 CustomMysqlDialect 클래스 추가

 

back-sto-sale/src/main/resources/application.properties

spring.jpa.properties.hibernate.metadata_builder_contributor=com.sto.sale.backstosale.sql.CustomMysqlDialect

 

 

 

2. Q클래스 생성

Tasks > build > build 실행

 

build.gradle에서 지정한 경로에서 Q클래스 생성 확인

 

3. Querydsl 사용하기

Q클래스 이용 :

QHolding qHolding = QHolding.holding;

 

back-sto-sale/src/main/java/com/sto/sale/backstosale/service/HoldingService.java

	@Autowired
	JPAQueryFactory jpaQueryFactory;
    
	/**
	 * 상품 별 판매 개수, 보유자 리스트
	 */
	public List<GoodsHoldingDto> findListHolding() {
		QHolding qHolding = QHolding.holding;
		List<GoodsHoldingDto> list = jpaQueryFactory
				.select(Projections.fields(
						GoodsHoldingDto.class,
						product.goods_id.as("goodsId"),
						product.goods_nm.as("goodsNm"),
						qHolding.goods_cnt.sum().as("sumGoodsCnt"),
						Expressions.stringTemplate("group_concat({0})", qHolding.user.user_id).as("userIds")
				))
				.from(qHolding)
				.join(qHolding.product, product)
				.join(qHolding.user, user)
				.groupBy(qHolding.product.goods_id)
				.fetch();

		return list;
	}

	/**
	 * 유저 별 보유 상품 개수, 보유 상품 리스트
	 */
	public List<UserHoldingDto> findListUserHolding() {
		QHolding qHolding = QHolding.holding;
		List<UserHoldingDto> userList = jpaQueryFactory
				.select(Projections.fields(
						UserHoldingDto.class,
						user.user_id.as("userId"),
						user.user_nm.as("userNm"),
						Expressions.stringTemplate("group_concat({0})", qHolding.product.goods_id).as("goodsIds"),
						Expressions.stringTemplate("group_concat({0})", qHolding.goods_cnt).as("goodsCnts")
				))
				.from(qHolding)
				.join(qHolding.product, product)
				.join(qHolding.user, user)
				.groupBy(qHolding.user.user_id)
				.fetch();

		return userList;
	}

 

Unsupported expression 에러 ==> 

2022.10.09 - [Java/Spring] - [Spring] JPA Querydsl Unsupported expression

 

 

참고 :

 

https://dingdingmin-back-end-developer.tistory.com/entry/Spring-Data-JPA-7-Querydsl-%EC%82%AC%EC%9A%A9-gradle-7x

https://stackoverflow.com/questions/57385780/how-to-register-non-standarized-sql-functions-manually-in-spring-boot-applicatio

https://web2eye.tistory.com/241

https://madplay.github.io/post/introduction-to-querydsl

http://querydsl.com/static/querydsl/latest/reference/html/ch02s03.html#d0e1067

https://jojoldu.tistory.com/372

 

 

 

 

 

댓글