> Hello World !!!

     

@syaku

QueryDSL for Spring Data JPA

Github: https://github.com/syakuis/spring-data-jpa-querydsl

Gradle의 annotationProcessor로 심플하게 설정 방법이 줄어들었다. 더 이상 플러그인은 사용하지 않아도 된다. 참고: https://iammert.medium.com/annotation-processing-dont-repeat-yourself-generate-your-code-8425e60c6657

Gradle 버전은 6.5 이며 스프링 부트 버전은 2.4.5 이다.

ext.querydslVersion = "4.4.0"

def querydslDir = "${buildDir}/generated/querydsl"

sourceSets {
                main.java.srcDir querydslDir
}

dependencies {
                implementation "com.querydsl:querydsl-jpa:${querydslVersion}"
        annotationProcessor(
            "jakarta.persistence:jakarta.persistence-api",
            "jakarta.annotation:jakarta.annotation-api",
            "com.querydsl:querydsl-apt:${querydslVersion}:jpa"
        )
}

스프링 빈 설정

@Configuration
public class QuerydslConfiguration {
    @PersistenceContext
    private EntityManager entityManager;

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

QuerydslRepositorySupport 사용하여 구현하기

QueryDSL에서 제공하는 클래스를 상속하여 사용할 수 있다.

@Repository
public class SignupAccountRepository extends QuerydslRepositorySupport {
    private final JPAQueryFactory jpaQueryFactory;

    public SignupAccountRepository(JPAQueryFactory jpaQueryFactory) {
        super(AccountEntity.class);
        this.jpaQueryFactory = jpaQueryFactory;
    }

    public boolean existsByUsername(String username) {
        return jpaQueryFactory.selectFrom(accountEntity)
            .where(accountEntity.deleted.isNotNull().and(accountEntity.username.eq(username))).limit(1).fetchCount() > 0;
    }
}

@DataJpaTest는 QuerydslConfiguration 을 로드하지 못하므로 @SpringBootTest 를 선언하여 테스트를 작성해야한다.

Spring Data JPA 와 함께 사용하기

QuerydslRepositorySupport를 사용하면 두개의 Repository 를 관리해야하며 @DataJpaTest 를 사용할 수 없다 하지만 Spring Data JPA와 함께 사용하면 하나의 Repository와 @DataJpaTest를 사용할 수 있게 된다.

QueryDSL 전용 인터페이스와 구현 클래스를 작성해야 한다. 인터페이스 ~Custom, 구현 클래스는 ~Impl 의 접미사를 클래스명으로 사용해야한다.

QueryDSL Interface & Class

public interface AccountRepositoryCustom {
    boolean existsByUsername(String username);
}

@RequiredArgsConstructor
public class AccountRepositoryImpl implements AccountRepositoryCustom {
    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public boolean existsByUsername(String username) {
        return jpaQueryFactory.selectFrom(accountEntity)
            .where(accountEntity.deleted.isNotNull().and(accountEntity.username.eq(username))).limit(1).fetchCount() > 0;
    }
}

Spring Data JPA Repository

public interface AccountRepository extends JpaRepository<AccountEntity, Long>, AccountRepositoryCustom {

    Optional<AccountEntity> findByUsername(String username);
}

Test Code

@ExtendWith(SpringExtension.class)
@DataJpaTest
@Import(QuerydslConfiguration.class)
class AccountRepositoryTest {
        @Autowired
    private AccountRepository accountRepository;

        @Test
    void existsByUsername() {
        accountRepository.deleteById(accountEntity.getId());
        assertTrue(accountRepository.existsByUsername("test"));
        assertFalse(accountRepository.existsByUsername("I51in"));
    }
}

Annotation을 만들어 사용할 수 있다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@DataJpaTest
@Import(QuerydslConfiguration.class)
public @interface QuerydslTest {
}