[LG U+ ์œ ๋ ˆ์นด 3๊ธฐ]์ˆœ์ˆ˜JPA ์‹ค์Šต — JPQL (Java Persistence Query Language)

2025. 10. 20. 14:56ใ†Java/JPA

๐Ÿงฉ JPA ์‹ค์Šต โ‘ค — JPQL (Java Persistence Query Language)

์ด๋ฒˆ ์‹ค์Šต์—์„œ๋Š” JPQL์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ Typed Query, ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ•™์Šตํ–ˆ๋‹ค.
JPQL์€ SQL์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์งˆ์˜ํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด๋‹ค.
์ฆ‰, “ํ…Œ์ด๋ธ”๋ช…”์ด ์•„๋‹Œ “์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ช…(Book)”๊ณผ “์ปฌ๋Ÿผ๋ช…”์ด ์•„๋‹Œ “ํ•„๋“œ๋ช…(bookname, price)”์œผ๋กœ ์ž‘์„ฑ๋œ๋‹ค.


โถ ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ


JPABasic_JPQL
 โ”œโ”€ src/main/java/
 โ”‚   โ”œโ”€ entity/
 โ”‚   โ”‚   โ””โ”€ Book.java
 โ”‚   โ””โ”€ Test.java
 โ”‚
 โ”œโ”€ src/main/resources/
 โ”‚   โ””โ”€ META-INF/
 โ”‚       โ””โ”€ persistence.xml
 โ”‚
 โ””โ”€ pom.xml

โžก๏ธ ์ˆœ์ˆ˜ JPA + Hibernate ํ™˜๊ฒฝ์—์„œ ์ž‘์„ฑ๋˜์—ˆ์œผ๋ฉฐ, ๋ณ„๋„์˜ Spring ์„ค์ • ์—†์ด EntityManagerFactory๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ–ˆ๋‹ค.


โท persistence.xml ์„ค์ •


<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="3.0"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
             https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">

  <persistence-unit name="my-pu">
      <class>entity.Book</class>
      <properties>
          <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
          <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpql_basic"/>
          <property name="jakarta.persistence.jdbc.user" value="root"/>
          <property name="jakarta.persistence.jdbc.password" value="1234"/>
          <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
          <property name="hibernate.hbm2ddl.auto" value="update"/>
          <property name="hibernate.show_sql" value="true"/>
      </properties>
  </persistence-unit>
</persistence>

๐Ÿ“Œ hbm2ddl.auto = update → ํ…Œ์ด๋ธ”์ด ์—†์œผ๋ฉด ์ž๋™ ์ƒ์„ฑ, ํ•„๋“œ ๋ณ€๊ฒฝ ์‹œ alter
๐Ÿ“Œ show_sql = true → ์‹คํ–‰๋˜๋Š” JPQL → SQL ๋ณ€ํ™˜ ๋กœ๊ทธ ์ถœ๋ ฅ


โธ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค — Book.java


package entity;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name="book")
public class Book {

    @Id
    private int bookid;
    private String bookname;
    private String publisher;
    private int price;

    public int getBookid() { return bookid; }
    public void setBookid(int bookid) { this.bookid = bookid; }

    public String getBookname() { return bookname; }
    public void setBookname(String bookname) { this.bookname = bookname; }

    public String getPublisher() { return publisher; }
    public void setPublisher(String publisher) { this.publisher = publisher; }

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

    @Override
    public String toString() {
        return "Book [bookid=" + bookid + ", bookname=" + bookname +
                ", publisher=" + publisher + ", price=" + price + "]";
    }
}

โœ… Book ์—”ํ‹ฐํ‹ฐ๋Š” ํ…Œ์ด๋ธ” “book”๊ณผ ๋งคํ•‘๋˜๋ฉฐ, ๊ฐ ํ•„๋“œ๋Š” ์ปฌ๋Ÿผ(bookid, bookname, publisher, price)์— ๋งคํ•‘๋œ๋‹ค.
์ด์ œ JPQL์€ ์ด Book ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์กฐํšŒ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.


