JPQL(Java Persistence Query Language) 이해하기

wujin·2023년 3월 30일
0

JPQL(Java Persistence Query Language)

JPQL은 엔티티 객체를 조회하는 객체지향 쿼리이다.
테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리한다.
SQL과 비슷한 문법을 가지며, JPQL은 결국 SQL로 변환된다.
JPA에서 제공하는 메소드 호출만으로 섬세한 쿼리 작성이 어렵다는 문제에서 JPQL이 탄생된 것이다.


JPQL 특징

  1. 테이블이 아닌 객체를 대상으로 쿼리를 작성한다.
  2. JPA에서 지원하는 모든 데이터베이스에서 사용할 수 있기 때문에 특정 데이터베이스에 종속성이 없다.
  3. 캐싱을 통해 쿼리의 실행 속도를 향상시킬 수 있다. 캐싱을 이용하면 쿼리 실행 결과를 메모리에 저장하고, 같은 쿼리가 여러 번 실행될 경우 메모리에 저장된 결과를 사용하므로 DB에 접근하지 않아도 되기 때문이다.
  4. JPA는 JPQL을 분석하여 SQL을 생성한 후 DB에서 조회

JPQL 기본 문법

String jpql = "select m from Member as m where m.name = 'coco'";
  1. 대소문자 구분

    • 엔티티와 속성은 대소문자를 구분한다.
    • 엔티티 이름인 Member, 그리고 Member의 속성 name은 대소문자를 구분해줘야 한다.
    • 반면에 SELECT, FROM, AS 같은 JPQL 키워드는 대소문자를 구분하지 않아도 된다.
  2. 엔티티 이름

    • JPQL에서 사용한 Member는 클래스 이름이 아닌 엔티티 이름이다. 엔티티 이름은 @Entity(name="abcd")로 설정 가능하다.
    • name 속성을 생략하면 기본 값으로 클래스 이름을 사용한다.
  3. 별칭

    • JPQL에서 엔티티의 별칭은 필수적으로 명시해야 한다.
    • 별칭을 명시하는 AS 키워드는 생략할 수 있다.

TypedQuery, Query

JPQL을 실행하려면 쿼리 객체를 만들어야 한다. 쿼리 객체로는 TypedQuery와 Query가 있는데 반환할 타입을 명확하게 지정할 수 있으면 TypedQuery 객체를, 명확하게 지정할 수 없으면 Query 객체를 사용한다.

TypedQuery

public static void typedQuery(EntityManager em) {
    String jpql = "select m from Member m";
	TypedQuery<Member> query = em.createQuery(jpql, Member.class);
	
	List<Member> list = query.getResultList();
	for(Member member : list) {
		System.out.println("Member : " + member);
	}
}

EntityManager 객체에서 createQuery() 메소드를 호출하면 쿼리가 생성된다.
em.createQuery 메소드를 호출할 때 두 번째 인자로 엔티티 클래스를 넘겨준다.

Query

public static void Query(EntityManager em) {
    String jpql = "select m.name, m.age from Member m";
	Query query = em.createQuery(jpql);
	
	List<Object> list = query.getResultList();
	
    for(Object object : list) {
	      Object[] results = (Object[]) object;
	      
	      for(Object result : results) {
	          System.out.println(result);
	     }
	}
}

Query 타입은 데이터 검색 결과의 타입을 명시하지 않는다.


파라미터 바인딩

파라미터 바인딩에는 이름 기준 파라미터위치 기준 파라미터가 있다.
위치 기준 파라미터 보다 이름 기준 파라미터가 더 명확하다.

이름 기준 파라미터

public static void namedParameter(EntityManager em, String param) {
    String jpql = "select m from Member m where m.name = :name";
	TypedQuery<Member> query = em.createQuery(jpql, Book.class);
	query.setParameter("name", param);
	
	List<Member> list = query.getResultList();
}

이름을 기준으로 파라미터를 바인딩 한다. 콜론( : ) 을 사용해 데이터가 추가될 곳을 지정하고, query.setParameter() 메소드를 호출해 데이터를 동적으로 바인딩 한다.

위치 기준 파라미터

public static void locationalParameter(EntityManager em, String param) {
    String jpql = "select m from Member m where m.name = ?1";
    List<Member> members = em.createQuery(jpql, Member.class)
    	.setParameter(1, param)
    	.getResultList();
}

위치 기준 파라미터를 사용하려면 ? 다음에 위치 값을 주면 된다. 위치 값은 1부터 시작한다.
위의 예시처럼 메소드 체이닝 방식으로 작성할 수도 있다.


DTO 사용시 - new 명령어

엔티티를 대상으로 조회하면 편리하겠지만, 꼭 필요한 데이터들만 선택해서 조회해야 할 때도 있다.
이럴때 MemberDTO처럼 의미 있는 객체로 변환해서 사용한다.

@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
public class MemberDTO {
	private String name;
	private int age;
}

public static void useDTO (EntityManager em) { // DTO 사용 ( new 명령어 )
	String jpql =
		"select new com.coco.example.MemberDTO(m.name, m.age) from Member m";
	TypedQuery<MemberDto> query = em.createQuery(jpql, MemberDTO.class);
	
	List<MemberDTO> list = query.getResultList();
	for(MemberDTO dto : list) {
		System.out.println("dto : " + dto);
	}
}

select 와 from 사이에 new 라는 키워드 뒤에 DTO의 패키지명까지 작성해야 한다.
이 때 new는 객체를 생성하라는 의미가 아니라 JPQL에서 지원하는 new 키워드이다.


추가 예시 코드

1. SELECT

String jpql = "SELECT p FROM Product p WHERE p.category = :category";
TypedQuery<Product> query = entityManager.createQuery(jpql, Product.class);
query.setParameter("category", "Electronics");
List<Product> results = query.getResultList();

2. INSERT

String jpql = "INSERT INTO Product (name, price, category) VALUES ('Laptop', 1500, 'Electronics')";
entityManager.getTransaction().begin();
Query query = entityManager.createQuery(jpql);
int insertedCount = query.executeUpdate();
entityManager.getTransaction().commit();

3. UPDATE

String jpql = "UPDATE Product p SET p.price = :newPrice WHERE p.name = :productName";
entityManager.getTransaction().begin();
Query query = entityManager.createQuery(jpql);
query.setParameter("newPrice", 1800);
query.setParameter("productName", "Laptop");
int updatedCount = query.executeUpdate();
entityManager.getTransaction().commit();

4. DELETE

String jpql = "DELETE FROM Product p WHERE p.id = :productId";
entityManager.getTransaction().begin();
Query query = entityManager.createQuery(jpql);
query.setParameter("productId", 1);
int deletedCount = query.executeUpdate();
entityManager.getTransaction().commit();

0개의 댓글