본문 바로가기
자바/JPA

[JPA/Java] JPQL 기본 문법과 기능

by drCode 2023. 4. 27.
728x90
반응형

JPQL은 객체지향 쿼리 언어이다.

 

테이블을 대상으로 쿼리하는 것이 아니라, 엔티티 객체를 대상으로 쿼리 한다.

 

JPQL은 SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.

 

JPQL은 결국 SQL로 변환된다.

 

 

새로 프로젝트를 만들어서 JPQL 실습을 진행한다.

 

신규 프로젝트를 만들 때, 

 

pom.xml에 있는 Dependecies와 resources 하위에 있는 META-INF/persistence.xml을 그대로 가져와서 진행한다.

 

 

모델링은 아래와 같다.

객체모델 ERD
DB 모델 ERD

 

Member.java

package jpql;

import javax.persistence.*;

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;
    private String username;
    private int age;

    @ManyToOne
    @JoinColumn(name="TEAM_ID")
    private Team team;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 

Team.java

package jpql;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Team {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 

Product.java

package jpql;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Product {

    @Id @GeneratedValue
    private Long id;
    private String name;
    private int price;
    private int stockAmount;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public int getStockAmount() {
        return stockAmount;
    }

    public void setStockAmount(int stockAmount) {
        this.stockAmount = stockAmount;
    }
}

 

Address.java

package jpql;

import javax.persistence.Embeddable;

@Embeddable
public class Address {

    private String city;
    private String street;
    private String zipcode;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }
}

 

Order.java ( ※ Order는 SQL에서 예약어이기 때문에, Orders로 보통 많이 한다)

package jpql;

import javax.persistence.*;

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id @GeneratedValue
    private Long id;
    private int orderAmount;

    @Embedded
    private Address address;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product products;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public int getOrderAmount() {
        return orderAmount;
    }

    public void setOrderAmount(int orderAmount) {
        this.orderAmount = orderAmount;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Product getProducts() {
        return products;
    }

    public void setProducts(Product products) {
        this.products = products;
    }
}

 

JpaMain.java

package jpql;

import javax.persistence.*;
import java.util.List;

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();


        try{
        
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        // code
        emf.close();
    }
}

 

실습을 위한 코드 세팅은 완료 됐다.

 

JPQL 문법은 SQL과 크게 다르지 않다.

JPQL 문법

 

JPQL 문법은 "select m from Member as m where m.age > 18" 과 같은 식으로 작성한다.

 

여기서  Member는 테이블이 아닌 엔티티 객체이다.

 

엔티티와 속성은 대소문자로 구분한다.(Member, age)

 

JPQL 키워드는 대소문자 구분을 하지 않는다. (SELECT, FROM where)

 

엔티티 이름을 사용하고, 테이블 이름이 아니다.(Member)

 

별칭은 필수이다(m) (as는 생략 가능)

 

집합과 정렬에 관련된 함수( COUNT, SUM, AVG, MAX, MIN)은 아래처럼 사용한다.

 

집합 & 정렬 사용 방식

집합 함수는 GROUP BY, HAVING,  정렬은 ORDER BY를 사용한다.

 

※ TypeQuery, Query

TypeQuery는 반환타입이 명확할 때 사용한다.

TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);

↑ 위의 구문은 SELECT 문에 엔티티 자체를 조회하기 때문에 

 

반면에, Query는 반환 타입이 명확하지 않을 때 사용한다.

Query query = em.createQuery("SELECT m.username, m.age from Member m");

↑ 위의 구문은 SELECT 문에서 찾고자 하는 컬럼에 대한 명시가 username(String), age(int) 이기 때문에

타입을 특정 지을 수가 없다.

※ 결과 조회 API

query.getResultList()는 결과가 하나 이상일 때, 리스트를 반환하고, 결과가 없으면 비어있는 리스트를 반환한다.

 

query.getSingleResult() 는 결과가 정확히 하나일 때 사용한다. 단일 객체를 반환한다.

 - 결과가 없을 시에는 : javax.persistence.NoResultException 발생

 - 결과가 둘 이상일 때 : javax.persistence.NonUniqueResultException 발생

 

Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);

TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
List<Member> resultList = query.getResultList();

for(Member member1 : resultList) {
    System.out.println("member1 = " + member1);
}

Member singleResult = query.getSingleResult();

 

※ 파라미터 바인딩 - 이름 기준, 위치 기준

 

1) 이름 기준

SELECT m FROM Member m where m.username=:username 
query.setParameter("username", usernameParam);

 

실제 적용 

TypedQuery<Member> query = em.createQuery("select m from Member m where m.username = :username", Member.class);
query.setParameter("username", "member1");

 

2) 위치 기준

SELECT m FROM Member m where m.username=?1 
query.setParameter(1, usernameParam);

 

728x90
반응형

댓글