public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByAgeGreaterThanEqual(int age);
}
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query(value = "SELECT * FROM member WHERE age >= ?1", nativeQuery = true)
List<Member> findByAgeGreaterThanEqualNative(int age);
}
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("SELECT m FROM Member m WHERE m.age >= :age")
List<Member> findByAgeGreaterThanEqualJPQL(@Param("age") int age);
}
@Repository
public class MemberCustomRepositoryImpl extends QuerydslRepositorySupport implements MemberCustomRepository {
public MemberCustomRepositoryImpl() {
super(Member.class);
}
@Override
public List<Member> findByAgeGreaterThanEqual(int age) {
QMember member = QMember.member;
return from(member).where(member.age.goe(age)).fetch();
}
}
dependencies {
//QueryDSL을 사용하여 JPA 엔티티를 쉽게 조회하고 조작할 수 있습니다.
implementation 'com.querydsl:querydsl-jpa'
//Annotation Processor를 사용하면 컴파일 시점에 QueryDSL이 생성하는 Q객체를 생성할 수 있습니다.
implementation 'com.querydsl:querydsl-apt'
//Annotation Processor를 사용하여 Q객체를 생성하기 위해, 컴파일 시점에 JPA 엔티티 클래스를 스캔하고 Q객체를 생성합니다.
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" // querydsl JPAAnnotationProcessor 사용 지정
//JPA 엔티티를 정의할 때 사용되는 어노테이션들이 포함되어 있습니다.
annotationProcessor("jakarta.persistence:jakarta.persistence-api")
//어노테이션을 사용하여 QueryDSL Annotation Processor를 구현할 때 필요합니다.
annotationProcessor("jakarta.annotation:jakarta.annotation-api")
}
//Annotation Processor가 생성한 Q객체를 저장할 디렉토리 경로를 설정합니다.
def generated='src/main/generated'
//Java 소스 파일의 위치를 지정합니다.
sourceSets {
//Java 소스 파일의 위치에 Annotation Processor가 생성한 Q객체를 저장하는 디렉토리를 추가합니다.
main.java.srcDirs += [ generated ]
}
//Java 소스 코드 컴파일 태스크에 대한 설정을 지정합니다.
tasks.withType(JavaCompile) {
//options.annotationProcessorGeneratedSourcesDirectory 옵션에 Annotation Processor가 생성한 Q객체를 저장하는 디렉토리를 지정합니다.
options.annotationProcessorGeneratedSourcesDirectory = file(generated)
}
clean.doLast {
//Annotation Processor가 생성한 Q객체를 저장하는 디렉토리를 삭제합니다.
file(generated).deleteDir()
}
@Configuration
public class QuerydslConfiguration {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
Gradle 옵션 - Tasks - other - compileJava를 실행시키면 Q클래스 파일이 생성된다.
생성 | 생성된파일 |
---|---|
public interface MemberRepository extends JpaRepository<Member,Long> {
List<Member> findByAge(int age);
}
public interface MemberRepositoryCustom {
List<Member> findByAgeQueryDsl(int age);
}
@Repository
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom{
private final JPAQueryFactory jpaQueryFactory;
@Override
public List<Member> findByAgeQueryDsl(int age) {
QMember member = QMember.member;
return jpaQueryFactory.selectFrom(member)
.where(member.age.eq(age))
.fetch();
}
}
하지만 위처럼 적용할경우 사용을할떄 두가지의 상속을 받아야 한다
@Autowired
private MemberRepository memberRepository;
@Autowired
private MemberRepositoryCustom memberRepositoryCustom;
이 문제를 해결하기위해서 MemberRepository에 memberRepositoryCustom을 상속받자
그럼 MemberRepository만 의존성 주입을해도 모두 사용 할 수 있다!
public interface MemberRepository extends JpaRepository<Member,Long> ,MemberRepositoryCustom{
List<Member> findByAge(int age);
}
아래의 코드에선 동적으로 쿼리를 생성하는 예시를 보여준다.
만약 name
값이 Null
일 경우는 쿼리 조건에 포함되지않는다
public List<Member> searchMembers(String name, String email, LocalDate startDate, LocalDate endDate) {
QMember member = QMember.member;
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.hasText(name)) {
builder.and(member.name.contains(name));
}
if(StringUtils.hasText(email)) {
builder.and(member.email.contains(email));
}
if(startDate != null) {
builder.and(member.createdDate.goe(startDate.atStartOfDay()));
}
if(endDate != null) {
builder.and(member.createdDate.lt(endDate.plusDays(1).atStartOfDay()));
}
return queryFactory.selectFrom(member).where(builder).fetch();
}
테스트 코드를 작성하여 잘 실행이 되는지 확인해보자
아래 코드처럼 진행하면 Spring JPA로 찾은 findByAge
와 findByAgeQueryDsl
의 결과가 같아
문제없이 assertThat이 통과되는 모습을 확인 할 수 있다.
@SpringBootTest
class MemberRepositoryTest {
@Autowired
private MemberRepository memberRepository;
@Test
@Transactional
public void test(){
memberRepository.save(new Member("홍길동",10));
memberRepository.save(new Member("김길동",10));
memberRepository.save(new Member("오길동",10));
memberRepository.save(new Member("홍길동",11));
List<Member> members = memberRepository.findByAge(10);
List<Member> members2 = memberRepository.findByAgeQueryDsl(10);
assertThat(members).isEqualTo(members2);
}
}