JPA(Java Persistence API)๋ ์๋ฐ ์ง์์ ORM(Object-Relational Mapping) ๊ธฐ์ ํ์ค์ผ๋ก ์ฑํ๋ ์ธํฐํ์ด์ค(Interface)์ ๋ชจ์์ด๋ค.
์ฌ๊ธฐ์ ์ฐ๋ฆฌ๋ 'ORM'์ด๋ผ๋ ๋จ์ด๋ฅผ ๊ฐ๋จํ๊ฒ ์ดํด๋ณด๋ฉด ๊ฐ์ฒด ์งํฅ ์ธ์ด์์ ์๋ฏธํ๋ ๊ฐ์ฒด(ํด๋์ค)์ RDB์ ํ ์ด๋ธ์ ์๋์ผ๋ก ๋งคํ(Mapping)ํ๋ ๋ฐฉ๋ฒ์ ์๋ฏธํ๋ค.
๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์์๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด ํํ๋ก ๋ค๋ฃจ๊ณ , RDB๋ ๋ฐ์ดํฐ๋ฅผ ํ ์ด๋ธ ํํ๋ก ์ ์ฅํ๋ค. ์ด ๋ ์์คํ ๊ฐ์๋ ๋ค์๊ณผ ๊ฐ์ ๋ถ์ผ์น๊ฐ ์กด์ฌํ๋ค.
=> ์ฆ. ์ด๋ฌํ ๋ ์ฌ์ด๊ฐ์ ๋ถ์ผ์น์ฑ์ ํด๊ฒฐํ๋ ์ญํ ์ด ๋ฐ๋ก ORM์ด๋ค.
JPA๋ ์ด๋ฌํ ORM์ ๊ตฌํํ๊ธฐ ์ํ ํ์ค ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ฉฐ, JPA๋ฅผ ๊ตฌํํ ๋ํ์ ์ธ ๊ตฌํ์ฒด๋ก๋ ๋ค์๊ณผ ๊ฐ์ด ์ธ๊ฐ์ง๋ค์ด ์๋ค.
Hibernate(ํ์ด๋ฒ๋ค์ดํธ)๋ ์๋ฐ์ ORM ํ๋ ์์ํฌ๋ก์, JPA๊ฐ ์ ์ํ๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์๋ JPA ๊ตฌํ์ฒด ์ค ํ๋์ด๋ค.
Hibernate๋ ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ๋งคํ์ ๊ด๋ฆฌํ๋ฉฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ํธ์์ฉ์ ๊ฐ์ํํ๋ค. ๋ํ, Query๋ฌธ๋ค์ ์ฝ์์ฐฝ์์ ํ์ธํ๊ณ ์ถ์ผ๋ฉด, ๋ค์๊ณผ ๊ฐ์ ์ค์ ์ application.properties ํ์ผ์ ์ถ๊ฐํ๋ฉด ๋๋ค.
// ์ฟผ๋ฆฌ ๋ก๊ทธ Show๋ฅผ true๋ก ์ค์
// ์คํ๋๋ SQL ์ฟผ๋ฆฌ๋ฅผ ์ฝ์์ ์ถ๋ ฅํ๋ค.
spring.jpa.show-sql=true
// SQL๋ฌธ์ ์ ๋ ฌํ์ฌ ์ถ๋ ฅ
// ์ฆ, ์ถ๋ ฅ๋๋ SQL ์ฟผ๋ฆฌ๋ฅผ ๋ณด๊ธฐ ์ฝ๊ฒ ํฌ๋งทํ
ํ๋ค.
spring.jpa.properties.hibernate.format_sql=true
// ๋ฐ์ธ๋ฉ๋๋ ํ๋ผ๋ฏธํฐ ๊ฐ์ ์ถ๋ ฅ
logging.level.org.hibernate.type.descriptor.sql=trace
JPA๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ง์ํ๋ ์คํ๋ง ํ์ ํ๋ก์ ํธ ์ค ํ๋์ด๋ค. CRUD ์ฒ๋ฆฌ์ ํ์ํ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๊ณ Hibernate์ ์ํฐํฐ ๋งค๋์ ๋ฅผ ์ง์ ๋ค๋ฃจ์ง ์๊ณ ๋ Repository๋ฅผ ์ ์ํด ์ฌ์ฉํจ์ผ์จ ์คํ๋ง์ด ์ ํฉํ ์ฟผ๋ฆฌ๋ฅผ ๋์ ์ผ๋ก ์์ฑํ๋ ๋ฐฉ์์ผ๋ก DB๋ฅผ ์กฐ์ํ๋ค.
์ด๋ฒ ๊ธ์์ ๋ค๋ฃจ๋ Spring Data JPA ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ๋ค.
JPA์์ ์ฌ์ฉํ ์ ์๋ ์ฟผ๋ฆฌ๋ฅผ ์๋ฏธํ๋ค.
JPQL์ ๋ฌธ๋ฒ์ด SQL ๋ฌธ๋ฒ๊ณผ ๋งค์ฐ ๋น์ทํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ์ ์ต์ํ ๋ถ๋ค์ด ์ด๋ ต์ง ์๊ฒ ์ฌ์ฉํ ์ ์๋ค. JPQL์ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ์ํํ๋ ์ฟผ๋ฆฌ์ด๋ฏ๋ก ๋งคํ๋ ์ํฐํฐ์ ์ด๋ฆ๊ณผ ํ๋์ ์ด๋ฆ์ ์ฌ์ฉํ๋ค.
// JPQL Basic Example
SELECT P FROM PRODUCT P WHERE P.NUMBER = ?1;
// PRODUCT๋ ์ํฐํฐ ํ์
, NUMBER๋ ์ํฐํฐ ๊ฐ์ฒด์ ์์ฑ์ ์๋ฏธํ๋ค.
// 1์ ์ฒซ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ ์๋ฏธํ๋ค. (์ฌ๊ธฐ์๋ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ number์ ๊ฐ์ด ๋ค์ด๊ฐ ๊ฒ์ด๋ค.)
๋ฆฌํฌ์งํฐ๋ฆฌ์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋๋ ๋ฉ์๋์ธ ๋ณ๋์ ๋ฉ์๋๋ฅผ ์ ์ํด์ผ ํ๋ ๊ฒฝ์ฐ ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ๊ฒ์ด ๋ฐ๋ก Query Method์ด๋ค.
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ Repository ์ธํฐํ์ด์ค๋ ๊ธฐ๋ณธ์ ์ผ๋ก JpaRepository๋ฅผ ๊ตฌํ๋ฐ์์ ๋ค์ํ CRUD ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. Query Method๋ ํฌ๊ฒ ๋์์ ๊ฒฐ์ ํ๋ Subject(์ฃผ์ ) + Predicate(๋์)์ผ๋ก ๊ตฌ๋ถํ๋ค.
'findBy...'์ 'getBy...' ๋ฑ์ด Subject(์ฃผ์ )๋ฅผ ๋ํ๋ด๋ฉฐ, By๋ Predicate(๋์)์ ์์์ ๋ํ๋ด์ด ๊ตฌ๋ถ์์ ์ญํ ์ ํ๋ค.
// ๋ฆฌํฌ์งํฐ๋ฆฌ์ ์ฟผ๋ฆฌ ๋ฉ์๋ ์์ฑ ์
List<Person> findByLastNameAndEmail(String lastName, String Email);
// ๋ฉ์๋ ๋ช
์ ๋ค์ด๊ฐ์๋ By ์ดํ์ LastName๊ณผ Email์ด ๋ฉ์๋์ ๋งค๊ฐ๋ณ์๋ก ๋ค์ด๊ฐ ์๋ค.
์์ ์ด์ ๋ค์ด๊ฐ ์ํฐํฐ์ ์์ฑ ์์ ์ํฐํฐ์์ ๊ด๋ฆฌํ๋ ํ๋๋ง ์ฐธ์กฐํ ์ ์๋ค.
๋์ฌ์ By ์ฌ์ด์ ๋๋ฉ์ธ์ด ํํ๋ ์ ์์ง๋ง repository์์ ์ด๋ฏธ ์ค์ ํ ํ์ด๊ธฐ ๋๋ฌธ์ ์๋ต ๊ฐ๋ฅํ๋ค.
- findBy...
- readBy...
- getBy...
- queryBy...
- searchBy...
- streamBy...
- existsBy...
// Example
boolean existsByNumber(Long number);
- deleteBy...
- removeBy...
// Example
void deleteByNumber(Long number);
long removeByName(String name);
- findFirst์ซ์By...
- findTop์ซ์By...
// Example
List<Product> findFirst5ByName(String name);
List<Product> findTop10ByName(String name);
Spring Data JPA์ ๋ฉ์๋ ์ด๋ฆ ๊ธฐ๋ฐ ์ฟผ๋ฆฌ ์์ฑ ๋ฐฉ์์์ ์กฐ๊ฑด์์ ๋ปํ๋ ๋จ์ด๋ฅผ ํฌํจํด์ ์ฌ์ฉ๋๋ ํค์๋๋ฅผ ์๋ฏธํ๋ค.
- ..IS
// Example
Product findByNumberIS(Long number);
Product findByNumberEquals(Long number);
- ..(Is)Not
// Example
Product findByNumberIsNot(Long number);
Product findByNumberNot(Long number);
- ..(Is)Null
- ..(Is)NotNull
// Example
List<Product> findByUpdatedAtNull();
List<Product> findByUpdatedAtIsNull();
List<Product> findByUpdatedAtNotNull();
List<Product> findByUpdatedAtIsNotNull();
- ..(Is)True
- ..(Is)False
// Example
Product findByActiveTrue();
Product findByActiveIsTrue();
Product findByActiveFalse();
Product findByActiveIsFalse();
- And
- Or
// Example
Product findByNumberAndName(Long number, String name);
Product findByNumberOrName(Long number, String name);
- ..(Is)GreaterThan
- ..(Is)LessThan
- ..(Is)Between
// Example
List<Product> findByPriceGreaterThan(Long price);
List<Product> findByPriceLessThan(Long price);
List<Product> findByPriceBetween(Long startPrice, Long endPrice);List<Product> findByPriceLessThanEqual(Long startPrice, Long endPrice);
GreaterThan
๊ณผ LessThan
ํค์๋๋ ๋น๊ต ๋์์ ๋ํ ์ด๊ณผ/๋ฏธ๋ง์ ๊ฐ๋
์ผ๋ก ๋น๊ต ์ฐ์ฐ์ ์ํํ๋ค.Equal
ํค์๋๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค.- ..(Is)StartingWith
- ..(Is)EndingWith
- ..(Is)Containing
- ..(Is)Like
// Example
List<Customer> findByFirstNameStartingWith(String prefix);
List<Customer> findByLastNameEndingWith(String suffix);
List<Customer> findByFirstNameContaining(String infix);
List<Customer> findByLastNameLike(String pattern);
// Service๋จ์์ ์ฌ์ฉ๋๋ ์
List<Customer> customers = customerRepository.findByFirstNameStartingWith("Jo");
// John, Johnny, Joanna
List<Customer> customers = customerRepository.findByLastNameEndingWith("son");
// Johnson, Jackson, Harrison
List<Customer> customers = customerRepository.findByFirstNameContaining("ann");
// Joanna, Annabelle, Hannah
List<Customer> customers = customerRepository.findByLastNameLike("%son%");
// Johnson, Jackson, Harrison
// ์ฟผ๋ฆฌ ๋ฉ์๋์ ์ ๋ ฌ ์ฒ๋ฆฌ
// Asc : ์ค๋ฆ์ฐจ์, Desc : ๋ด๋ฆผ์ฐจ์
List<Product> findByNameOrderByNumberAsc(String name);
List<Product> findByNameOrderByNumberDesc(String name);
OrderBy
ํค์๋๋ฅผ ์ฝ์
ํ์ฌ ์ ๋ ฌํ๊ณ ์ ํ๋ ์ปฌ๋ผ(Number)๊ณผ ์ค๋ฆ์ฐจ์/๋ด๋ฆผ์ฐจ์(Asc ํน์ Desc)์ ์ค์ ํ๋ฉด ์ ๋ ฌ์ด ์ํ๋๋ค.List<Product> findByNameOrderByNumberAsc(String name);
์ Query Method๋ฅผ ํด์ํ๋ฉด '์ํ ์ ๋ณด๋ฅผ ์ด๋ฆ์ผ๋ก ๊ฒ์ํ ํ ์ํ ๋ฒํธ๋ก ์ค๋ฆ์ฐจ์ ์ ๋ ฌ์ ์ํํ๋ค.'๋ ์๋ฏธ์ด๋ค.List<Product> findByNameOrderByNumberDesc(String name);
์ Query Method๋ฅผ ํด์ํ๋ฉด '์ํ ์ ๋ณด๋ฅผ ์ด๋ฆ์ผ๋ก ๊ฒ์ํ ํ ์ํ ๋ฒํธ์ ๋ด๋ฆผ์ฐจ์ ์ ๋ ฌ์ ์ํํ๋ค.'๋ ์๋ฏธ์ด๋ค.// ์ฟผ๋ฆฌ ๋ฉ์๋์์ ์ฌ๋ฌ ์ ๋ ฌ ๊ธฐ์ค ์ฌ์ฉ
// And๋ฅผ ๋ถ์ด์ง ์์
List<Prodcut> findByNameOrderByPriceAscStockDesc(String name);
List<Product> findByNameOrderByPrice(String name);
// List<Prodcut> findByNameOrderByPriceAscStockDesc(String name)์ hibernate
Hibernate:
select
product0_.id as id1_0_,
product0_.name as name2_0_,
product0_.price as price3_0_,
product0_.stock as stock4_0_
from
product product0_
where
product0_.name=?
order by
product0_.price asc,
product0_.stock desc
List<Product> findByName(String name, Sort sort);
// Service๋จ์์ indByName์ ํธ์ถํ๋ ์์
productRepository.findByName("์ฐํ", Sort.by(Order.asc("price")));
productRepository.findByName("๋ณผํ", Sort.by(Order.asc("price"), Order.desc("stock")));
asc()
๋ฉ์๋์ desc()
๋ฉ์๋๋ฅผ ํ์ฉํ์ฌ ์ค๋ฆ์ฐจ์๊ณผ ๋ด๋ฆผ์ฐจ์์ ์ง์ ํ๋ค.,(์ฝค๋ง)
๋ฅผ ์ด์ฉํ์ฌ ๊ตฌ๋ถํ๋ค.class ProductService{
...
productRepository.findByName("์ฐํ", getSort());
...
private Sort getSort(){ // ํ์ํ ์ฟผ๋ฆฌ๋ฉ์๋๋ฅผ ํ๋์ ๋ฉ์๋๋ก ๋ถ๋ฆฌํ์ฌ ์ฝ๋๋ฅผ ์ฌํ์ฉ ์ธ ๊ฒฌ์ ๋์๋ค.
return Sort.by(
Order.Asc("price"),
Order.Desc("stock")
);
}
ํ์ด์ง(Paging)์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ ์ฝ๋๋ฅผ ๊ฐ์๋ก ๋๋ ํ์ด์ง๋ฅผ ๊ตฌ๋ถํ๋ ๊ฒ์ ์๋ฏธํ๋ค. ์ด๋ฅผ ํตํด ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ , ์ฌ์ฉ์์๊ฒ ํ์ํ ๋ถ๋ถ๋ง์ ๋ณด์ฌ์ค ์ ์๋ค.
// ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ํ ์ฟผ๋ฆฌ ๋ฉ์๋
Page<Product> findByName(String name, Pageable pageable);
// Service๋จ์์ indByName์ ํธ์ถํ๋ ์์
Page<Product> productPage = produtRepository.findByName("์ฐํ, PageRequest.of(0, 2));
for(Product product : productPage.getContent()){
System.out.println(product);
}
// ์ถ๋ ฅ๊ฒฐ๊ณผ์์
// Product{id=1, name='์ฐํ', price=100.0, stock=50}
// Product{id=2, name='์ฐํ', price=120.0, stock=30}
Hibernate:
select
product0_.id as id1_0_,
product0_.name as name2_0_,
product0_.price as price3_0_,
product0_.stock as stock4_0_
from
product product0_
where
product0_.name=? limit ?
Hibernate:
select
count(product0_.id) as col_0_0_
from
product product0_
where
product0_.name=?
getContent()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.of ๋ฉ์๋ | ๋งค๊ฐ๋ณ์ ์ค๋ช | ๋น๊ณ |
---|---|---|
of(int page, int size) | ํ์ด์ง ๋ฒํธ(0๋ถํฐ ์์), ํ์ด์ง๋น ๋ฐ์ดํฐ ๊ฐฏ์ | ๋ฐ์ดํฐ ์ ๋ ฌX |
of(int page, int size, Sort) | ํ์ด์ง ๋ฒํธ(0๋ถํฐ ์์), ํ์ด์ง๋น ๋ฐ์ดํฐ ๊ฐฏ์, ์ ๋ ฌ | sort์ ์ํด ์ ๋ ฌ |
of(int page, int size, Direction, String --- ์์ฑ) | ํ์ด์ง ๋ฒํธ(0๋ถํฐ ์์), ํ์ด์ง๋น ๋ฐ์ดํฐ ๊ฐฏ์, ์ ๋ ฌ ๋ฐฉํฅ, ์์ฑ(์ปฌ๋ผ) | Sort.by(direction, properties)์ ์ํด ์ ๋ ฌ |
๐๐ปโโ๏ธ : "OK, ๊ทธ๋ผ ๊ทธ๋ฅ JPQL(ํน์ native SQL)์ ์์ฑํ์ง ์๊ณ Query Method๋ง ์์ฑํ๋ฉด ๋๋๊ฑฐ์ผ??"
๐ข : "์.. ๊ทธ๋ ๊ฒ๋ ์๊ฐ ํ ์ ์์ด ํ์ง๋ง ์๋!!"
// Example
package com.example.firstobject.repository;
import com.example.firstobject.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
// ํน์ ๊ฒ์๊ธ์ ๋ชจ๋ ๋๊ธ ์กฐํ
@Query("SELECT p FROM Product p WHERE p.name = :name")
List<Product> findByName(@Param("name") String name);
// ํน์ ๋๋ค์์ ๋ชจ๋ ๋๊ธ ์กฐํ
List<Comment> findByNickname(String nickname);
}
@Query ์ด๋ ธํ ์ด์ ์ JPQL ๋๋ ๋ค์ดํฐ๋ธ SQL์ ์ง์ ์์ฑํ์ฌ ํ๋๋ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๋ ์ฌ์ฉํ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
@Query
์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ ์ ์๋ค.public interface ProductRepository extends JpaRepository<Product, Long> {
// @Query ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ ์ฌ์ฉ์ ์ ์ JPQL ์ฟผ๋ฆฌ
@Query("SELECT p FROM Product p WHERE p.name = :name")
List<Product> findByNameCustom(@Param("name") String name);
// @Query ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ ๋ค์ดํฐ๋ธ SQL ์ฟผ๋ฆฌ
@Query(value = "SELECT * FROM product WHERE name = :name", nativeQuery = true)
List<Product> findByNameNative(@Param("name") String name);
}
native SQL | JPQL | |
---|---|---|
๋์ | ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ | ์ํฐํฐ ๊ฐ์ฒด |
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ๋ฆฝ์ฑ | ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ข ์์ | ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ๋ฆฝ์ |
๊ตฌ๋ฌธ | SQL ๊ตฌ๋ฌธ ์ง์ ์ฌ์ฉ | ๊ฐ์ฒด ์งํฅ์ ์ธ ๊ตฌ๋ฌธ |
์ ์ฐ์ฑ ๋ฐ ์ต์ ํ | ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ณ ์ ๊ธฐ๋ฅ ์ฌ์ฉ | ๊ฐ์ฒด ์งํฅ์ ์ธ ์ ๊ทผ ๋ฐฉ์ |
FROM
์ ๋ค์ ์ํฐํฐ ํ์
์ ์ง์ ํ๊ณ ๋ณ์นญ์ ์ค์ ํ๋ค.WHERE
์ ์ ํตํด SQL๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์กฐ๊ฑด์ ์ง์ ํ๋๋ฐ ?1
, ?2
์ ๊ฐ์ด ์๋ฒ์ ์ด์ฉํด์ ์ธ์๋ฅผ ๋ฐ์์ฌ ์๋ ์๋ค. // ์๋ฒ์ ์ด์ฉํด์ ์ธ์๋ฅผ ๋ฐ์ ์ฌ ์ ์๋ Example
@Query("SELECT p FROM Product p WHERE p.name = ?1")
List<Product> findByName(String name);
@Param
์ ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์ง์ ๋ฐ์ธ๋ฉํ๋ ๋ฐฉ์์ผ๋ก ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ฉด ์ค๋ฅ ๋ฐ์ ํ๋ฅ ์ ์ค์ด๊ณ ์ ์ง๋ณด์๋ฅผ ์์ํ๊ฒ ํ ์ ์๋ค.@Query
์ด๋
ธํ
์ด์
์ ์ํฐํฐ ํ์
์ด ์๋๋ผ ์ํ๋ ์ปฌ๋ผ์ ๊ฐ๋ง ์ถ์ถํ ์๋ ์๊ณ ์ด๋์ ๋ฆฌํด ํ์
์ List<Object[]>
ํํ๋ก ์ง์ ํ ์ ์๋ค.Reference๐
Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?! Dohyeon Kong ๊ทธ๋ JPA ์ ์ธ๊ฐ?!