[JPA] JPA(Java Persistent API)
by rlaehddnd0422์ง๊ธ๊น์ง jdbc๋ถํฐ jdbctemplate๊น์ง ์ฌ์ฉ์๊ฐ ์ง์ SQL์ ์์ฑํด ๋งคํํด์ค๋ SQL Mapper ๋ฐฉ์์ผ๋ก SQL ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์ ํด์์ต๋๋ค.
SQL ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์ ๊ฐ๋ฐ์๊ฐ ๊ณ ์์ ๋ง์ดํด์ผํฉ๋๋ค.. CRUD์์ ์์ํด์ ์๋ฐ ๊ฐ์ฒด๋ฅผ SQL๋ก ๋ฐ๊พธ๊ณ , SQL์ ์๋ฐ ๊ฐ์ฒด๋ก ๋ฐ๊พธ๊ณ .. ์ด๋ ๊ฒ ๊ฐ์ฒด๋ฅผ SQL ๋ก ๋งคํํ๋ ์์ ๋ค์ ๊ฐ๋ฐ์๊ฐ ํด์ผํ์ฃ . ๊ต์ฅํ ๋ฒ๊ฑฐ๋กญ์ต๋๋ค.
๊ฐ๋จํ ์ผ์ด์ค์๋ ๊ฐ๋ฐ์๊ฐ ๋น๊ต์ ์ฝ๊ฒ ๋งคํํ ์ ์๋ค๊ณ ํด๋, ๊ฐ์ฒด ๋ ๋ฒจ์ด ์์์ ์ํด์ ์ปค์ง๊ฒ ๋๋ฉด SQL๋ก ๋งคํ์ ๋ฐฐ๋ก ๋ณต์กํด์ง๋๋ค.
์์๋ฅผ ํ๋๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
Item์ ์์ํ ๊ฐ์ฒด Album, Movie, Book์ด ์๋ค๊ณ ํด๋ด ์๋ค.
๊ทธ๋ฆฌ๊ณ Album, Movie, Book ์ Item์ PK์ธ id๋ฅผ ์ธ๋ํค๋ก ๊ฐ์ง๋ค๊ณ ํด๋ด ์๋ค.
์ด ๋ Album์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ณ ์ถ๋ค๊ณ ํ ๋ ๊ฐ๋ฐ์๋
1) ๊ฐ์ฒด๋ฅผ ๋ถํดํ๊ณ
2) insert into item ...
3) insert into album values ...
๋ณต์กํ ์์ ๋ค์ ๊ฑฐ์ณ์ผ ํฉ๋๋ค.
์ถ๊ฐ ๋ฟ๋ง ์๋๋ผ ์กฐํ, ์ญ์ , ์์ ์์๋ ๊ฐ๋ฐ์๊ฐ ์๋ด์ผ ํ๋ ๋ถ๋ถ๋ค์ด ์๋นํ ๋ง์์ง๋๋ค.
๊ฐ์ฒด์ ๊ฐ์ฒด๊ฐ, ํ ์ด๋ธ๊ณผ ํ ์ด๋ธ๊ฐ ๊ด๊ณ๋ ๊ฐ๋ฐ์๊ฐ ์ฝ๊ฒ ํ์ํ ์ ์์ด์ผ ํฉ๋๋ค. JPA๋ ์ด๊ฒ์ ๊ฐ๋ฅํ๊ฒ ํด์ค๋๋ค.
JPA๋?
JPA๋ ์๋ฐ ์ง์์ ORM(Object-relational mapping ๊ฐ์ฒด ๊ด๊ณ ๋งคํ) ๊ธฐ์ ํ์ค์ผ๋ก
JPA๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ๋ฐ์๋ ๊ฐ์ฒด๋ ๊ฐ์ฒด๋๋ก ์ค๊ณํ๊ณ , ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋๋ก ์ค๊ณ๋ง ํ๋ฉด ORM ํ๋ ์์ํฌ์ธ JPA๊ฐ ์ค๊ฐ์์ ์์์ ๋งคํ์ ํด์ฃผ๊ธฐ ๋๋ฌธ์, ๊ฐ๋ฐ์ ์ ์ฅ์์ ๊ฐ์ฒด๋ฅผ ํ ์ด๋ธ์ ๋งคํํด์ฃผ์ง ์์๋ ๋๋ฏ๋ก ์๋นํ ํธํด์ง๋๋ค.
JPA๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ JDBC ์ฌ์ด์์ ๋์ํฉ๋๋ค.
์ด์ ๋ถํฐ๋ JPA๋ฅผ ์ฌ์ฉํด SQL ์ค์ฌ ๊ฐ๋ฐ์์ ๋ฒ์ด๋ ๊ฐ์ฒด ์ค์ฌ ๊ฐ๋ฐ๋ก ์ ํํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
JPA๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฐ์ฑ ์ฆ๊ฐ, ์ ์ง๋ณด์ ์ฉ์ด, ํจ๋ฌ๋ค์์ ๋ถ์ผ์น ํด๊ฒฐ, ์ฑ๋ฅ ๋ฑ ์ฌ๋ฌ ๋ฐฉ๋ฉด์์ ์ด์ ์ ์ป์ ์ ์์ต๋๋ค.
JPA๋ ์คํ๋ง๋งํผ ๋ฐฉ๋ํ๊ณ ๊ณต๋ถํ ๊ฒ ๋ง๊ธฐ ๋๋ฌธ์, ์์ธํ ๋ด์ฉ์ ์ถํ JPA๋ฅผ ๊ณต๋ถํ๋ฉด์ ์์๋ณด๋๋ก ํ๊ณ , ์ง๊ธ์ ๋๋ต์ ์ผ๋ก JPA๋ฅผ ์ฐ๋ฉด ์ด๋ ๊ฒ ํ ์ ์๊ตฌ๋ ์ ๋๋ก๋ง ์์๋ก์๋ค.
JPA ์ค์
- build.gradle
//JPA, ์คํ๋ง ๋ฐ์ดํฐ JPA ์ถ๊ฐ
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- hibernate-core : JPA ๊ตฌํ์ฒด์ธ ํ์ด๋ฒ๋ค์ดํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- jakarta.persistence-api : JPA ์ธํฐํ์ด์ค
- spring-data-jpa : ์คํ๋ง ๋ฐ์ดํฐ JPA ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ถ๊ฐ๋ฉ๋๋ค.
//JdbcTemplate ์ถ๊ฐ
//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
spring-boot-starter-data-jpa๋ jpa, spring data jpa ๋ฟ๋ง ์๋๋ผ jdbc๋ ํฌํจํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์์กด๊ด๊ณ๋ฅผ ์ ๊ฑฐํด๋ ๋ฉ๋๋ค.
- application.properties ( main, test )
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
- ํ์ด๋ฒ๋ค์ดํธ๊ฐ ์์ฑํ๊ณ ์คํํ๋ SQL์ ํ์ธํ๊ณ , SQL์ ๋ฐ์ธ๋ฉ๋๋ ํ๋ผ๋ฏธํฐ๋ฅผ ํ์ธํ๋๋ก ์ค์
JPA ์ ์ฉ
์ด์ ๋ชจ๋ ์ค๋น๊ฐ ๋๋ฌ์ต๋๋ค. ์ด ์ ์ jdbctemplate์์ ์ฌ์ฉํ ์์์ ์ ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
1. Item - ORM ๋งคํ
@Data
@Entity
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="item_name",length = 10)
private String itemName;
private Integer price;
private Integer quantity;
public Item() {
}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
- @Entity : JPA๊ฐ ์ฌ์ฉํ๋ ๊ฐ์ฒด๋ผ๋ ๋ป์ผ๋ก ์ด ์ด๋ ธํ ์ด์ ์ด ์์ด์ผ JPA๊ฐ ์ธ์ํ ์ ์์ต๋๋ค.
- @Id : ํ ์ด๋ธ์ PK์ ํด๋น ํ๋๋ฅผ ๋งคํํฉ๋๋ค.
- @GeneratedValue(strategy = GenerationType.IDENTITY) : PK ์์ฑ ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์์ฑํ๋ IDENTITY ๋ฐฉ์์ ์ฌ์ฉ
- @Column : ๊ฐ์ฒด์ ํ๋๋ฅผ ํ ์ด๋ธ์ ์ปฌ๋ผ๊ณผ ๋งคํํฉ๋๋ค. ์๋ตํด๋ ๋์ง๋ง, ํ ์ด๋ธ์ ์ปฌ๋ผ๋ช ๊ณผ ๋ค๋ฅผ ๊ฒฝ์ฐ ์ง์ ๋งคํํด์ค๋๋ค.
+ JPA๋ public์ด๋ protected ๊ธฐ๋ณธ์์ฑ์๊ฐ ํ์์ ์ผ๋ก ์์ด์ผ ํฉ๋๋ค.
2. Repository์ ์ ์ฉ
@Slf4j
@Repository
@Transactional
@RequiredArgsConstructor
public class JpaItemRepositoryV1 implements ItemRepository {
private final EntityManager em;
- JPA์ ๋ชจ๋ ๋์์ EntityManager๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋๋ค. EntityManager๋ ๋ด๋ถ์ ๋ฐ์ดํฐ์์ค๋ฅผ ๊ฐ์ง๊ณ ์์ด DB์ ์ ๊ทผํ ์ ์์ต๋๋ค.
- JPA์ ๋ชจ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ(์ฝ์ ,์์ ,์ญ์ // ์กฐํ๋ X)๋ ํธ๋์ญ์ ์์์ ์ด๋ฃจ์ด์ ธ์ผ ํ๊ธฐ ๋๋ฌธ์, @Transactional์ ๋ถ์ฌ์ฃผ์ด์ผ ํฉ๋๋ค. (JPA์์๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์ ์ด ํ์)
์ด ์ ํฌ์คํ ์์๋ ๋งํ์ง๋ง, ํธ๋์ญ์ ์ ์๋น์ค ๊ณ์ธต์ ๊ฑธ์ด์ฃผ๋ ๊ฒ์ด ๋ง์ง๋ง ์ด ์์ ์์๋ ์๋น์ค ๊ณ์ธต์ ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ด ์์ด ๋ฆฌํฌ์งํ ๋ฆฌ ๊ณ์ธต์ ๊ฑธ์ด์ฃผ์์ต๋๋ค.
2-1. save()
@Override
public Item save(Item item) {
em.persist(item);
return item;
}
์ํฐํฐ ๋งค๋์ ์ persist ๋ฉ์๋๋ก ๊ฐ์ฒด๋ฅผ ํ ์ด๋ธ์ ์ ์ฅํ ์ ์์ต๋๋ค. ( ๋งคํ์ JPA๊ฐ ๋ด๋ถ์ ์ผ๋ก ํด์ค๋๋ค. )
์ด ๋ Item ๊ฐ์ฒด์ PK id๋ ์์ฑ์ ๋ต์ IDENTITY๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์, ์ ์ฅ ์ดํ์ Item ๊ฐ์ฒด์ idํ๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์์ฑํ PK๊ฐ์ด ๋ค์ด๊ฐ๊ฒ ๋ฉ๋๋ค. ์ฆ, JPA๊ฐ INSERT SQL(persist ๋ฉ์๋) ์คํ ์ดํ์ ์์ฑ๋ ID๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ ํ ์ด๋ธ์ ๋ฃ์ด์ค๋๋ค.
2-2. update()
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
Item item = em.find(Item.class, itemId);
item.setItemName(updateParam.getItemName());
item.setPrice(updateParam.getPrice());
item.setQuantity(updateParam.getQuantity());
}
JPA๋ ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๋ ์์ ์, ๋ณ๊ฒฝ๋ ์ํฐํฐ ๊ฐ์ฒด๊ฐ ์๋์ง ํ์ธํ๊ณ , ํน์ ์ํฐํฐ ๊ฐ์ฒด๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์ UPDATE SQL์ ์คํํด์ค๋๋ค.
๋ณ๊ฒฝ๋ ์ํฐํฐ ๊ฐ์ฒด๋ ์์์ฑ ์ปจํ ์คํธ๋ผ๋ JPA ์๋ฆฌ๋ฅผ ํตํด ํ์์ด ์ด๋ฃจ์ด์ง๋๋ค.
2-3. findById()
@Override
public Optional<Item> findById(Long id) {
Item item = em.find(Item.class, id);
return Optional.ofNullable(item);
}
์ํฐํฐ ๋งค๋์ ์ find ๋ฉ์๋๋ก ํ ์ด๋ธ์์ ๊ฐ์ฒด๋ฅผ ์กฐํํ ์ ์์ต๋๋ค.
find์๋ ์กฐํ ํ์ ๊ณผ, PK ๊ฐ์ด ํ์ํฉ๋๋ค.
๋จ์ํ PK๊ฐ ์๋ ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ๋ณต์กํ ์กฐ๊ฑด์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ค๋ฉด ์ด๋ป๊ฒ ํ๋ฉด ๋ ๊น์?
2-4. findAll()
JPA๋ JPQL์ด๋ผ๋ ๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ ์ธ์ด๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ฃผ๋ก ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ๋ณต์กํ ์กฐ๊ฑด์ผ๋ก ์กฐํํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
SQL์ด ํ ์ด๋ธ์ ๋์์ผ๋ก ํ๋ค๋ฉด, JPQL์ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก SQL์ ์คํํฉ๋๋ค.
@Override
public List<Item> findAll(ItemSearchCond cond) {
String jpql = "select i from Item i";
Integer maxPrice = cond.getMaxPrice();
String itemName = cond.getItemName();
if (StringUtils.hasText(itemName) || maxPrice != null) {
jpql += " where";
}
boolean andFlag = false;
if (StringUtils.hasText(itemName)) {
jpql += " i.itemName like concat('%',:itemName,'%')";
andFlag = true;
}
if (maxPrice != null) {
if (andFlag) {
jpql += " and";
}
jpql += " i.price <= :maxPrice";
}
log.info("jpql={}", jpql);
TypedQuery<Item> query = em.createQuery(jpql, Item.class);
if (StringUtils.hasText(itemName)) {
query.setParameter("itemName", itemName);
}
if (maxPrice != null) {
query.setParameter("maxPrice", maxPrice);
}
return query.getResultList();
}
์คํ๋ JPQL ์์)
select i from Item i
where i.itemName like concat('%',:itemName,'%')
and i.price <= :maxPrice
JPQL์ ํตํด ์คํ๋ SQL
select
item0_.id as id1_0_,
item0_.item_name as item_nam2_0_,
item0_.price as price3_0_,
item0_.quantity as quantity4_0_
from item item0_
where (item0_.item_name like ('%'||?||'%'))
and item0_.price<=?
JPQL๋ JPA๋ฅผ ์์ธํ ๊ณต๋ถํ๋ฉด์ ์์๋ณด๋๋ก ํฉ์๋ค.
- (SQL) query์ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ setParameter() ์ฌ์ฉ
- JPA๋ฅผ ์ฌ์ฉํด๋ ๋์ ์ฟผ๋ฆฌ๋ ์ฒ๋ฆฌํ๊ธฐ ์ฌ์ ํ ์ด๋ ต์ต๋๋ค. ๋์ ์ฟผ๋ฆฌ๋ ๋์ค์ Querydsl์ด๋ผ๋ ๊ธฐ์ ์ ํ์ฉํ๋ฉด ๊น๋ํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+ Config ์ค์
@Configuration
public class JpaConfig {
// private final ItemMapper itemMapper; // Mybatis
private final EntityManager em;
public JpaConfig(EntityManager em) {
this.em = em;
}
JPA๋ฅผ ์ค์ ํ๋ ค๋ฉด EntityManagerFactory, JPA ํธ๋์ญ์ ๋งค๋์ , ๋ฐ์ดํฐ ์์ค ๋ฑ๋ฑ ๋ค์ํ ์ค์ ์ด ํ์ํ์ง๋ง ์คํ๋ง ๋ถํธ๊ฐ ์ด ๊ณผ์ ์ ๋ชจ๋ ์๋ํ ํด์ค๋๋ค.
+ ์์ธ ๋ณํ
JPA์ ๊ฒฝ์ฐ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด JPA ์์ธ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค. EntityManager๋ ์์ํ JPA ๊ธฐ์ ์ด๊ธฐ ๋๋ฌธ์ ์คํ๋ง๊ณผ ๊ด๋ จ์ด ์์ต๋๋ค.
๋ฐ๋ผ์ JPA์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด JPA ์์ธ๊ฐ ๋ฐ์ํ๋๋ฐ JPA ์์ธ๋ฅผ ์คํ๋ง ์์ธ ์ถ์ํ๋ก ๋ณํํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น์?
๋ฐ๋ก @Repository ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
@Repository๋ ์ปดํฌ๋ํธ ์ค์บ๋์์ผ๋ก ์ง์ ํด์ค ๋ฟ๋ง ์๋๋ผ, ์์ธ ๋ณํ AOP์ ์ ์ฉ ๋์์ด ๋๊ธฐ ๋๋ฌธ์ ์คํ๋ง๊ณผ JPA๋ฅผ ๊ฐ์ด ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์คํ๋ง์ JPA ์์ธ ๋ณํ๊ธฐ๋ฅผ ๋ฑ๋กํฉ๋๋ค.
์์ธ ๋ณํ AOP ํ๋ก์๋ JPA ๊ด๋ จ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด JPA ์์ธ ๋ณํ๊ธฐ๋ฅผ ํตํด ๋ฐ์ํ ์์ธ๋ฅผ ์คํ๋ง ๋ฐ์ดํฐ ์ ๊ทผ ์์ธ๋ก ๋ณํํ๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๋ฆฌํฌ์งํ ๋ฆฌ์ @Repository ์ ๋ ธํ ์ด์ ๋ง ์์ผ๋ฉด ์คํ๋ง์ด ์์ธ ๋ณํ์ ์ฒ๋ฆฌํ๋ AOP๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
<์ ๋ฆฌ>
- ORM ๊ธฐ์ ์ธ JPA๋ฅผ ๋๋ต์ ์ผ๋ก ์์๋ณด์์ต๋๋ค.
- JPA๋ฅผ ์ด์ฉํด SQL์ ์ง์ ์์ฑํ์ง ์๊ณ ๋ฉ์๋๋ฅผ ํตํด ์ฒ๋ฆฌํ ์ ์์์ต๋๋ค.
- JPA๋ ๋ด์ฉ์ด ๋ฐฉ๋ํ๊ธฐ ๋๋ฌธ์ ์ด ํ ๊ณต๋ถํ๊ณ ์ ๋ฆฌํ ์ ์๋๋ก ํ๊ฒ ์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
'๐ Backend > DB Access' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Querydsl] Querydsl ์ด๋? (0) | 2023.04.19 |
---|---|
[Spring Data JPA] ์คํ๋ง ๋ฐ์ดํฐ JPA (0) | 2023.04.17 |
TestCode์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ ์ฐ๋ (1) | 2023.04.15 |
[JdbcTemplate] JdbcTemplate ์ ์ฉ (0) | 2023.04.12 |
[JdbcTemplate] RowMapper์ ๋ํด (0) | 2023.04.12 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422