Post

JPA์™€ Persistence Context

JPA์™€ Persistence Context

๐Ÿ“Œ JPA๋ž€?

JPA(Java Persistence API) ๋Š” ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ RDBMS๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํ‘œ์ค€ ORM ๊ธฐ์ˆ ์ด๋‹ค. JPA๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋กœ, ์ด๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

JPA์˜ ๊ตฌํ˜„์ฒด๋Š” Hibernate, EclipseLink, OpenJPA, DataNucleus ๋“ฑ์ด ์žˆ๋‹ค.

image.png

์ „์ฒด์ ์ธ ๋ฐ์ดํ„ฐ ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ฐ์ดํ„ฐ ๊ด€๋ จ ์ž‘์—…์„ ํ•  ๋•Œ JPA๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  2. JPA๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ์  ์ฝ”๋“œ๋ฅผ SQL ์ฟผ๋ฆฌ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ JDBC API๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•œ๋‹ค.
  3. JDBC API๋Š” ์ ์ ˆํ•œ JDBC ๋“œ๋ผ์ด๋ฒ„๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ๋ฅผ DB์— ์ „์†กํ•œ๋‹ค.
  4. DB๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ JDBC ๋“œ๋ผ์ด๋ฒ„์— ์ „๋‹ฌํ•œ๋‹ค.
  5. ๋“œ๋ผ์ด๋ฒ„๋Š” ๋‹ค์‹œ ๊ฒฐ๊ณผ๋ฅผ JDBC API๋กœ ์ „๋‹ฌํ•˜๋ฉฐ, JPA๋Š” ๋ฐ›์€ ๊ฒฐ๊ณผ๋ฅผ ์ž๋ฐ” ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „๋‹ฌํ•œ๋‹ค.

๐Ÿ“Œ ORM

image.png

  • 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๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•œ๋‹ค.

  1. Container-Managed

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์˜ EntityManagerFactory ์—์„œ EntityManager๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. Thread-safeํ•˜๋‹ค.

1
2
@PersistenceContext
EntityManager entityManager;
  1. 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

image.png

์—”ํ‹ฐํ‹ฐ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€์˜ ๊ด€๊ณ„์— ๋”ฐ๋ผ ํฌ๊ฒŒ ๋„ค ๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„๋‹ค.

  1. ๋น„์˜์†(New/Transient)

์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ์ด๋‹ค.

1
2
Member member = new Member();
member.setId("member1");
  1. ์˜์†(Managed)

์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋˜์–ด ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ์ด๋‹ค.

1
em.persist(member);
  1. ์ค€์˜์†(Detached)

์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์† ์ƒํƒœ์˜€๋‹ค๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ถ„๋ฆฌ๋œ ์ƒํƒœ์ด๋‹ค.

1
2
3
em.detach(member);
em.clear();
em.close();

detach() ๋ฅผ ํ†ตํ•ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ถ„๋ฆฌํ•œ๋‹ค.

clear() ๋ฅผ ํ†ตํ•ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.

close() ๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ด€๋ฆฌํ•˜๋Š” EntityManager๋ฅผ ๋‹ซ๋Š”๋‹ค.

merge() ๋ฅผ ํ†ตํ•ด ๋ถ„๋ฆฌ๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‹ค์‹œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. ์‚ญ์ œ(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

https://velog.io/@neptunes032/JPA-์˜์†์„ฑ-์ปจํ…์ŠคํŠธ๋ž€

This post is licensed under CC BY 4.0 by the author.