JPQL은 엔티티 객체를 조회하는 객체지향 쿼리이다.
테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리한다.
SQL과 비슷한 문법을 가지며, JPQL은 결국 SQL로 변환된다.
JPA에서 제공하는 메소드 호출만으로 섬세한 쿼리 작성이 어렵다는 문제에서 JPQL이 탄생된 것이다.
String jpql = "select m from Member as m where m.name = 'coco'";
대소문자 구분
엔티티 이름
별칭
JPQL을 실행하려면 쿼리 객체를 만들어야 한다. 쿼리 객체로는 TypedQuery와 Query가 있는데 반환할 타입을 명확하게 지정할 수 있으면 TypedQuery 객체를, 명확하게 지정할 수 없으면 Query 객체를 사용한다.
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 메소드를 호출할 때 두 번째 인자로 엔티티 클래스를 넘겨준다.
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부터 시작한다.
위의 예시처럼 메소드 체이닝 방식으로 작성할 수도 있다.
엔티티를 대상으로 조회하면 편리하겠지만, 꼭 필요한 데이터들만 선택해서 조회해야 할 때도 있다.
이럴때 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 키워드이다.
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();
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();
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();
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();