โน JPQL ๊ธฐ๋ณธ ์ฟผ๋ฆฌ — ์ „์ฒด ์กฐํšŒ


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

public class Test {
    public static void main(String[] args) {
        EntityManagerFactory emf =
                Persistence.createEntityManagerFactory("my-pu");
        EntityManager em = emf.createEntityManager();

        // Typed Query
        String jpql = "select b from Book b"; // ํ…Œ์ด๋ธ”์ด ์•„๋‹Œ Book ์—”ํ‹ฐํ‹ฐ ๊ธฐ์ค€
        TypedQuery<Book> query = em.createQuery(jpql, Book.class);
        List<Book> bookList = query.getResultList();

        for (Book book : bookList) {
            System.out.println(book);
        }

        em.close();
    }
}

โœ… ์‹คํ–‰ ๊ฒฐ๊ณผ (์ฝ˜์†”)


Book [bookid=1, bookname=์ถ•๊ตฌ์˜ ์—ญ์‚ฌ, publisher=๊ตฟ์Šคํฌ์ธ , price=7000]
Book [bookid=2, bookname=์ถ•๊ตฌ๋ฅผ ์•„๋Š” ์—ฌ์ž, publisher=๋‚˜๋ฌด์ˆ˜, price=13000]
Book [bookid=3, bookname=์ถ•๊ตฌ์˜ ์ดํ•ด, publisher=๋Œ€ํ•œ๋ฏธ๋””์–ด, price=22000]
Book [bookid=4, bookname=๊ณจํ”„ ๋ฐ”์ด๋ธ”, publisher=๋Œ€ํ•œ๋ฏธ๋””์–ด, price=35000]
Book [bookid=5, bookname=ํ”ผ๊ฒจ ๊ต๋ณธ, publisher=๊ตฟ์Šคํฌ์ธ , price=8000]
...

- JPQL์˜ select b from Book b๋Š” SQL๋กœ ๋ณ€ํ™˜ ์‹œ select * from book ํ˜•ํƒœ๋กœ ์‹คํ–‰๋œ๋‹ค.
- Book์€ ํด๋ž˜์Šค๋ช…์ด๋ฉฐ, b๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ๋ณ„์นญ(alias)์ด๋‹ค.


โบ JPQL + ์œ„์น˜ ํŒŒ๋ผ๋ฏธํ„ฐ (Positional Parameter)


String jpql = "select b from Book b where b.price > ?1";
TypedQuery<Book> query = em.createQuery(jpql, Book.class);
query.setParameter(1, 15000); // ?1 → 15000์œผ๋กœ ์น˜ํ™˜
List<Book> bookList = query.getResultList();

for (Book book : bookList) {
    System.out.println(book);
}

โœ… ์‹คํ–‰ SQL


Hibernate: select b1_0.bookid,b1_0.bookname,b1_0.price,b1_0.publisher
from Book b1_0 where b1_0.price>?

โœ… ๊ฒฐ๊ณผ (price > 15000)


Book [bookid=3, bookname=์ถ•๊ตฌ์˜ ์ดํ•ด, publisher=๋Œ€ํ•œ๋ฏธ๋””์–ด, price=22000]
Book [bookid=4, bookname=๊ณจํ”„ ๋ฐ”์ด๋ธ”, publisher=๋Œ€ํ•œ๋ฏธ๋””์–ด, price=35000]
Book [bookid=7, bookname=์•ผ๊ตฌ์˜ ์ถ”์–ต, publisher=์ด์ƒ๋ฏธ๋””์–ด, price=20000]

- ?1์€ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
- setParameter(1, 15000) ํ˜•ํƒœ๋กœ ๊ฐ’์„ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค.
- SQL Injection ์œ„ํ—˜ ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ์กฐ๊ฑด ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•˜๋‹ค.


โป JPQL + ์ด๋ฆ„ ํŒŒ๋ผ๋ฏธํ„ฐ (Named Parameter)


