[JPA] QueryDSL

LDB·2024년 12월 1일
0

JPA 기본

목록 보기
7/10
post-thumbnail

QueryDSL

QueryDSL은 하이버네이트 쿼리 언어(HQL: Hibernate Query Language)의 쿼리를 타입에 안전하게 생성 및 관리해주는 프레임워크이다. QueryDSL은 정적타입을 이용하여 SQL과 같은 쿼리를 장성 할 수 있게 해준다.

QueryDSL은 Spring Data JPA의 단점과 기존의 MyBatis, JPQL의 단점을 해결해준다,
Spring Data JPA는 복잡한 쿼리 작성이 어렵고 MyBatis, JPQL은 쿼리작성을 해도 쿼리에 에러가 있음을 프로젝트를 실행시키고 직접 확인해야 알 수 있다.

QueryDSL은 쿼리문을 코드로 작성하기 때문에 컴파일 단계에서 에러를 확인 할 수 있고 복잡한 쿼리나 동적 쿼리 작성이 편리하다.

QueryDSL 사용준비

(해당 예시는 Spring Boot로 작성했고 빌드도구는 gradle로 했다.)

build.gradle 파일

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' // QueryDSL 목적
	annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" // Qclass 생성
	annotationProcessor "jakarta.annotation:jakarta.annotation-api"
	annotationProcessor "jakarta.persistence:jakarta.persistence-api" // JPA 어노테이션
    
    runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
    
    annotationProcessor 'org.projectlombok:lombok'
}

application.properties 파일

spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=

entity 파일

@Entity
@Getter
@NoArgsConstructor
@Table(name="member")
public class Member{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private int age;
}

QueryDSL 설정파일

@Configuration
public class querydslConfig {

    @PersistenceContext // 영속성 컨텍스트에 추가
    private EntityManager em; // EntityManager는 JPA에서 엔터티의 생성, 조회, 수정 삭제를 수행

    @Bean
    public JPAQueryFactory jpaQueryFactory(EntityManager em){ 
        // 쿼리를 작성하는 JPAQueryFactory에 EntityManager를 넘겨 사용한다.	
        return new JPAQueryFactory(em);
    }
}

JPAQueryFactory는 QueryDSL에서 제공하는 주요 클래스 중 하나이다. 해당 Config 파일은 JPAQueryFactory를 QueryDSL을 이용한 JPA 쿼리를 빌드하는 Factory 역할로 Bean으로 등록하여 사용하는데 장점이 많아서 제일 많이 사용하는 방법이다.


QClass 란?

사용하기전에 있어서 QClass란 무엇인가를 알아볼 필요가있다.

상단의 이미지는 entity 클래스를 빌드 했을 때 생성되는 QClass이다. QClass는 타입 세이프(Typed-Safe) 쿼리를 생성하기 위해 자동으로 생성되는 클래스이다.

타입 세이프(Typed-Safe)
프로그래밍 언어의 특성을 나타내는 용어로 프로그램이 실행되는 동안(런타임) 데이터의 타입을 체크하여 타입 오류를 방지하는 것을 의미한다. 잘못된 타입의 데이터를 사용하려고 시도하면 컴파일 오류나 런타임 오류가 발생하여 버그를 방지한다.

QueryDSL의 장점인 컴파일할 때 쿼리오류를 발견한다는 QClass에서 나오는 것 이라고해도 과언이 아니다.


사용예시

기본 CRUD 메서드

  • select : 엔티티의 모든 컬럼 혹은 특정컬럼 조회
  • selectfrom : 엔티티의 모든 컬럼조회
  • insert : 엔터티와 연결되어있는 테이블에 row 추가
  • update : 조건에 해당하는 값을 업데이트
  • delete : 조건에 해당하는 값을 삭제
@Repository
public class memberRepository{
  
	@Autowired
    private JPAQueryFactory queryFactory;
  
  	// 특정 컬럼만 선택하여 조회
	@Transactional
    public List<memresultDTO> searchAll() {
        List<memresultDTO> result = queryFactory
                                .select(Projections.constructor(memresultDTO.class,
                                    member.name,
                                    member.age
                                ))
                                .from(member)
                                .fetch();

        return result;
    }
  	
    // 엔터티에 작성된 컬럼 1개만 조회
	@Transactional
    public Member searchOne(int seq) {
        return queryFactory.selectFrom(member)
                            .where(member.seq.eq(seq))
                            .fetchOne();
    }
    
    // 엔터티에 row를 추가한다.
    @Transactional
	public long insert() { // execute 리턴값 타입이 long이다.
		return queryFactory.insert(member)
                            .columns(member.age,member.name)
                            .values(12,"tester11")
                            .execute();
	}
	
    // 조건에 해당하는 row를 업데이트한다.
    @Transactional
    public long update() {
        return queryFactory.update(member)
                           .set(member.age, 22)
                           .where(member.age.eq(12))
                           .execute(); 
    }
	
    // 조건에 해당하는 row를 삭제한다.
    @Transactional
    public long delete() {
        return queryFactory.delete(member)
                            .where(member.age.eq(12))
                            .execute();
    }
    
}

insert, update, delete는 리턴을 long으로 했는데 excute()메서드 내부를 보면

long 타입인 것을 확인할 수 있다. 만약 결과값을 출력한다면

몇 건을 추가하고 수정했고 삭제했는지 출력이가능하다.

select 결과값 조회 메서드

  • fetch() : 결과값을 리스트 형태로 조회한다.
  • fetchOne() : 결과값을 1개만 출력한다.
  • fetchFirst() : 데이터베이스 테이블 정렬된 결과값 첫 번째 row를 조회한다.

fetchOne()과 fetchFirst()는 결과 1개만 출력한다는 점에서 같지만

queryFactory.selectFrom(member).fetchFirst();

queryFactory.selectFrom(member).fetchOne();

두 코드를 비교해보면 fetchOne은 조회시 단일 건이 아니라서 NonUniqueResultException이 예외로 발생되지만 fetchFirst는 조회된 것중에 첫번 째 값만 가져오기 때문에 첫번 째에 해당하는 row가 출력된다.

정리

메서드반환 타입다수 결과 경우결과 없는 경우사용하는 경우
fetch()List<T>조건에 해당하는 값 전체조회빈 List 반환다량의 값을 조회 할 때 사용
fetchOne()TNonUniqueResultException발생null 반환반드시 단일 결과일 경우 사용
fetchFirst()T조건에 해당하는 첫 번째 값 조회null 반환첫번 째 값만 조회할 경우 사용

참고 사이트

https://ittrue.tistory.com/292

https://sjh9708.tistory.com/174

https://adjh54.tistory.com/484

https://joont92.github.io/jpa/QueryDSL/

profile
가끔은 정신줄 놓고 멍 때리는 것도 필요하다.

0개의 댓글