JPA์ Persistence Context
๐ JPA๋?
JPA(Java Persistence API)
๋ ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์
์์ RDBMS๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ํ์ค ORM ๊ธฐ์ ์ด๋ค. JPA๋ ์ธํฐํ์ด์ค๋ก, ์ด๋ฅผ ๊ตฌํํ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
JPA์ ๊ตฌํ์ฒด๋ Hibernate, EclipseLink, OpenJPA, DataNucleus ๋ฑ์ด ์๋ค.
์ ์ฒด์ ์ธ ๋ฐ์ดํฐ ํ๋ฆ์ ์๋์ ๊ฐ๋ค.
- ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ ๊ด๋ จ ์์ ์ ํ ๋ JPA๋ฅผ ํธ์ถํ๋ค.
- JPA๋ ๊ฐ์ฒด ์งํฅ์ ์ฝ๋๋ฅผ SQL ์ฟผ๋ฆฌ๋ก ๋ณํํ ํ JDBC API๋ฅผ ํตํด ์ ๋ฌํ๋ค.
- JDBC API๋ ์ ์ ํ JDBC ๋๋ผ์ด๋ฒ๋ฅผ ํตํด ์ฟผ๋ฆฌ๋ฅผ DB์ ์ ์กํ๋ค.
- DB๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ JDBC ๋๋ผ์ด๋ฒ์ ์ ๋ฌํ๋ค.
- ๋๋ผ์ด๋ฒ๋ ๋ค์ ๊ฒฐ๊ณผ๋ฅผ JDBC API๋ก ์ ๋ฌํ๋ฉฐ, JPA๋ ๋ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์๋ฐ ๊ฐ์ฒด๋ก ๋ณํํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ฌํ๋ค.
๐ ORM
- JPA๋ ORM์ ์ผ์ข
์ด๋ค.
ORM(Object-Relational Mapping)
์ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ๊ณผ RDBMS ๊ฐ ๋ฐ์ดํฐ ๋ณํ ๋ฐ ๋งคํํ๋ ๋๊ตฌ์ด๋ค. ORM์ ํตํด SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ง ์๊ณ ๊ฐ์ฒด ์งํฅ์ ์ฝ๋๋ฅผ ํตํด DB ์์ ์ ์ํํ ์ ์๋ค.
์ฅ์
- ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ํ ์ ์์ผ๋ฏ๋ก ๊ฐ๋ฐ ์๋๋ฅผ ํฅ์์ํฌ ์ ์๋ค. ๋ ๋์๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ฆ๋์ค ๋ก์ง์ ์ง์คํ ์ ์๋ค.
- ๊ฐ์ฒด ์งํฅ์ ์ธ ์ฝ๋ ์์ฑ์ผ๋ก ์์ฐ์ฑ์ด ์ฆ๊ฐํ๋ฉฐ, ์ฝ๋ ์ฌํ์ฉ์ด ๊ฐ๋ฅํ๋ค.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ๊ต์ฒด๋์ด๋ ์ฝ๋๋ฅผ ์์ ํ ํ์๊ฐ ์๋ค. ์ฆ, DBMS์ ๋ํด ์ข ์์ ์ด์ง ์๋ค.
๋จ์
- ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ํํํ๋ ๋ฐ ์ ํฉํ์ง ์์ ์ ์๋ค.
- OOP์ RDBMS์ ์ดํด๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ ๋ค์ ๋์ ๋ฌ๋ ์ปค๋ธ๋ฅผ ๊ฐ์ง๋ค.
๐ JPA vs. MyBatis
JPA์ MyBatis ๋ชจ๋ ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ DB ๊ฐ ์ํธ์์ฉ์์ ์ฌ์ฉ๋๋ ํ๋ ์์ํฌ์ด๋, ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ JPA๋ ORM, MyBatis๋ SQL Mapper๋ผ๋ ๊ฒ์ด๋ค.
SQL Mapper
๋ DB์ SQL ์ฟผ๋ฆฌ์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋ก ๋งคํํ๋ ์ญํ ์ ํ๋ค. ๋น๊ต์ ์ฌ์ฉ์ด ํธ๋ฆฌํ๋, DBMS์ ๋ฐ๋ผ SQL ๋ฌธ๋ฒ์ด ๋ฌ๋ผ์ง ์ ์์ผ๋ฉฐ, SQL์ ์ง์ ์์ฑํด์ผ ํ๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
๐ ์์์ฑ ์ปจํ ์คํธ
Persistence Context
(์์์ฑ ์ปจํ
์คํธ)๋ ์ํฐํฐ๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ๋ ํ๊ฒฝ์ ๋งํ๋ค. ์๋ฐ ์ ํ๋ฆฌ์ผ์ด์
๊ณผ DB ์ฌ์ด์ Entity
๋ฅผ ์ ์ฅํ๋ ๊ฐ์์ DB๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ์์์ฑ ์ปจํ
์คํธ์ ์ ๊ทผ ๋ฐ ๊ด๋ฆฌ๋ฅผ ํ๋ ์ฃผ์ฒด๋ EntityManager
์ด๋ค.
Entity
DB์ ํ ์ด๋ธ์ ํด๋์ค๋ก ๊ตฌํํ ๊ฒ์ด๋ค.
EntityManager
Entity๋ฅผ ๊ด๋ฆฌํ๋ ์ฃผ์ฒด์ด๋ค.
EntityManager๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๊ฐ ์กด์ฌํ๋ค.
- Container-Managed
์คํ๋ง ์ปจํ
์ด๋์ EntityManagerFactory
์์ EntityManager๋ฅผ ์์ฑํ์ฌ ์ฃผ์
ํ๋ ๋ฐฉ์์ด๋ค. Thread-safeํ๋ค.
1
2
@PersistenceContext
EntityManager entityManager;
- Application-Managed
์ ํ๋ฆฌ์ผ์ด์ ์์ ์ง์ EntityManager๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
1
2
3
4
5
6
7
8
@Autowired
private EntityManagerFactory emf;
public void someMethod() {
EntityManager em = emf.createEntityManager();
// ...
em.close();
}
Entity Lifecycle
์ํฐํฐ๋ ์์์ฑ ์ปจํ ์คํธ์์ ๊ด๊ณ์ ๋ฐ๋ผ ํฌ๊ฒ ๋ค ๊ฐ์ง ์ํ๋ฅผ ๊ฐ์ง๋ค.
- ๋น์์(New/Transient)
์ํฐํฐ๊ฐ ์์์ฑ ์ปจํ ์คํธ์ ์ ํ ๊ด๊ณ๊ฐ ์๋ ์ํ์ด๋ค.
1
2
Member member = new Member();
member.setId("member1");
- ์์(Managed)
์ํฐํฐ๊ฐ ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ๋์ด ๊ด๋ฆฌ๋๋ ์ํ์ด๋ค.
1
em.persist(member);
- ์ค์์(Detached)
์ํฐํฐ๊ฐ ์์ ์ํ์๋ค๊ฐ ์์์ฑ ์ปจํ ์คํธ์์ ๋ถ๋ฆฌ๋ ์ํ์ด๋ค.
1
2
3
em.detach(member);
em.clear();
em.close();
detach()
๋ฅผ ํตํด ์ํฐํฐ๋ฅผ ์์์ฑ ์ปจํ
์คํธ์์ ๋ถ๋ฆฌํ๋ค.
clear()
๋ฅผ ํตํด ์์์ฑ ์ปจํ
์คํธ๋ฅผ ์ด๊ธฐํํ๋ค.
close()
๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์
์์ ๊ด๋ฆฌํ๋ EntityManager๋ฅผ ๋ซ๋๋ค.
merge()
๋ฅผ ํตํด ๋ถ๋ฆฌ๋ ์ํฐํฐ๋ฅผ ๋ค์ ์์์ฑ ์ปจํ
์คํธ์ ๋ณํฉํ ์ ์๋ค.
- ์ญ์ (Removed)
์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ๋ ์ํฐํฐ๊ฐ ์ญ์ ๋ ์ํ์ด๋ค.
1
em.remove(member);
remove()
๋ฅผ ํตํด ์ํฐํฐ๋ฅผ ์์์ฑ ์ปจํ
์คํธ์ DB ๋ชจ๋ ์ ๊ฑฐํ๋ค.
์ฅ์
- ์์์ฑ ์ปจํ
์คํธ๋ ๋ด๋ถ์ 1์ฐจ ์บ์๋ฅผ ๊ฐ์ง๊ณ ์๋ค. 1์ฐจ ์บ์๋ฅผ ํตํด ๋์ผํ ์ํฐํฐ ์ ๊ทผ ์ DB๊ฐ ์๋ ์บ์์์ ์กฐํํ ์ ์๋ค. ๋จ, 1์ฐจ ์บ์๋ ํธ๋์ญ์
๋จ์์ด๋ฏ๋ก ํธ๋์ญ์
์ด ๋๋๋ฉด ์บ์์ ํด๋น ์ ๋ณด๋ ์ฌ๋ผ์ง๊ฒ ๋๋ค.
- ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด๊ฐ ๊ณต์ ํ๋ ์บ์๋ 2์ฐจ ์บ์๋ผ๊ณ ๋ถ๋ฅธ๋ค.
- ๊ฐ์ ํธ๋์ญ์ ์์์ ๊ฐ์ ์ํฐํฐ๋ฅผ ์กฐํํ๋ฉด ๋์ผํ ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๋ฆฌํดํ๋ค. ์ด๋ฅผ ํตํด ๋์ผ์ฑ์ ๋ณด์ฅํ๋ค.
1
2
3
Member a = em.find(Member.class, "member1"); // DB ์กฐํ
Member b = em.find(Member.class, "member1"); // 1์ฐจ ์บ์ ์กฐํ
System.out.println(a == b); // true (๋์ผ์ฑ ๋ณด์ฅ)
- SQL ์ฟผ๋ฆฌ๋ฅผ ๋ฐ๋ก ์คํํ๋ ๊ฒ์ด ์๋๋ผ, ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๋ ์์ ์ ์ด๋ค์ ๋ชจ์ ํ ๋ฒ์ ์คํํ๋ค. ์ด๋ฅผ ํตํด DB ์ ๊ทผ ํ์๋ฅผ ์ค์ฌ ์ฑ๋ฅ์ ์ต์ ํํ ์ ์๋ค.
- ์ํฐํฐ์ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ๊ฐ์งํ์ฌ UPDATE ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํ๋ค. ์ด๋ฅผ
Dirty Checking
์ด๋ผ๊ณ ํ๋ค. Lazy Loading
(์ง์ฐ ๋ก๋ฉ) ๊ธฐ๋ฅ์ ํตํด ํ์ํ ์์ ์ ๊ด๋ จ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋ค.
๐ JPA ๋ฉ์๋
JPA๋ ๋ฉ์๋ ์ด๋ฆ์์ ์๋์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
findBy
๋ ํน์ ์์ฑ์ ๊ธฐ์ค์ผ๋ก ์ํฐํฐ๋ฅผ ์กฐํํ๋ค.
existBy
๋ ํน์ ์์ฑ์ ๊ธฐ์ค์ผ๋ก ์ํฐํฐ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ค.
deleteBy
๋ ํน์ ์์ฑ์ ๊ธฐ์ค์ผ๋ก ์ํฐํฐ๋ฅผ ์ญ์ ํ๋ค.
countBy
๋ ํน์ ์์ฑ์ ๊ธฐ์ค์ผ๋ก ์ํฐํฐ ์๋ฅผ ๊ณ์ฐํ๋ค.
1
2
List<Book> findByTitle(String title);
List<Book> findByTitleOrAuthor(String title, String author);
@Query
์ด๋
ธํ
์ด์
์ ํตํด JPQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ ์ ์๋ค.
1
2
@Query("select u from User u where u.firstname = ?1 and u.emailAddress = ?#{principal.emailAddress}")
List<User> findByFirstnameAndCurrentUserWithCustomQuery(String firstname);
๐ ํ์ด์ง
์ธ ๊ฐ์ง ๊ฐ๋ ์ด ์กด์ฌํ๋ค.
Pageable
: ํ์ด์ง ์ ๋ณด๋ฅผ ๋ด๋ ์ธํฐํ์ด์ค๋ก ํ์ด์ง ๋ฒํธ, ํฌ๊ธฐ, ์ ๋ ฌ ๋ฐฉ์ ๋ฑ์ ํฌํจํ๋ค.
PageRequest
: Pageable ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ก PageRequest.of()
๋ฅผ ํตํด ์์ฑํ๋ค.
Page
: ํ์ด์ง ๊ฒฐ๊ณผ๋ฅผ ๋ด๋ ์ธํฐํ์ด์ค๋ก ์กฐํ๋ ๋ฐ์ดํฐ์ ํ์ด์ง ๊ด๋ จ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๋ค.
๊ตฌํ ๋ฐฉ๋ฒ
์๋๋ Repository ์ฝ๋์ด๋ค.
1
2
3
public interface PersonRepository extends JpaRepository<Person, Long> {
Page<Person> findByName(String name, Pageable pageable);
}
๋ฉ์๋์ Pageable ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๊ฐํ๋ฉด JPA๊ฐ ์๋์ผ๋ก ํ์ด์ง ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ฌ ์คํํ๋ค.
์๋๋ Controller ์ฝ๋์ด๋ค.
1
2
3
4
@GetMapping
public Page<AccountDto.Res> getAccounts(Pageable pageable) {
return accountService.findAll(pageable).map(AccountDto.Res::new);
}
๋ฉ์๋ ํ๋ผ๋ฏธํฐ๋ก Pageable ํ์ ์ ์ ์ธํ๋ฉด URL ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋๋ ํ์ด์ง ์ ๋ณด๋ฅผ ์๋์ผ๋ก ๋ฐ์ธ๋ฉํ์ฌ Pageable ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
์๋๋ Service ์ฝ๋์ด๋ค.
1
2
3
public Page<Product> findProductsByName(String name, Pageable pageable) {
return productRepository.findByName(name, pageable);
}
์ปจํธ๋กค๋ฌ๋ถํฐ ์ ๋ฌ๋ฐ์ Pageable ๊ฐ์ฒด๋ฅผ ๋ ํ์งํ ๋ฆฌ๋ก ์ ๋ฌํ๋ค.
๐ ์ฐธ๊ณ
https://velog.io/@modsiw/JPAJava-Persistence-API์-๊ฐ๋
https://ittrue.tistory.com/254