String jpql = "select b from Book b where b.price > :price";
TypedQuery<Book> query = em.createQuery(jpql, Book.class);
query.setParameter("price", 15000);
List<Book> bookList = query.getResultList();

for (Book book : bookList) {
    System.out.println(book);
}

โœ… ์‹คํ–‰ ๊ฒฐ๊ณผ


Hibernate: select b1_0.bookid,b1_0.bookname,b1_0.price,b1_0.publisher
from Book b1_0 where b1_0.price>?
Book [bookid=3, bookname=์ถ•๊ตฌ์˜ ์ดํ•ด, publisher=๋Œ€ํ•œ๋ฏธ๋””์–ด, price=22000]
Book [bookid=4, bookname=๊ณจํ”„ ๋ฐ”์ด๋ธ”, publisher=๋Œ€ํ•œ๋ฏธ๋””์–ด, price=35000]
Book [bookid=7, bookname=์•ผ๊ตฌ์˜ ์ถ”์–ต, publisher=์ด์ƒ๋ฏธ๋””์–ด, price=20000]

- :price ํ˜•ํƒœ๋กœ ์„ ์–ธ ํ›„, setParameter("price", 15000)๋กœ ๊ฐ’ ์ „๋‹ฌ.
- ์œ„์น˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ณด๋‹ค ๊ฐ€๋…์„ฑ์ด ์ข‹๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๋‹ค.


โผ JPQL vs SQL ๋น„๊ต

๊ตฌ๋ถ„ JPQL SQL
๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค ํ…Œ์ด๋ธ”
ํ•„๋“œ ์ฐธ์กฐ ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ๋ช… ์ปฌ๋Ÿผ๋ช…
์˜ˆ์‹œ select b from Book b where b.price > 10000 select * from book where price > 10000
๊ฒฐ๊ณผ Book ๊ฐ์ฒด ๋ฆฌ์ŠคํŠธ ResultSet (row ๊ธฐ๋ฐ˜)

โฝ ํ•ต์‹ฌ ์š”์•ฝ

  • JPQL์€ SQL์ด ์•„๋‹ˆ๋ผ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ์ค‘์‹ฌ์˜ ์ฟผ๋ฆฌ ์–ธ์–ด์ด๋‹ค.
  • TypedQuery<T>๋ฅผ ์‚ฌ์šฉํ•ด ํƒ€์ž… ์•ˆ์ „ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  • setParameter()๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ์กฐ๊ฑด์„ ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ๊ฒฐ๊ณผ๋Š” ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด(Book)๋กœ ๋ฐ˜ํ™˜๋˜์–ด, ์ž๋ฐ” ์ปฌ๋ ‰์…˜(List)์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.

โพ ์‹ค์Šต ์ •๋ฆฌ

์ข…๋ฅ˜ ์˜ˆ์‹œ ํŠน์ง•
๊ธฐ๋ณธ ์กฐํšŒ select b from Book b ์—”ํ‹ฐํ‹ฐ ์ „์ฒด ์กฐํšŒ
์œ„์น˜ ํŒŒ๋ผ๋ฏธํ„ฐ ?1, ?2 ์ˆœ์„œ ๊ธฐ๋ฐ˜ ๋ฐ”์ธ๋”ฉ
์ด๋ฆ„ ํŒŒ๋ผ๋ฏธํ„ฐ :price ์ด๋ฆ„ ๊ธฐ๋ฐ˜ ๋ฐ”์ธ๋”ฉ (๊ฐ€๋…์„ฑ↑)
๊ฒฐ๊ณผ List<Book> ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜

โฟ ํ•œ ์ค„ ์š”์•ฝ

JPQL์€ “ํ…Œ์ด๋ธ” ์ค‘์‹ฌ SQL”์ด ์•„๋‹ˆ๋ผ “์—”ํ‹ฐํ‹ฐ ์ค‘์‹ฌ ์ฟผ๋ฆฌ ์–ธ์–ด”์ด๋‹ค.
SQL์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค๋ฉด, JPQL์€ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.