본문 바로가기

Backend/Spring

Spring Boot Querydsl (Maven, Gradle 5)

항상 JPA를 사용하면서 JpaRepository와 EntityManager만 사용했었는데 항상 먼가 제한적이다는 느낌을 많이 받았어요.

그래서 Native Query를 어떻게 하면 깔끔하게 코딩할 수 있을까 고민하다가 답을 찾지 못했고 결국 Querydsl이나 jooq로 눈을 돌리게 되었어요.

쿼리의 도움을 받아야 하는 레거시 시스템 환경은 확실히 JPA가 많이 불리한 것 같아요.

 

개발 환경은 JDK 11 + Spring boot 2.1.9 버전이구요.

예제를 통해 간단히 Querydsl을 설정하고 사용해볼게요.

 

for Maven

더보기

pom.xml

아래 코드 추가 후에 Update Project

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <dependencies>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/generated-sources/java</outputDirectory>
                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 

@Entity를 추가/수정할 때마다 target 폴더에 Q-Class들이 생성/수정됩니다.

 

 

for Gradle 5

더보기

build.gradle

아래 코드 추가 후에 Refresh Gradle Project

 

plugins {
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

dependencies {
	compile 'com.querydsl:querydsl-jpa'
	compile 'com.querydsl:querydsl-apt'
}

def querydslDir = "$buildDir/generated/querydsl"

querydsl {
  jpa = true
  querydslSourcesDir = querydslDir
}

sourceSets {
  main.java.srcDir querydslDir
}

configurations {
  querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
  options.annotationProcessorPath = configurations.querydsl
} 

Gradle Tasks - Show All Tasks

Gradle Tasks - compileQuerydsl

compileQuerydsl 하면 @Entity들을 읽어서 build/generated/querydsl 폴더에 Querydsl 사용에 필요한 Q-Class들이 생성되요.


플러그인 설정을 마쳤다면 아주 간단하게 사용해볼게요.

QuerydslConfig

@Configuration
public class QuerydslConfig {

	@PersistenceContext
	private EntityManager em;

	@Bean
	public JPAQueryFactory jpaQueryFactory() {
		return new JPAQueryFactory(em);
	}

}

UserRepositoryCustom

생성할 쿼리의 인터페이스에요.

 

public interface UserRepositoryCustom {
	
	long updateFailCnt(String email);

}

UserRepositoryImpl

Custom 인터페이스의 비즈니스 로직을 구현해주세요.

이름명은 꼭 Impl를 뒤에 붙여주어야 오류가 나지 않아요.

저는 사용자가 로그인에 실패하면 fain_cnt 컬럼을 +1해주는 쿼리를 만들었어요.

설정이 목표이니 querydsl 문법은 여기서 설명하지 않을게요.

 

public class UserRepositoryImpl extends QuerydslRepositorySupport implements UserRepositoryCustom {

	private final JPAQueryFactory query;
	
	private static QUser user = QUser.user;

	public UserRepositoryImpl(JPAQueryFactory queryFactory) {
		super(User.class);
		this.query = queryFactory;
	}

	@Override
	public long updateFailCnt(String email) {
		return query.update(user)
			.set(user.failCnt, user.failCnt.add(1))
			.where(user.email.eq(email))
			.execute();
	}

}

UserRepository

커스텀한 인터페이스를 UserRepository에서 상속받아주세요.

 

public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {

}

UserService

@Service
public class UserService {

	@Autowired
	private UserRepository userRepository;

}

 

userRepository 인스턴스를 통하여

JpaRepository에서 제공하는 메소드 뿐만 아니라 커스텀하게 생성한 쿼리도 함께 사용할 수 있습니다.

 

 

장점

  • 엔티티 스키마로 만든 Q-Class 기반으로 쿼리를 작성하므로 엔티티 변경 시에 유지보수 매우 유리
  • 코드 기반이라 익숙해지기만 하면 자동완성을 통하여 정확하고 빠르게 쿼리문을 작성할 수 있음
  • 컴파일 시점에 SQL 에러 잡아줌 (코드 기반이라)

 

단점

  • 굉장히 길고 복잡한 쿼리를 Querydsl로 작성하면 유지보수가 거의 불가한 수준의 쿼리를 많이 보게 됨
  • sql 외의 Querydsl도 공부해야 함
  • 이건 쓰는 사람에 따라 다른데 결국 프로젝트 소스에 Querydsl을 도입한 순간부터 Querydsl로 도배되기 시작하여 JPA 쓰는 의미를 잃어버리는 경우도 생김 (이건 주의하자는 측면에서 적은 개인적인 단점..?)